• #### Debug Fundamentals Exercise 1: Reverse engineer a function

Hello ntdebuggers!  We’ve seen a lot of interest in our Puzzlers, and we’ve also seen requests and interest in topics covering debugging fundamentals.  So we’ve decided to combine the two topics and post a series of “Fundamentals Exercises”.  These exercises will be designed more as learning experiences rather than simply puzzlers.  We hope you find them interesting and educational!

Feel free to post your responses here, but we won’t put them on the site until after we post the “official” responses, so as to avoid spoilers.

Examine the following code, registers, and stack values to determine the following:

1.       When the function “DoTheWork” returns, what is the return value from that function?

2.       Bonus: what is the mathematical operation that “DoTheWork” performs?

Hints:

1.       The bracket notation [] in the assembly means to treat the value in brackets as a memory address, and access the value at that address.

2.       32-bit integer return values are stored in eax

// Code

0:000> uf eip

demo2!DoTheWork:

0040101c 55              push    ebp

0040101d 8bec            mov     ebp,esp

0040101f 8b4d08          mov     ecx,dword ptr [ebp+8]

00401022 8bc1            mov     eax,ecx

00401024 49              dec     ecx

00401025 0fafc1          imul    eax,ecx

00401028 83f902          cmp     ecx,2

0040102b 7ff7            jg      demo2!DoTheWork+0x8 (00401024)

0040102d 5d              pop     ebp

0040102e c3              ret

// Current register state

0:000> r

eax=00000007 ebx=7ffd9000 ecx=ffffffff edx=00000007 esi=00001771 edi=00000000

eip=0040101c esp=0012fe9c ebp=0012feac iopl=0         nv up ei pl nz na po nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202

demo2!DoTheWork:

0040101c 55              push    ebp

// Current stack values for this thread

0:000> dps esp

0012fe9c  00406717 demo2!main+0x27

0012fea0  00000007

0012fea4  82059a87

0012fea8  00000007

0012feac  0012ff88

0012feb0  004012b2 demo2!mainCRTStartup+0x170

0012feb4  00000002

0012feb8  00980e48

0012febc  00980e80

0012fec0  00000094

0012fec4  00000006

0012fec8  00000000

0012fecc  00001771

0012fed0  00000002

0012fed4  76726553

0012fed8  20656369

0012fedc  6b636150

0012fee0  00003120

0012fee4  00000000

0012fee8  00000000

0012feec  00000000

0012fef0  00000000

0012fef4  00000000

0012fef8  00000000

0012fefc  00000000

0012ff00  00000000

0012ff04  00000000

0012ff08  00000000

0012ff0c  00000000

0012ff10  00000000

0012ff14  00000000

0012ff18  00000000

Wow - what a great response from our readers on this exercise!  It is great to see the various approaches to reverse engineering this code.  As for the answer, the numerical result (stored in eax) is 5040, and the corresponding mathematical operation is a factorial.  So 7! is the result calculated, given that 7 was passed to the function.  Congratulations to all of you that got it right!

Many of you posted some C code to give an idea of what the original source of DoTheWork() might have looked like.  The original source was actually written in assembly!  However, it was written to be called from C, and it uses ebp in the same way that compiled C code might.  This function wasn’t written with optimal performance in mind, but rather for learning about reverse engineering.

• #### Debug Fundamentals Exercise 2: Some reverse engineering for Thanksgiving

Continuing our series on “Fundamentals Exercises”, we have some more reverse engineering for you!  Again, these exercises are designed more as learning experiences rather than simply puzzlers.  We hope you find them interesting and educational!  Feel free to post your responses here, but we won’t put them on the site until after we post the “official” responses, to avoid spoilers.

Examine the following code, registers, and stack values to determine the following:

1.       What is the return value from DemoFunction2?

2.       What is the purpose of DemoFunction2?

3.       Bonus: Both the last exercise and this week’s exercise involved accessing data at ebp+8.  Why ebp+8?

Hints:

1.       You probably don’t want to manually walk through every instruction that executes in the loop.  Instead, walk through a few iterations to determine the intent of the code.

2.       The bracket notation [] in the assembly means to treat the value in brackets as a memory address, and access the value at that address.

3.       32-bit integer return values are stored in eax

0:000> uf 010024d0

asmdemo2!DemoFunction2:

010024d0 55              push    ebp

010024d1 8bec            mov     ebp,esp

010024d3 8b5508          mov     edx,dword ptr [ebp+8]

010024d6 33c0            xor     eax,eax

010024d8 b920000000      mov     ecx,20h

010024dd d1ea            shr     edx,1

010024df 7301            jnc     asmdemo2!DemoFunction2+0x12 (010024e2)

010024e1 40              inc     eax

010024e2 e2f9            loop    asmdemo2!DemoFunction2+0xd (010024dd)

010024e4 5d              pop     ebp

010024e5 c3              ret

0:000> r

eax=80002418 ebx=7ffd7000 ecx=00682295 edx=00000000 esi=80002418 edi=00000002

eip=010024d0 esp=0006fe98 ebp=0006fea8 iopl=0         nv up ei pl zr na pe nc

cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246

asmdemo2!DemoFunction2:

010024d0 55              push    ebp

0:000> dps esp

0006fe98  0100251c asmdemo2!main+0x20

0006fe9c  80002418

0006fea0  00000002

0006fea4  00000000

0006fea8  0006ff88

0006feac  01002969 asmdemo2!_mainCRTStartup+0x12c

0006feb0  00000002

0006feb4  00682270

0006feb8  006822b8

0006febc  f395c17d

0006fec0  00000000

0006fec4  00000000

0006fec8  7ffd7000

0006fecc  00000000

0006fed0  00000000

0006fed4  00000000

0006fed8  00000094

