Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Eilam E.Reversing.Secrets of reverse engineering.2005

.pdf
Скачиваний:
69
Добавлен:
23.08.2013
Размер:
8.78 Mб
Скачать

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