libPeConv
A library to load, manipulate, dump PE files.
Loading...
Searching...
No Matches
fix_dot_net_ep.cpp
Go to the documentation of this file.
1#include "fix_dot_net_ep.h"
2#include <peconv.h>
3#include "peconv/logger.h"
4
5#include <string>
6#include <map>
7
9{
10public:
11 ListImportNames(BYTE* _modulePtr, size_t _moduleSize, std::map<std::string, DWORD> &name_to_addr)
12 : ImportThunksCallback(_modulePtr, _moduleSize), nameToAddr(name_to_addr)
13 {
14 }
15
16 virtual bool processThunks(LPSTR lib_name, ULONG_PTR origFirstThunkPtr, ULONG_PTR firstThunkPtr)
17 {
18 if (this->is64b) {
19 IMAGE_THUNK_DATA64* desc = reinterpret_cast<IMAGE_THUNK_DATA64*>(origFirstThunkPtr);
20 ULONGLONG* call_via = reinterpret_cast<ULONGLONG*>(firstThunkPtr);
21 return processThunks_tpl<ULONGLONG, IMAGE_THUNK_DATA64>(lib_name, desc, call_via, IMAGE_ORDINAL_FLAG64);
22 }
23 IMAGE_THUNK_DATA32* desc = reinterpret_cast<IMAGE_THUNK_DATA32*>(origFirstThunkPtr);
24 DWORD* call_via = reinterpret_cast<DWORD*>(firstThunkPtr);
25 return processThunks_tpl<DWORD, IMAGE_THUNK_DATA32>(lib_name, desc, call_via, IMAGE_ORDINAL_FLAG32);
26 }
27
28protected:
29 template <typename T_FIELD, typename T_IMAGE_THUNK_DATA>
30 bool processThunks_tpl(LPSTR lib_name, T_IMAGE_THUNK_DATA* desc, T_FIELD* call_via, T_FIELD ordinal_flag)
31 {
32 DWORD call_via_rva = static_cast<DWORD>((ULONG_PTR)call_via - (ULONG_PTR)this->modulePtr);
33 LOG_DEBUG("via RVA: 0x%lx", call_via_rva);
34 bool is_by_ord = (desc->u1.Ordinal & ordinal_flag) != 0;
35 if (!is_by_ord) {
36 PIMAGE_IMPORT_BY_NAME by_name = (PIMAGE_IMPORT_BY_NAME)((ULONGLONG)modulePtr + desc->u1.AddressOfData);
37 LPSTR func_name = reinterpret_cast<LPSTR>(by_name->Name);
38 LOG_DEBUG("name: %s", func_name);
39 nameToAddr[func_name] = call_via_rva;
40 }
41 return true;
42 }
43
44 std::map<std::string, DWORD> &nameToAddr;
45};
46
47DWORD find_corexemain(BYTE *buf, size_t buf_size)
48{
49 std::map<std::string, DWORD> name_to_addr;
50 ListImportNames callback(buf, buf_size, name_to_addr);
51 if (!peconv::process_import_table(buf, buf_size, &callback)) return 0;
52
53 std::map<std::string, DWORD>::iterator found = name_to_addr.find("_CorExeMain");
54 if (found != name_to_addr.end()) return found->second;
55
56 found = name_to_addr.find("_CorDllMain");
57 if (found != name_to_addr.end()) return found->second;
58
59 return 0;
60}
61
62BYTE* search_jump(BYTE *buf, size_t buf_size, const DWORD cor_exe_main_thunk, const ULONGLONG img_base)
63{
64 // search the jump pattern, i.e.:
65 //JMP DWORD NEAR [0X402000] : FF 25 00204000
66 const size_t jmp_size = 2;
67 const BYTE jmp_pattern[jmp_size] = { 0xFF, 0x25 };
68
69 const size_t arg_size = sizeof(DWORD);
70 if ((jmp_size + arg_size) > buf_size) {
71 return nullptr;
72 }
73 const size_t end_offset = buf_size - (jmp_size + arg_size);
74
75 for (size_t i = end_offset; // search backwards
76 (i + 1) != 0; // this is unsigned comparison, so we cannot do: i >= 0
77 i--) // go back by one BYTE
78 {
79 if (buf[i] == jmp_pattern[0] && buf[i + 1] == jmp_pattern[1]) { // JMP
80 DWORD* addr = (DWORD*)(&buf[i + jmp_size]);
81 DWORD rva = static_cast<DWORD>((*addr) - img_base);
82 if (rva == cor_exe_main_thunk) {
83 LOG_DEBUG("Found call to _CorExeMain.");
84 return buf + i;
85 }
86 else {
87 LOG_WARNING("Mismatch: 0x%lx vs _CorExeMain: 0x%lx", rva, cor_exe_main_thunk);
88 }
89 }
90 }
91 return nullptr;
92}
93
94bool fix_dot_net_ep(BYTE *pe_buffer, size_t pe_buffer_size)
95{
96 if (!pe_buffer) return false;
97
98 if (peconv::is64bit(pe_buffer)) {
99 //64bit .NET files have EP=0
100 peconv::update_entry_point_rva(pe_buffer, 0);
101 return true;
102 }
103
104 DWORD ep_rva = peconv::get_entry_point_rva(pe_buffer);
105 LOG_INFO(".NET payload may require Entry Point correction. Current EP: 0x%lx", (unsigned long)ep_rva);
106 PIMAGE_SECTION_HEADER sec_hdr = peconv::get_section_hdr(pe_buffer, pe_buffer_size, 0);
107 if (!sec_hdr) {
108 return false;
109 }
110 BYTE* sec_ptr = (BYTE*)((ULONG_PTR)pe_buffer + sec_hdr->VirtualAddress);
111 if (!peconv::validate_ptr(pe_buffer, pe_buffer_size, sec_ptr, sec_hdr->SizeOfRawData)) {
112 return false;
113 }
114 ULONGLONG img_base = peconv::get_image_base(pe_buffer);
115 DWORD cor_exe_main_thunk = find_corexemain(pe_buffer, pe_buffer_size);
116 if (!cor_exe_main_thunk) {
117 return false;
118 }
119 BYTE* jump_ptr = search_jump(sec_ptr, sec_hdr->SizeOfRawData, cor_exe_main_thunk, img_base);
120 if (!jump_ptr) {
121 return false;
122 }
123 size_t offset = (ULONG_PTR)jump_ptr - (ULONG_PTR)pe_buffer;
124 peconv::update_entry_point_rva(pe_buffer, static_cast<DWORD>(offset));
125 LOG_INFO("Found possible Entry Point: 0x%lx", (unsigned long)offset);
126 return true;
127}
128
virtual bool processThunks(LPSTR lib_name, ULONG_PTR origFirstThunkPtr, ULONG_PTR firstThunkPtr)
bool processThunks_tpl(LPSTR lib_name, T_IMAGE_THUNK_DATA *desc, T_FIELD *call_via, T_FIELD ordinal_flag)
ListImportNames(BYTE *_modulePtr, size_t _moduleSize, std::map< std::string, DWORD > &name_to_addr)
std::map< std::string, DWORD > & nameToAddr
ImportThunksCallback(BYTE *_modulePtr, size_t _moduleSize)
bool fix_dot_net_ep(BYTE *pe_buffer, size_t pe_buffer_size)
BYTE * search_jump(BYTE *buf, size_t buf_size, const DWORD cor_exe_main_thunk, const ULONGLONG img_base)
DWORD find_corexemain(BYTE *buf, size_t buf_size)
#define LOG_DEBUG(fmt,...)
Definition logger.h:56
#define LOG_INFO(fmt,...)
Definition logger.h:50
#define LOG_WARNING(fmt,...)
Definition logger.h:44
bool update_entry_point_rva(IN OUT BYTE *pe_buffer, IN DWORD ep)
DWORD get_entry_point_rva(IN const BYTE *pe_buffer)
ULONGLONG get_image_base(IN const BYTE *pe_buffer)
bool process_import_table(IN BYTE *modulePtr, IN SIZE_T moduleSize, IN ImportThunksCallback *callback)
bool validate_ptr(IN const void *buffer_bgn, IN size_t buffer_size, IN const void *field_bgn, IN size_t field_size)
PIMAGE_SECTION_HEADER get_section_hdr(IN const BYTE *pe_buffer, IN const size_t buffer_size, IN size_t section_num)
bool is64bit(IN const BYTE *pe_buffer)
Master include for LibPEConv.