0006fedc  00000006

0006fee0  00000000

0006fee4  00001771

0006fee8  00000002

0006feec  76726553

0006fef0  20656369

0006fef4  6b636150

0006fef8  00003120

0006fefc  00000000

0006ff00  00000000

0006ff04  00000000

0006ff08  00000000

0006ff0c  00000000

0006ff10  00000000

0006ff14  00000000

We had a great response to this exercise!  It was good to see so many of you going through this.  There were some readers that found this a good exercise for beginners, and others were looking for a return to Puzzlers.  As an FYI, we may do more Puzzlers in the future, but for now we are going to continue on the “Fundamentals Exercise” track to help all our readers build up a solid foundation for debugging.

It was interesting to read how several of you not only gave the answers, but made suggestions for how the code could be optimized!  I want to point out that the code we post for these exercises isn’t intended to be the optimal solution; it is written as a learning tool.   That said, keep that in-depth feedback coming; I think everyone will benefit from a discussion of optimization.

1. DemoFunction2 returns 5, which is the number of bits set in 80002418, the value at ebp+8.
2. DemoFunction2 finds the hamming weight of the 32-bit value passed to the function (it returns the count of bits that are equal to 1).
3. ebp points to the base of the stack frame (the value stored there points to the previous frame's ebp), ebp+4 points to the return address, and ebp+8 points to the first parameter passed to the function.  Note that the value of ebp changes in the function prolog, at instruction 010024d1.  At this point ebp is set to 0006fe94, so at instruction 010024d3, ebp+8 is 0006fe9c, and [ebp+8] = 80002418.
• #### How to modify an application behavior when you don't have the source

From time to time we need to help customers change the way an application interacts with the operating system or SDKs.  The challenge is often the access to the code.  Sometimes neither party may own the application in question and none of the parties have access to the source.   Luckily, the Microsoft Research team came up with the Detours SDK to address this problem a number of years ago and the latest version makes it easy to implement a solution to a situation like this.  In short, Detours allows you to create a DLL that hooks one or more operating system functions, so that when that function is called, the caller will actually invoke your custom Detours code instead.

The process is very simple:

·         You can start with the SIMPLE Sample or our included sample that builds in the Visual Studio command-line environment.

·         Create a function pointer prototype for the  API you want to detour (TrueCreateFile in the example below).   It should have the same parameters and return value as the function you will detour.  As part of the declaration set the function pointer value to the real API Address.  In the following sample we will detour the CreateFile API.

·         You will also need to create your own version of the API you are detouring (ModifyCreateFile below).   In this case we are creating our own Createfile, which will call the original CreateFile with the FILE_FLAG_WRITE_THROUGH flag OR’d into the dwFlagsAndAttributes parameter.

static HANDLE (WINAPI * TrueCreateFile)(LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) = CreateFile;

HANDLE WINAPI ModifyCreateFile(LPCTSTR lpFileName,  DWORD dwDesiredAccess, DWORD dwShareMode,

LPSECURITY_ATTRIBUTES   lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)

{

dwFlagsAndAttributes |= FILE_FLAG_WRITE_THROUGH;

return TrueCreateFile(lpFileName, dwDesiredAccess,  dwShareMode, lpSecurityAttributes,

dwCreationDisposition,      dwFlagsAndAttributes,  hTemplateFile);

}

·         You will need to write your detour code in the DLLmain of your dll.   This should be executed when your DLL loads and dwReason is == DLL_PROCESS_ATTACH.   In our call to DetourAttach we pass our TrueCreateFile pointer (the real CreateFile address),  and the address of ModifyCreateFile (our custom create file api).  The detour API handles the intercept for us.

DetourRestoreAfterWith();

DetourTransactionBegin();

DetourAttach(&(PVOID&)TrueCreateFile, ModifyCreateFile);

DetourTransactionCommit();

·         When the DLL_PROCESS_DETACH happens you will need to clean up the detour and unhook the real API.

DetourTransactionBegin();

DetourDetach(&(PVOID&)TrueCreateFile, ModifyCreateFile);

DetourTransactionCommit();

So how do you get the DLL loaded into the target process?  There are a couple ways.  I recommend using the setdll tool that comes as part of the Detour SDK.  In the following case we are modifying NTBackup to automatically load our detoured DLL when NTbackup runs.

C:\test>setdll /d:nocache.dll ntbackup.exe

ntbackup.exe:

nowritethru.dll

MFC42u.dll -> MFC42u.dll

msvcrt.dll -> msvcrt.dll

KERNEL32.dll -> KERNEL32.dll

GDI32.dll -> GDI32.dll

USER32.dll -> USER32.dll

ntdll.dll -> ntdll.dll

COMCTL32.dll -> COMCTL32.dll

SHELL32.dll -> SHELL32.dll

MPR.dll -> MPR.dll

comdlg32.dll -> comdlg32.dll

NETAPI32.dll -> NETAPI32.dll

RPCRT4.dll -> RPCRT4.dll

ole32.dll -> ole32.dll

SETUPAPI.dll -> SETUPAPI.dll

USERENV.dll -> USERENV.dll

NTMSAPI.dll -> NTMSAPI.dll

CLUSAPI.dll -> CLUSAPI.dll

query.dll -> query.dll

sfc_os.dll -> sfc_os.dll

SYSSETUP.dll -> SYSSETUP.dll

OLEAUT32.dll -> OLEAUT32.dll

VSSAPI.DLL -> VSSAPI.DLL

Note that if you modify a binary that is protected by Windows File Protection the modified binary will be replaced by the OS with the original binary.  I recommend keeping your modified version in another directory so it does not get replaced.

Click here for the sample source code and Makefile, which will build from a Visual Studio command prompt.

Jeff Dailey

Page 1 of 1 (3 items)