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
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
[Update: our answer. Posted 12/04/2008]
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.
Answers to exercise 2:
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:
· Download the detours SDK http://research.microsoft.com/sn/detours/ and build it.
· 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();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)TrueCreateFile, ModifyCreateFile);
DetourTransactionCommit();
· When the DLL_PROCESS_DETACH happens you will need to clean up the detour and unhook the real API.
DetourDetach(&(PVOID&)TrueCreateFile, ModifyCreateFile);
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
Adding nocache.dll to binary files.
ntbackup.exe:
nowritethru.dll
MFC42u.dll -> MFC42u.dll
msvcrt.dll -> msvcrt.dll
ADVAPI32.dll -> ADVAPI32.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
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.
1. When the function “DoTheWork” returns, what is the return value from that function?
2. Bonus: what is the mathematical operation that “DoTheWork” performs?
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
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
// Current stack values for this thread
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
[Update: our answer. Posted 11/19/2008]
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.