1 #pragma once
2 
3 #include "pe.h"
4 #include "pe_constants.h"
5 #include "pe_header.h"
6 #include "pe_analyzer.h"
7 #include "pe_debug.h"
8 #include "pe_utils.h"
9 #include "dotnet/dotnet.h"
10 #include "vb/vb_analyzer.h"
11 #include "borland/borland_version.h"
12 #include "../../support/coff/coff_symboltable.h"
13 
14 namespace REDasm {
15 
LOADER_PLUGIN_TEST(PELoader<b>,ImageDosHeader)16 template<size_t b> LOADER_PLUGIN_TEST(PELoader<b>, ImageDosHeader)
17 {
18     if((header->e_magic != IMAGE_DOS_SIGNATURE) || !request.view.inRange(header->e_lfanew))
19         return false;
20 
21     const ImageNtHeaders* ntheaders = PELoader::relpointer<const ImageNtHeaders>(header, header->e_lfanew);
22 
23     if(ntheaders->Signature != IMAGE_NT_SIGNATURE)
24         return false;
25 
26     if(b == 32)
27         return ntheaders->OptionalHeaderMagic == IMAGE_NT_OPTIONAL_HDR32_MAGIC;
28 
29     if(b == 64)
30         return ntheaders->OptionalHeaderMagic == IMAGE_NT_OPTIONAL_HDR64_MAGIC;
31 
32     return false;
33 }
34 
PELoader(AbstractBuffer * buffer)35 template<size_t b> PELoader<b>::PELoader(AbstractBuffer *buffer): LoaderPluginT<ImageDosHeader>(buffer), m_dosheader(nullptr), m_ntheaders(nullptr), m_sectiontable(nullptr), m_datadirectory(nullptr)
36 {
37     m_imagebase = m_sectionalignment = m_entrypoint = 0;
38     m_classifier.setBits(b);
39 
40     m_validimportsections.insert(".text");
41     m_validimportsections.insert(".idata");
42     m_validimportsections.insert(".rdata");
43 }
44 
dotNetReader() const45 template<size_t b> const DotNetReader *PELoader<b>::dotNetReader() const { return m_dotnetreader.get(); }
rvaToVa(address_t rva) const46 template<size_t b> address_t PELoader<b>::rvaToVa(address_t rva) const { return rva + m_imagebase; }
vaToRva(address_t va) const47 template<size_t b> address_t PELoader<b>::vaToRva(address_t va) const { return va - m_imagebase; }
48 
assembler() const49 template<size_t b> std::string PELoader<b>::assembler() const
50 {
51     if(m_classifier.checkDotNet())
52         return "cil";
53     if(m_ntheaders->FileHeader.Machine == IMAGE_FILE_MACHINE_I386)
54         return "x86_32";
55     if(m_ntheaders->FileHeader.Machine == IMAGE_FILE_MACHINE_AMD64)
56         return "x86_64";
57 
58     if(m_ntheaders->FileHeader.Machine == IMAGE_FILE_MACHINE_ARM)
59     {
60         if(m_ntheaders->OptionalHeaderMagic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
61             return "arm64";
62 
63         return "arm";
64     }
65 
66     return nullptr;
67 }
68 
createAnalyzer(DisassemblerAPI * disassembler) const69 template<size_t b> Analyzer *PELoader<b>::createAnalyzer(DisassemblerAPI *disassembler) const
70 {
71     if(m_classifier.checkVisualBasic())
72         return new VBAnalyzer(&m_classifier, disassembler);
73 
74     return new PEAnalyzer(&m_classifier, disassembler);
75 }
76 
load()77 template<size_t b> void PELoader<b>::load()
78 {
79     m_dosheader = m_header;
80     m_ntheaders = pointer<ImageNtHeaders>(m_dosheader->e_lfanew);
81     m_sectiontable = IMAGE_FIRST_SECTION(m_ntheaders);
82 
83     if(b == 64)
84         m_optionalheader = reinterpret_cast<ImageOptionalHeader*>(&m_ntheaders->OptionalHeader64);
85     else
86         m_optionalheader = reinterpret_cast<ImageOptionalHeader*>(&m_ntheaders->OptionalHeader32);
87 
88     m_imagebase = m_optionalheader->ImageBase;
89     m_sectionalignment = m_optionalheader->SectionAlignment;
90     m_entrypoint = m_imagebase + m_optionalheader->AddressOfEntryPoint;
91     m_datadirectory = reinterpret_cast<ImageDataDirectory*>(&m_optionalheader->DataDirectory);
92 
93     this->loadSections();
94     ImageCorHeader* corheader = this->checkDotNet();
95 
96     if(m_classifier.checkDotNet() == PEClassifications::DotNet_1)
97         REDasm::log(".NET 1.x is not supported");
98     else if(!corheader)
99         this->loadDefault();
100     else
101         this->loadDotNet(reinterpret_cast<ImageCor20Header*>(corheader));
102 
103     m_classifier.display();
104 }
105 
checkResources()106 template<size_t b> void PELoader<b>::checkResources()
107 {
108     const ImageDataDirectory& resourcedatadir = m_datadirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE];
109 
110     if(!resourcedatadir.VirtualAddress)
111         return;
112 
113     ImageResourceDirectory* resourcedir = this->rvaPointer<ImageResourceDirectory>(resourcedatadir.VirtualAddress);
114 
115     if(!resourcedir)
116         return;
117 
118     PEResources peresources(resourcedir);
119     m_classifier.classifyDelphi(m_dosheader, m_ntheaders, resourcedir);
120 }
121 
checkDebugInfo()122 template<size_t b> void PELoader<b>::checkDebugInfo()
123 {
124     const ImageDataDirectory& debuginfodir = m_datadirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
125 
126     if(!debuginfodir.VirtualAddress)
127         return;
128 
129     ImageDebugDirectory* debugdir = this->rvaPointer<ImageDebugDirectory>(debuginfodir.VirtualAddress);
130 
131     if(!debugdir)
132         return;
133 
134     u64 dbgoffset = 0;
135 
136     if(debugdir->AddressOfRawData)
137     {
138         offset_location offset = PEUtils::rvaToOffset(m_ntheaders, m_imagebase - debugdir->AddressOfRawData);
139 
140         if(offset.valid)
141             dbgoffset = offset;
142     }
143 
144     if(!dbgoffset && debugdir->PointerToRawData)
145         dbgoffset = debugdir->PointerToRawData;
146 
147     if(debugdir->Type == IMAGE_DEBUG_TYPE_UNKNOWN)
148         REDasm::log("Debug info type: UNKNOWN");
149     else if(debugdir->Type == IMAGE_DEBUG_TYPE_COFF)
150         REDasm::log("Debug info type: COFF");
151     else if(debugdir->Type == IMAGE_DEBUG_TYPE_CODEVIEW)
152     {
153         REDasm::log("Debug info type: CodeView");
154         m_classifier.classifyVisualStudio();
155 
156         if(!m_view.inRange(dbgoffset))
157             return;
158 
159         CVHeader* cvhdr = pointer<CVHeader>(dbgoffset);
160 
161         if(cvhdr->Signature == PE_PDB_NB10_SIGNATURE)
162         {
163             CvInfoPDB20* pdb20 = pointer<CvInfoPDB20>(dbgoffset);
164             REDasm::log("PDB 2.0 @ " + std::string(reinterpret_cast<const char*>(&pdb20->PdbFileName)));
165         }
166         else if(cvhdr->Signature == PE_PDB_RSDS_SIGNATURE)
167         {
168             CvInfoPDB70* pdb70 = pointer<CvInfoPDB70>(dbgoffset);
169             REDasm::log("PDB 7.0 @ " + std::string(reinterpret_cast<const char*>(&pdb70->PdbFileName)));
170         }
171         else
172             REDasm::log("Unknown Signature: '" + std::string(reinterpret_cast<const char*>(&cvhdr->Signature), sizeof(u32)));
173     }
174     else if(debugdir->Type == IMAGE_DEBUG_TYPE_FPO)
175         REDasm::log("Debug info type: FPO");
176     else if(debugdir->Type == IMAGE_DEBUG_TYPE_MISC)
177         REDasm::log("Debug info type: Misc");
178     else if(debugdir->Type == IMAGE_DEBUG_TYPE_EXCEPTION)
179         REDasm::log("Debug info type: Exception");
180     else if(debugdir->Type == IMAGE_DEBUG_TYPE_FIXUP)
181         REDasm::log("Debug info type: FixUp");
182     else if(debugdir->Type == IMAGE_DEBUG_TYPE_OMAP_TO_SRC)
183         REDasm::log("Debug info type: OMAP to Src");
184     else if(debugdir->Type == IMAGE_DEBUG_TYPE_OMAP_FROM_SRC)
185         REDasm::log("Debug info type: OMAP from Src");
186     else if(debugdir->Type == IMAGE_DEBUG_TYPE_BORLAND)
187         REDasm::log("Debug info type: Borland");
188     else if(debugdir->Type == IMAGE_DEBUG_TYPE_RESERVED10)
189         REDasm::log("Debug info type: Reserved10");
190     else if(debugdir->Type == IMAGE_DEBUG_TYPE_CLSID)
191         REDasm::log("Debug info type: CLSID");
192     else if(debugdir->Type == IMAGE_DEBUG_TYPE_VC_FEATURE)
193         REDasm::log("Debug info type: VC Feature");
194     else if(debugdir->Type == IMAGE_DEBUG_TYPE_POGO)
195         REDasm::log("Debug info type: POGO");
196     else if(debugdir->Type == IMAGE_DEBUG_TYPE_ILTCG)
197         REDasm::log("Debug info type: ILTCG");
198     else if(debugdir->Type == IMAGE_DEBUG_TYPE_REPRO)
199         REDasm::log("Debug info type: REPRO");
200     else
201         REDasm::log("Unknown Debug info type (value " + REDasm::hex(debugdir->Type, 32, true) + ")");
202 }
203 
checkDotNet()204 template<size_t b> ImageCorHeader* PELoader<b>::checkDotNet()
205 {
206     const ImageDataDirectory& dotnetdir = m_datadirectory[IMAGE_DIRECTORY_ENTRY_DOTNET];
207 
208     if(!dotnetdir.VirtualAddress)
209         return nullptr;
210 
211     ImageCorHeader* corheader = this->rvaPointer<ImageCorHeader>(dotnetdir.VirtualAddress);
212     m_classifier.classifyDotNet(corheader);
213     return corheader;
214 }
215 
loadDotNet(ImageCor20Header * corheader)216 template<size_t b> void PELoader<b>::loadDotNet(ImageCor20Header* corheader)
217 {
218     if(!corheader->MetaData.VirtualAddress)
219     {
220         REDasm::log("Invalid .NET MetaData");
221         return;
222     }
223 
224     ImageCor20MetaData* cormetadata = this->rvaPointer<ImageCor20MetaData>(corheader->MetaData.VirtualAddress);
225 
226     if(!cormetadata)
227         return;
228 
229     m_dotnetreader = std::make_unique<DotNetReader>(cormetadata);
230 
231     if(!m_dotnetreader->isValid())
232         return;
233 
234     m_dotnetreader->iterateTypes([this](u32 rva, const std::string& name) {
235         m_document->lockFunction(m_imagebase + rva, name);
236     });
237 }
238 
loadDefault()239 template<size_t b> void PELoader<b>::loadDefault()
240 {
241     this->loadExports();
242 
243     if(!this->loadImports())
244         REDasm::log("WARNING: This file seems to be PACKED");
245 
246     this->loadTLS();
247     this->loadConfig();
248     this->loadExceptions();
249     this->loadSymbolTable();
250     this->checkDebugInfo();
251     this->checkResources();
252 
253     m_document->entry(m_entrypoint);
254     m_classifier.classify(m_ntheaders);
255     m_signatures = m_classifier.signatures();
256 }
257 
loadSections()258 template<size_t b> void PELoader<b>::loadSections()
259 {
260     for(size_t i = 0; i < m_ntheaders->FileHeader.NumberOfSections; i++)
261     {
262         const ImageSectionHeader& section = m_sectiontable[i];
263         SegmentType flags = SegmentType::None;
264 
265         if((section.Characteristics & IMAGE_SCN_CNT_CODE) || (section.Characteristics & IMAGE_SCN_MEM_EXECUTE))
266             flags |= SegmentType::Code;
267 
268         if((section.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) || (section.Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA))
269             flags |= SegmentType::Data;
270 
271         u64 vsize = section.Misc.VirtualSize;
272 
273         if(!section.SizeOfRawData)
274             flags |= SegmentType::Bss;
275 
276         u64 diff = vsize % m_sectionalignment;
277 
278         if(diff)
279             vsize += m_sectionalignment - diff;
280 
281         std::string name = PEUtils::sectionName(reinterpret_cast<const char*>(section.Name));
282 
283         if(name.empty()) // Rename unnamed sections
284             name = "sect" + std::to_string(i);
285 
286         m_document->segment(name, section.PointerToRawData, m_imagebase + section.VirtualAddress, section.SizeOfRawData, vsize, flags);
287     }
288 
289     Segment* segment = m_document->segment(m_entrypoint);
290 
291     if(segment) // Entry points always points to code segment
292         segment->type |= SegmentType::Code;
293 }
294 
loadExports()295 template<size_t b> void PELoader<b>::loadExports()
296 {
297     const ImageDataDirectory& exportdir = m_datadirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
298 
299     if(!exportdir.VirtualAddress)
300         return;
301 
302     ImageExportDirectory* exporttable = this->rvaPointer<ImageExportDirectory>(exportdir.VirtualAddress);
303 
304     if(!exporttable)
305         return;
306 
307     u32* functions = this->rvaPointer<u32>(exporttable->AddressOfFunctions);
308     u32* names = this->rvaPointer<u32>(exporttable->AddressOfNames);
309     u16* nameords = this->rvaPointer<u16>(exporttable->AddressOfNameOrdinals);
310 
311     if(!functions || !names || !nameords)
312     {
313         REDasm::log("Corrupted export table");
314         return;
315     }
316 
317     for(size_t i = 0; i < exporttable->NumberOfFunctions; i++)
318     {
319         if(!functions[i])
320             continue;
321 
322         bool namedfunction = false;
323         u64 funcep = m_imagebase + functions[i];
324         const Segment* segment = m_document->segment(funcep);
325 
326         if(!segment)
327             continue;
328 
329         SymbolType symboltype = segment->is(SegmentType::Code) ? SymbolType::ExportFunction :
330                                                            SymbolType::ExportData;
331 
332         for(pe_integer_t j = 0; j < exporttable->NumberOfNames; j++)
333         {
334             if(nameords[j] != i)
335                 continue;
336 
337             namedfunction = true;
338             m_document->lock(funcep, this->rvaPointer<const char>(names[j]), symboltype);
339             break;
340         }
341 
342         if(namedfunction)
343             continue;
344 
345         std::stringstream ss;
346         ss << "Ordinal__" << std::uppercase << std::setw(4) << std::setfill('0') << std::setbase(16) << (exporttable->Base + i);
347         m_document->lock(funcep, ss.str(), symboltype);
348     }
349 }
350 
loadImports()351 template<size_t b> bool PELoader<b>::loadImports()
352 {
353     const ImageDataDirectory& importdir = m_datadirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
354 
355     if(!importdir.VirtualAddress)
356         return false;
357 
358     ImageImportDescriptor* importtable = this->rvaPointer<ImageImportDescriptor>(importdir.VirtualAddress);
359 
360     if(!importtable)
361         return false;
362 
363     for(size_t i = 0; i < importtable[i].FirstThunk; i++)
364         this->readDescriptor(importtable[i], b == 64 ? IMAGE_ORDINAL_FLAG64 : IMAGE_ORDINAL_FLAG32);
365 
366     Segment* segment = m_document->segment(m_imagebase + importdir.VirtualAddress);
367     return segment && (m_validimportsections.find(segment->name) != m_validimportsections.end());
368 }
369 
loadExceptions()370 template<size_t b> void PELoader<b>::loadExceptions()
371 {
372     const ImageDataDirectory& exceptiondir = m_datadirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
373 
374     if(!exceptiondir.VirtualAddress || !exceptiondir.Size)
375         return;
376 
377     ImageRuntimeFunctionEntry* runtimeentry = this->rvaPointer<ImageRuntimeFunctionEntry>(exceptiondir.VirtualAddress);
378 
379     if(!runtimeentry)
380         return;
381 
382     u64 c = 0, csize = 0;
383 
384     for(pe_integer_t i = 0; csize < exceptiondir.Size; i++, csize += sizeof(ImageRuntimeFunctionEntry))
385     {
386         address_t va = m_imagebase + runtimeentry[i].BeginAddress;
387 
388         if(!m_document->segment(va) || (runtimeentry[i].UnwindInfoAddress & 1))
389             continue;
390 
391         UnwindInfo* unwindinfo = this->rvaPointer<UnwindInfo>(runtimeentry[i].UnwindInfoAddress & ~1u);
392 
393         if(!unwindinfo || (unwindinfo->Flags & UNW_FLAG_CHAININFO))
394             continue;
395 
396         m_document->function(va);
397         c++;
398     }
399 
400     if(c)
401         REDasm::log("Found " + std::to_string(c) + " function(s) in Exception Directory");
402 }
403 
loadConfig()404 template<size_t b> void PELoader<b>::loadConfig()
405 {
406     const ImageDataDirectory& configdir = m_datadirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG];
407 
408     if(!configdir.VirtualAddress)
409         return;
410 
411     ImageLoadConfigDirectory* loadconfigdir = this->rvaPointer<ImageLoadConfigDirectory>(configdir.VirtualAddress);
412 
413     if(!loadconfigdir || !loadconfigdir->SecurityCookie)
414         return;
415 
416     m_document->lock(loadconfigdir->SecurityCookie, PE_SECURITY_COOKIE_SYMBOL, SymbolType::Data);
417 }
418 
loadTLS()419 template<size_t b> void PELoader<b>::loadTLS()
420 {
421     const ImageDataDirectory& tlsdir = m_datadirectory[IMAGE_DIRECTORY_ENTRY_TLS];
422 
423     if(!tlsdir.VirtualAddress)
424         return;
425 
426     ImageTlsDirectory* imagetlsdir = this->rvaPointer<ImageTlsDirectory>(tlsdir.VirtualAddress);
427 
428     if(imagetlsdir)
429         this->readTLSCallbacks(imagetlsdir);
430 }
431 
loadSymbolTable()432 template<size_t b> void PELoader<b>::loadSymbolTable()
433 {
434     if(!m_ntheaders->FileHeader.PointerToSymbolTable || !m_ntheaders->FileHeader.NumberOfSymbols)
435         return;
436 
437     REDasm::log("Loading symbol table @ " + REDasm::hex(m_ntheaders->FileHeader.PointerToSymbolTable));
438 
439     COFF::loadSymbols([&](const std::string& name, const COFF::COFF_Entry* entry) {
440                       const Segment& segment = m_document->segments()[entry->e_scnum - 1];
441                       m_document->lock(segment.address + entry->e_value, name, SymbolType::Function);
442     },
443     pointer<u8>(m_ntheaders->FileHeader.PointerToSymbolTable),
444     m_ntheaders->FileHeader.NumberOfSymbols);
445 }
446 
readTLSCallbacks(const ImageTlsDirectory * tlsdirectory)447 template<size_t b> void PELoader<b>::readTLSCallbacks(const ImageTlsDirectory *tlsdirectory)
448 {
449     if(!tlsdirectory->AddressOfCallBacks)
450         return;
451 
452     pe_integer_t* callbacks = addrpointer<pe_integer_t>(tlsdirectory->AddressOfCallBacks);
453 
454     for(pe_integer_t i = 0; *callbacks; i++, callbacks++)
455         m_document->lock(*callbacks, "TlsCallback_" + std::to_string(i), SymbolType::Function);
456 }
457 
readDescriptor(const ImageImportDescriptor & importdescriptor,pe_integer_t ordinalflag)458 template<size_t b> void PELoader<b>::readDescriptor(const ImageImportDescriptor& importdescriptor, pe_integer_t ordinalflag)
459 {
460     // Check if OFT exists
461     ImageThunkData* thunk = this->rvaPointer<ImageThunkData>(importdescriptor.OriginalFirstThunk ? importdescriptor.OriginalFirstThunk :
462                                                                                                    importdescriptor.FirstThunk);
463 
464     if(!thunk)
465         return;
466 
467     std::string descriptorname = this->rvaPointer<const char>(importdescriptor.Name);
468     std::transform(descriptorname.begin(), descriptorname.end(), descriptorname.begin(), ::tolower);
469     m_classifier.classifyImport(descriptorname);
470 
471     for(size_t i = 0; thunk[i]; i++)
472     {
473         std::string importname;
474         address_t address = m_imagebase + (importdescriptor.FirstThunk + (i * sizeof(ImageThunkData))); // Instructions refers to FT
475 
476         if(!(thunk[i] & ordinalflag))
477         {
478             ImageImportByName* importbyname = this->rvaPointer<ImageImportByName>(thunk[i]);
479 
480             if(!importbyname)
481                 continue;
482 
483             importname = PEUtils::importName(descriptorname, reinterpret_cast<const char*>(&importbyname->Name));
484         }
485         else
486         {
487             u16 ordinal = static_cast<u16>(ordinalflag ^ thunk[i]);
488 
489             if(!PEImports::importName<b>(descriptorname, ordinal, importname))
490                 importname = PEUtils::importName(descriptorname, ordinal);
491             else
492                 importname = PEUtils::importName(descriptorname, importname);
493         }
494 
495         m_document->lock(address, importname, SymbolType::Import);
496     }
497 }
498 
499 } // REDasm
500