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