Eilam E.Reversing.Secrets of reverse engineering.2005
.pdf
Breaking Protections 381
+0x02c ThreadLocalStoragePointer : Ptr32 Void
+0x030 ProcessEnvironmentBlock : Ptr32 _PEB
.
.
It’s obvious that the first line is accessing the Process Environment Block through the TEB. The PEB is the process-information data structure in Windows, just like the TEB is the thread information data structure. In address 00402EB5 the program is accessing offset +c in the PEB. Let’s look at what’s in there. Again, the full definition is quite long, so I’ll just print the beginning of the definition.
+0x000 |
InheritedAddressSpace : UChar |
||
+0x001 |
ReadImageFileExecOptions |
: UChar |
|
+0x002 |
BeingDebugged |
: UChar |
|
+0x003 |
SpareBool |
: UChar |
|
+0x004 |
Mutant |
: Ptr32 |
Void |
+0x008 |
ImageBaseAddress |
: Ptr32 |
Void |
+0x00c Ldr |
: Ptr32 |
_PEB_LDR_DATA |
|
. |
|
|
|
. |
|
|
|
In this case, offset +c goes to the _PEB_LDR_DATA, which is the loader information. Let’s take a look at this data structure and see what’s inside.
+0x000 |
Length |
: Uint4B |
+0x004 |
Initialized |
: UChar |
+0x008 |
SsHandle |
: Ptr32 Void |
+0x00c InLoadOrderModuleList : _LIST_ENTRY |
||
+0x014 |
InMemoryOrderModuleList : _LIST_ENTRY |
|
+0x01c InInitializationOrderModuleList : _LIST_ENTRY |
||
+0x024 |
EntryInProgress |
: Ptr32 Void |
This data structure appears to be used for managing the loaded executables within the current process. There are several module lists, each containing the currently loaded executable modules in a different order. The function is taking offset +c, which means that it’s going after the InLoadOrder ModuleList item. Let’s take a look at the module data structure, LDR_DATA_TABLE_ENTRY, and try to understand what this function is looking for.
The following definition for LDR_DATA_TABLE_ENTRY was produced using the DT command in WinDbg. Some Windows symbol files actually contain data structure definitions that can be dumped using that command. All you need to do is type DT ModuleName!* to get a list of all available names, and then type DT ModuleName!StructureName to get a nice listing of its members!
382 Chapter 11
+0x000 |
InLoadOrderLinks |
: |
_LIST_ENTRY |
+0x008 |
InMemoryOrderLinks |
: _LIST_ENTRY |
|
+0x010 |
InInitializationOrderLinks : _LIST_ENTRY |
||
+0x018 |
DllBase |
: Ptr32 Void |
|
+0x01c EntryPoint |
: Ptr32 Void |
||
+0x020 |
SizeOfImage |
: Uint4B |
|
+0x024 |
FullDllName |
: _UNICODE_STRING |
|
+0x02c BaseDllName |
: _UNICODE_STRING |
||
+0x034 |
Flags |
: Uint4B |
|
+0x038 |
LoadCount |
: Uint2B |
|
+0x03a TlsIndex |
: Uint2B |
||
+0x03c HashLinks |
: _LIST_ENTRY |
||
+0x03c SectionPointer |
: Ptr32 Void |
||
+0x040 |
CheckSum |
: Uint4B |
|
+0x044 |
TimeDateStamp |
: Uint4B |
|
+0x044 |
LoadedImports |
: Ptr32 Void |
|
+0x048 |
EntryPointActivationContext : Ptr32 _ACTIVATION_CONTEXT |
||
+0x04c PatchInformation : Ptr32 Void
After getting a pointer to InLoadOrderModuleList the function appears to go after offset +0 in the first module. From looking at this structure, it would seem that offset +0 is part of the LIST_ENTRY data structure. Let’s dump LIST_ENTRY and see what offset +0 means.
+0x000 |
Flink |
: |
Ptr32 |
_LIST_ENTRY |
+0x004 |
Blink |
: |
Ptr32 |
_LIST_ENTRY |
Offset +0 is Flink, which probably stands for “forward link”. This means that the function is hard-coded to skip the first entry, regardless of what it is. This is quite unusual because with a linked list you would expect to see a loop—no loop, the function is just hard-coded to skip the first entry. After doing that, the function simply returns the value from offset +18 at the second entry. Offset +18 in _LDR_DATA_TABLE_ENTRY is DllBase. So, it would seem that all this function is doing is looking for the base of some DLL. At this point it would be wise to load Defender.EXE in WinDbg, just to take a look at the loader information and see what the second module is. For this, you use the !dlls command, which dumps a (relatively) user-friendly view of the loader data structures. The –l option makes the command dump modules in their load order, which is essentially the list you traversed by taking
InLoadOrderModuleList from PEB_LDR_DATA.
0:000> !dlls -l
0x00241ee0: C:\Documents |
and Settings\Eldad Eilam\Defender.exe |
||||
Base |
0x00400000 |
EntryPoint |
0x00404232 |
Size |
0x00008000 |
Flags |
0x00005000 |
LoadCount |
0x0000ffff |
TlsIndex |
0x00000000 |
LDRP_LOAD_IN_PROGRESS
LDRP_ENTRY_PROCESSED
Breaking Protections 383
0x00241f48: C:\WINDOWS\system32\ntdll.dll |
|
|
|||
Base |
0x7c900000 |
EntryPoint |
0x7c913156 |
Size |
0x000b0000 |
Flags |
0x00085004 |
LoadCount |
0x0000ffff |
TlsIndex |
0x00000000 |
|
LDRP_IMAGE_DLL |
|
|
|
|
|
LDRP_LOAD_IN_PROGRESS |
|
|
|
|
|
LDRP_ENTRY_PROCESSED |
|
|
|
|
|
LDRP_PROCESS_ATTACH_CALLED |
|
|
||
0x00242010: C:\WINDOWS\system32\kernel32.dll |
|
|
|||
Base |
0x7c800000 |
EntryPoint |
0x7c80b436 |
Size |
0x000f4000 |
Flags |
0x00085004 |
LoadCount |
0x0000ffff |
TlsIndex |
0x00000000 |
|
LDRP_IMAGE_DLL |
|
|
|
|
|
LDRP_LOAD_IN_PROGRESS |
|
|
|
|
|
LDRP_ENTRY_PROCESSED |
|
|
|
|
|
LDRP_PROCESS_ATTACH_CALLED |
|
|
||
So, it would seem that the second module is NTDLL.DLL. The function at 00402EA8 simply obtains the address of NTDLL.DLL in memory. This makes a lot of sense because as I’ve said before, it would be utterly impossible for the program to communicate with the user without any kind of interface to the operating system. Obtaining the address of NTDLL.DLL is apparently the first step in creating such an interface.
If you go back to Listing 11.6, you see that the return value from 00402EA8 is passed right into 004033D1, which is the next function being called. Let’s take a look at it.
loc_4033D1: |
|
|
.h3mf85n:004033D1 |
push |
ebp |
.h3mf85n:004033D2 |
mov |
ebp, esp |
.h3mf85n:004033D4 |
sub |
esp, 22Ch |
.h3mf85n:004033DA |
push |
ebx |
.h3mf85n:004033DB |
push |
esi |
.h3mf85n:004033DC |
push |
edi |
.h3mf85n:004033DD |
push |
offset dword_4034DD |
.h3mf85n:004033E2 |
pop |
eax |
.h3mf85n:004033E3 |
mov |
[ebp-20h], eax |
.h3mf85n:004033E6 |
push |
offset loc_4041FD |
.h3mf85n:004033EB |
pop |
eax |
.h3mf85n:004033EC |
mov |
[ebp-18h], eax |
.h3mf85n:004033EF |
mov |
eax, offset dword_4034E5 |
.h3mf85n:004033F4 |
mov |
ds:dword_4034D6, eax |
.h3mf85n:004033FA |
mov |
dword ptr [ebp-8], 1 |
.h3mf85n:00403401 |
cmp |
dword ptr [ebp-8], 0 |
.h3mf85n:00403405 |
jz |
short loc_40346D |
.h3mf85n:00403407 |
mov |
eax, [ebp-18h] |
.h3mf85n:0040340A |
sub |
eax, [ebp-20h] |
.h3mf85n:0040340D |
mov |
[ebp-30h], eax |
|
|
|
Listing 11.7 A disassembly of function 4033D1 from Defender, generated by IDA Pro.
(continued)
384 Chapter 11
.h3mf85n:00403410 |
mov |
eax, [ebp-20h] |
.h3mf85n:00403413 |
mov |
[ebp-34h], eax |
.h3mf85n:00403416 |
and |
dword ptr [ebp-24h], 0 |
.h3mf85n:0040341A |
and |
dword ptr [ebp-28h], 0 |
.h3mf85n:0040341E loc_40341E: |
; CODE XREF: .h3mf85n:00403469_j |
|
.h3mf85n:0040341E |
cmp |
dword ptr [ebp-30h], 3 |
.h3mf85n:00403422 |
jbe |
short loc_40346B |
.h3mf85n:00403424 |
mov |
eax, [ebp-34h] |
.h3mf85n:00403427 |
mov |
eax, [eax] |
.h3mf85n:00403429 |
mov |
[ebp-2Ch], eax |
.h3mf85n:0040342C |
mov |
eax, [ebp-34h] |
.h3mf85n:0040342F |
mov |
eax, [eax] |
.h3mf85n:00403431 |
xor |
eax, 2BCA6179h |
.h3mf85n:00403436 |
mov |
ecx, [ebp-34h] |
.h3mf85n:00403439 |
mov |
[ecx], eax |
.h3mf85n:0040343B |
mov |
eax, [ebp-34h] |
.h3mf85n:0040343E |
mov |
eax, [eax] |
.h3mf85n:00403440 |
xor |
eax, [ebp-28h] |
.h3mf85n:00403443 |
mov |
ecx, [ebp-34h] |
.h3mf85n:00403446 |
mov |
[ecx], eax |
.h3mf85n:00403448 |
mov |
eax, [ebp-2Ch] |
.h3mf85n:0040344B |
mov |
[ebp-28h], eax |
.h3mf85n:0040344E |
mov |
eax, [ebp-24h] |
.h3mf85n:00403451 |
xor |
eax, [ebp-2Ch] |
.h3mf85n:00403454 |
mov |
[ebp-24h], eax |
.h3mf85n:00403457 |
mov |
eax, [ebp-34h] |
.h3mf85n:0040345A |
add |
eax, 4 |
.h3mf85n:0040345D |
mov |
[ebp-34h], eax |
.h3mf85n:00403460 |
mov |
eax, [ebp-30h] |
.h3mf85n:00403463 |
sub |
eax, 4 |
.h3mf85n:00403466 |
mov |
[ebp-30h], eax |
.h3mf85n:00403469 |
jmp |
short loc_40341E |
.h3mf85n:0040346B ; ---------------------------------------------------- |
|
|
.h3mf85n:0040346B |
|
|
.h3mf85n:0040346B loc_40346B: |
; CODE XREF: .h3mf85n:00403422_j |
|
.h3mf85n:0040346B |
jmp |
short near ptr unk_4034D5 |
.h3mf85n:0040346D ; ---------------------------------------------------- |
|
|
.h3mf85n:0040346D |
|
|
.h3mf85n:0040346D loc_40346D: |
; CODE XREF: .h3mf85n:00403405_j |
|
.h3mf85n:0040346D |
mov |
eax, [ebp-18h] |
.h3mf85n:00403470 |
sub |
eax, [ebp-20h] |
.h3mf85n:00403473 |
mov |
[ebp-40h], eax |
.h3mf85n:00403476 |
mov |
eax, [ebp-20h] |
.h3mf85n:00403479 |
mov |
[ebp-44h], eax |
.h3mf85n:0040347C |
and |
dword ptr [ebp-38h], 0 |
.h3mf85n:00403480 |
and |
dword ptr [ebp-3Ch], 0 |
.h3mf85n:00403484 |
|
|
.h3mf85n:00403484 loc_403484: |
; CODE XREF: .h3mf85n:004034CB_j |
|
.h3mf85n:00403484 |
cmp |
dword ptr [ebp-40h], 3 |
|
|
|
Listing 11.7 (continued)
|
|
Breaking Protections 385 |
|
|
|
|
|
.h3mf85n:00403488 |
jbe |
short loc_4034CD |
|
.h3mf85n:0040348A |
mov |
eax, [ebp-44h] |
|
.h3mf85n:0040348D |
mov |
eax, [eax] |
|
.h3mf85n:0040348F |
xor |
eax, [ebp-3Ch] |
|
.h3mf85n:00403492 |
mov |
ecx, [ebp-44h] |
|
.h3mf85n:00403495 |
mov |
[ecx], eax |
|
.h3mf85n:00403497 |
mov |
eax, [ebp-44h] |
|
.h3mf85n:0040349A |
mov |
eax, [eax] |
|
.h3mf85n:0040349C |
xor |
eax, 2BCA6179h |
|
.h3mf85n:004034A1 |
mov |
ecx, [ebp-44h] |
|
.h3mf85n:004034A4 |
mov |
[ecx], eax |
|
.h3mf85n:004034A6 |
mov |
eax, [ebp-44h] |
|
.h3mf85n:004034A9 |
mov |
eax, [eax] |
|
.h3mf85n:004034AB |
mov |
[ebp-3Ch], eax |
|
.h3mf85n:004034AE |
mov |
eax, [ebp-44h] |
|
.h3mf85n:004034B1 |
mov |
ecx, [ebp-38h] |
|
.h3mf85n:004034B4 |
xor |
ecx, [eax] |
|
.h3mf85n:004034B6 |
mov |
[ebp-38h], ecx |
|
.h3mf85n:004034B9 |
mov |
eax, [ebp-44h] |
|
.h3mf85n:004034BC |
add |
eax, 4 |
|
.h3mf85n:004034BF |
mov |
[ebp-44h], eax |
|
.h3mf85n:004034C2 |
mov |
eax, [ebp-40h] |
|
.h3mf85n:004034C5 |
sub |
eax, 4 |
|
.h3mf85n:004034C8 |
mov |
[ebp-40h], eax |
|
.h3mf85n:004034CB |
jmp |
short loc_403484 |
|
.h3mf85n:004034CD ; ---------------------------------------------------- |
|
|
|
.h3mf85n:004034CD |
|
|
|
.h3mf85n:004034CD loc_4034CD: |
; CODE XREF: .h3mf85n:00403488_j |
|
|
.h3mf85n:004034CD |
mov |
eax, [ebp-38h] |
|
.h3mf85n:004034D0 |
mov |
dword_406008, eax |
|
.h3mf85n:004034D0 ; ---------------------------------------------------- |
|
|
|
.h3mf85n:004034D5 db 68h |
; CODE XREF: .h3mf85n:loc_40346B_j |
|
|
.h3mf85n:004034D6 dd 4034E5h |
; DATA XREF: .h3mf85n:004033F4_w |
|
|
.h3mf85n:004034DA ; ---------------------------------------------------- |
|
|
|
.h3mf85n:004034DA |
pop |
ebx |
|
.h3mf85n:004034DB |
jmp |
ebx |
|
.h3mf85n:004034DB ; ---------------------------------------------------- |
|
|
|
.h3mf85n:004034DD dword_4034DD |
dd 0DDF8286Bh, 2A7B348Ch |
|
|
.h3mf85n:004034E5 dword_4034E5 |
dd 88B9107Eh, 0E6F8C142h, 7D7F2B8Bh, |
|
|
|
0DF8902F1h, 0B1C8CBC5h |
|
|
. |
|
|
|
. |
|
|
|
. |
|
|
|
.h3mf85n:00403CE5 |
dd 157CB335h |
|
|
.h3mf85n:004041FD ; ---------------------------------------------------- |
|
|
|
.h3mf85n:004041FD |
|
|
|
.h3mf85n:004041FD loc_4041FD: |
; DATA XREF: .h3mf85n:004033E6_o |
|
|
.h3mf85n:004041FD |
pop |
edi |
|
.h3mf85n:004041FE |
pop |
esi |
|
|
|
|
|
Listing 11.7 (continued)
386 Chapter 11
.h3mf85n:004041FF |
pop |
ebx |
.h3mf85n:00404200 |
leave |
|
.h3mf85n:00404201 |
retn |
|
Listing 11.7 (continued)
This function starts out in what appears to be a familiar sequence, but at some point something very strange happens. Observe the code at address 004034DD, after the JMP EBX. It appears that IDA has determined that it is data, and not code. This data goes on and on until address 4041FD (I’ve eliminated most of the data from the listing just to preserve space). Why is there data in the middle of the function? This is a fairly common picture in copy protection code—routines are stored encrypted in the binaries and are decrypted in runtime. It is likely that this unrecognized data is just encrypted code that gets decrypted during runtime.
Let’s perform a quick analysis of the initial, unencrypted code in the beginning of this function. One thing that’s quickly evident is that the “readable” code area is roughly divided into two large sections, probably by an if statement. The conditional jump at 00403405 is where the program decides where to go, but notice that the CMP instruction at 00403401 is comparing [ebp-8] against 0 even though it is set to 1 one line before. You would usually see this kind of a sequence in a loop, where the variable is modified and then the code is executed again, in some kind of a loop. According to IDA, there are no such jumps in this function.
Since you have no reason to believe that the code at 40346D is ever executed (because the variable at [ebp-8] is hard-coded to 1), you can just focus on the first case for now. Briefly, you’re looking at a loop that iterates through a chunk of data and XORs it with a constant (2BCA6179h). Going back to where the pointer is first initialized, you get to 004033E3, where [ebp-20h] is initialized to 4034DD through the stack. [ebp-20h] is later used as the initial address from where to start the XORing. If you look at the listing, you can see that 4034DD is an address in the middle of the function—right where the code stops and the data starts.
So, it appears that this code implements some kind of a decryption algorithm. The encrypted data is sitting right there in the middle of the function, at 4034DD. At this point, it is usually worthwhile to switch to a live view of the code in a debugger to see what comes out of that decryption process. For that you can run the program in OllyDbg and place a breakpoint right at the end of the decryption process, at 0040346B. When OllyDbg reaches this address, at first it looks as if the data at 4034DD is still unrecognized data, because Olly outputs something like this:
Breaking Protections 387
004034DD |
12 |
DB 12 |
004034DE |
49 |
DB 49 |
004034DF |
32 |
DB 32 |
004034E0 |
F6 |
DB F6 |
004034E1 |
9E |
DB 9E |
004034E2 |
7D |
DB 7D |
However, you simply must tell Olly to reanalyze this memory to look for anything meaningful. You do this by pressing Ctrl+A. It is immediately obvious that something has changed. Instead of meaningless bytes you now have assembly language code. Scrolling down a few pages reveals that this is quite a bit of code—dozens of pages of code actually. This is really the body of the function you’re investigating: 4033D1. The code in Listing 11.7 was just the decryption prologue. The full decrypted version of 4033D1 is quite long and would fill many pages, so instead I’ll just go over the general structure of the function and what it does as a whole. I’ll include key code sections that are worth investigating. It would be a good idea to have OllyDbg open and to let the function decrypt itself so that you can look at the code while reading this— there is quite a bit of interesting code in this function. One important thing to realize is that it wouldn’t be practical or even useful to try to understand every line in this huge function. Instead, you must try to recognize key areas in the code and to understand their purpose.
Analyzing the Decrypted Code
The function starts out with some pointer manipulation on the NTDLL base address you acquired earlier. The function digs through NTDLL’s PE header until it gets to its export directory (OllyDbg tells you this because when the function has the pointer to the export directory Olly will comment it as ntdll.$$VProc_ImageExportDirectory). The function then goes through each export and performs an interesting (and highly unusual) bit of arithmetic on each function name string. Let’s look at the code that does this.
004035A4 |
MOV EAX,DWORD PTR [EBP-68] |
004035A7 |
MOV ECX,DWORD PTR [EBP-68] |
004035AA |
DEC ECX |
004035AB |
MOV DWORD PTR [EBP-68],ECX |
004035AE |
TEST EAX,EAX |
004035B0 |
JE SHORT Defender.004035D0 |
004035B2 |
MOV EAX,DWORD PTR [EBP-64] |
004035B5 |
ADD EAX,DWORD PTR [EBP-68] |
004035B8 |
MOVSX ESI,BYTE PTR [EAX] |
004035BB |
MOV EAX,DWORD PTR [EBP-68] |
004035BE |
CDQ |
004035BF |
PUSH 18 |
004035C1 |
POP ECX |
388 Chapter 11
004035C2 |
IDIV ECX |
004035C4 |
MOV ECX,EDX |
004035C6 |
SHL ESI,CL |
004035C8 |
ADD ESI,DWORD PTR [EBP-6C] |
004035CB |
MOV DWORD PTR [EBP-6C],ESI |
004035CE |
JMP SHORT Defender.004035A4 |
It is easy to see in the debugger that [EBP-68] contains the current string’s length (calculated earlier) and that [EBP-64] contains the address to the current string. It then enters a loop that takes each character in the string and shifts it left by the current index [EBP-68] modulo 24, and then adds the result into an accumulator at [EBP-6C]. This produces a 32-bit number that is like a checksum of the string. It is not clear at this point why this checksum is required. After all the characters are processed, the following code is executed:
004035D0 CMP DWORD PTR [EBP-6C],39DBA17A
004035D7 JNZ SHORT Defender.004035F1
If [EBP-6C] doesn’t equal 39DBA17A the function proceeds to compute the same checksum on the next NTDLL export entry. If it is 39DBA17A the loop stops. This means that one of the entries is going to produce a checksum of 39DBA17A. You can put a breakpoint on the line that follows the JNZ in the code (at address 004035D9) and let the program run. This will show you which function the program is looking for. When you do that Olly breaks, and you can now go to [EBP-64] to see which name is currently loaded. It is NtAllocateVirtualMemory. So, it seems that the function is somehow interested in NtAllocateVirtualMemory, the Native API equivalent of VirtualAlloc, the documented Win32 API for allocating memory pages.
After computing the exact address of NtAllocateVirtualMemory (which is stored at [EBP-10]) the function proceeds to call the API. The following is the call sequence:
0040365F |
RDTSC |
00403661 |
AND EAX,7FFF0000 |
00403666 |
MOV DWORD PTR [EBP-C],EAX |
00403669 |
PUSH 4 |
0040366B |
PUSH 3000 |
00403670 |
LEA EAX,DWORD PTR [EBP-4] |
00403673 |
PUSH EAX |
00403674 |
PUSH 0 |
00403676 |
LEA EAX,DWORD PTR [EBP-C] |
00403679 |
PUSH EAX |
0040367A |
PUSH -1 |
0040367C |
CALL DWORD PTR [EBP-10] |
Notice the RDTSC instruction at the beginning. This is an unusual instruction that you haven’t encountered before. Referring to the Intel Instruction Set
Breaking Protections 389
reference manuals [Intel2, Intel3] we learn that RDTSC performs a Read TimeStamp Counter operation. The time-stamp counter is a very high-speed 64-bit counter, which is incremented by one on each clock cycle. This means that on a 3.4-GHz system this counter is incremented roughly 3.4 billion times per second. RDTSC loads the counter into EDX:EAX, where EDX receives the highorder 32 bits, and EAX receives the lower 32 bits. Defender takes the lower 32 bits from EAX and does a bitwise AND with 7FFF0000. It then takes the result and passes that (it actually passes a pointer to that value) as the second parameter in the NtAllocateVirtualMemory call.
Why would defender pass a part of the time-stamp counter as a parameter to NtAllocateVirtualMemory? Let’s take a look at the prototype for NtAllocateVirtualMemory to determine what the system expects in the second parameter. This prototype was taken from http://undocumented. ntinternals.net , which is a good resource for undocumented Windows APIs. Of course, the authoritative source of information regarding the Native API is Gary Nebbett’s book Windows NT/2000 Native API Reference [Nebbett].
NTSYSAPI |
|
NTSTATUS |
|
NTAPI |
|
NtAllocateVirtualMemory( |
|
IN HANDLE |
ProcessHandle, |
IN OUT PVOID |
*BaseAddress, |
IN ULONG |
ZeroBits, |
IN OUT PULONG |
RegionSize, |
IN ULONG |
AllocationType, |
IN ULONG |
Protect ); |
It looks like the second parameter is a pointer to the base address. IN OUT specifies that the function reads the value stored in BaseAddr and then writes to it. The way this works is that the function attempts to allocate memory at the specified address and writes the actual address of the allocated block back into BaseAddress. So, Defender is passing the time-stamp counter as the proposed allocation address. . . . This may seem strange, but it really isn’t—all the program is doing is trying to allocate memory at a random address in memory. The time-stamp counter is a good way to achieve a certain level of randomness.
Another interesting aspect of this call is the fourth parameter, which is the requested block size. Defender is taking a value from [EBP-4] and using that as the block size. Going back in the code, you can find the following sequence, which appears to take part in producing the block size:
004035FE MOV EAX,DWORD PTR [EBP+8]
00403601 MOV DWORD PTR [EBP-70],EAX
390 Chapter 11
00403604 MOV EAX,DWORD PTR [EBP-70]
00403607 MOV ECX,DWORD PTR [EBP-70]
0040360A ADD ECX,DWORD PTR [EAX+3C]
0040360D MOV DWORD PTR [EBP-74],ECX
00403610 MOV EAX,DWORD PTR [EBP-74]
00403613 MOV EAX,DWORD PTR [EAX+1C]
00403616 MOV DWORD PTR [EBP-78],EAX
This sequence starts out with the NTDLL base address from [EBP+8] and proceeds to access the PE part of the header. It then stores the pointer to the PE header in [EBP-74] and accesses offset +1C from the PE header. Because the PE header is made up of several structures, it is slightly more difficult to figure out an individual offset within it. The DT command in WinDbg is a good solution to this problem.
0:000> dt _IMAGE_NT_HEADERS -b |
|
||
+0x000 Signature |
: Uint4B |
||
+0x004 FileHeader |
: |
|
|
+0x000 |
Machine |
|
: Uint2B |
+0x002 |
NumberOfSections |
: Uint2B |
|
+0x004 |
TimeDateStamp |
|
: Uint4B |
+0x008 |
PointerToSymbolTable : Uint4B |
||
+0x00c NumberOfSymbols |
: Uint4B |
||
+0x010 |
SizeOfOptionalHeader : Uint2B |
||
+0x012 |
Characteristics |
: Uint2B |
|
+0x018 OptionalHeader |
: |
|
|
+0x000 |
Magic |
|
: Uint2B |
+0x002 |
MajorLinkerVersion : UChar |
||
+0x003 |
MinorLinkerVersion : UChar |
||
+0x004 |
SizeOfCode |
|
: Uint4B |
+0x008 |
SizeOfInitializedData : Uint4B |
||
+0x00c SizeOfUninitializedData : Uint4B |
|||
+0x010 |
AddressOfEntryPoint : Uint4B |
||
+0x014 |
BaseOfCode |
|
: Uint4B |
+0x018 |
BaseOfData |
|
: Uint4B |
|
. |
|
|
|
. |
|
|
Offset +1c is clearly a part of the OptionalHeader structure, and because OptionalHeader starts at offset +18 it is obvious that offset +1c is effectively offset +4 in OptionalHeader; Offset +4 is SizeOfCode. There is one other short sequence that appears to be related to the size calculations:
0040363D MOV EAX,DWORD PTR [EBP-7C]
00403640 MOV EAX,DWORD PTR [EAX+18]
00403643 MOV DWORD PTR [EBP-88],EAX
In this case, Defender is taking the pointer at [EBP-7C] and reading offset +18 from it. If you look at the value that is read into EAX in 0040363D, you’ll
