The next instruction is add [esp+4+var_4], 5
. If you are used to reading IDA Pro disassembly, you might think that this instruction is referencing a stack variable var_4
. In this case, IDA Pro’s stack-frame analysis was incorrect, and this instruction was not referencing what would be a normal stack variable, autonamed to var_4
in an ordinary function. This may seem confusing at first, but notice that at the top of the function, var_4
is defined as the constant -4
. This means that what is inside the brackets is [esp+4+(-4)]
, which can also be represented as [esp+0]
or simply [esp]
. This instruction is adding five to the value at the top of the stack, which was 0x004011C5
. The result of the addition instruction is that the value at the top of the stack will be 0x004011CA
.
The last instruction in this sequence is the retn
instruction, which has the sole purpose of taking this value off the stack and jumping to it. If you examine the code at the location 0x004011CA, it appears to be the legitimate beginning of a rather normal-looking function. This “real” function was determined by IDA Pro to not be part of any function due to the presence of the rogue retn
instruction.
To repair this example, we could patch over the first three instructions with NOP instructions and adjust the function boundaries to cover the real function.
To adjust the function boundaries, place the cursor in IDA Pro inside the function you wish to adjust and press ALT-P. Adjust the function end address to the memory address immediately following the last instruction in the function. To replace the first few instructions with nop
, refer to the script technique described in .
To add a record to this list, we need to construct a new record on the stack. Since the record structure is simply two DWORD
s, we can do this with two push
instructions. The stack grows upward, so the first push
will be the pointer to the handler function, and the second push
will be the pointer to the next record. We are trying to add a record to the top of the chain, so the next record in the chain when we finish will be what is currently the top, which is pointed to by fs:[0]
. The following code performs this sequence.
push ExceptionHandler push fs:[0] mov fs:[0], esp
The ExceptionHandler
function will be called first whenever an exception occurs. This action will be subject to the constraints imposed by Microsoft’s Software Data Execution Prevention (Software DEP, also known as SafeSEH).
Software DEP is a security feature that prevents the addition of third-party exception handlers at runtime. For purposes of handwritten assembly code, there are several ways to work around this technology, such as using an assembler that has support for SafeSEH directives. Using Microsoft’s C compilers, an author can add /SAFESEH:NO
to the linker command line to disable this.
When the ExceptionHandler
code is called, the stack will be drastically altered. Luckily, it is not essential for our purposes to fully examine all the data that is added to the stack at this point. We must simply understand how to return the stack to its original position prior to the exception. Remember that our goal is to obscure flow control and not to properly handle program exceptions.
The OS adds another SEH handler when our handler is called. To return the program to normal operation, we need to unlink not just our handler, but this handler as well. Therefore, we need to pull our original stack pointer from esp+8
instead of esp
.
mov esp, [esp+8] mov eax, fs:[0] mov eax, [eax] mov eax, [eax] mov fs:[0], eax add esp, 8
Let’s bring all this knowledge back to our original goal of obscuring flow control. The following fragment contains a piece of code from a Visual C++ binary that covertly transfers flow to a subroutine. Since there is no pointer to this function and the disassembler doesn’t understand SEH, it appears as though the subroutine has no references, and the disassembler thinks the code immediately following the triggering of the exception will be executed.
00401050 ❷mov eax, (offset loc_40106B+1) 00401055 add eax, 14h 00401058 push eax 00401059 push large dword ptr fs:0 ; dwMilliseconds 00401060 mov large fs:0, esp 00401067 xor ecx, ecx 00401069 ❸div ecx 0040106B 0040106B loc_40106B: ; DATA XREF: sub_401050o 0040106B call near ptr Sleep 00401070 retn 00401070 sub_401050 endp ; sp-analysis failed 00401070 00401070 ; ------------------------------------------------------------------ 00401071 align 10h 00401080 ❶dd 824648Bh, 0A164h, 8B0000h, 0A364008Bh, 0 00401094 dd 6808C483h 00401098 dd offset aMysteryCode ; "Mystery Code" 0040109C dd 2DE8h, 4C48300h, 3 dup(0CCCCCCCCh)
In this example, IDA Pro has not only missed the fact that the subroutine at location 401080 ❶ was not called, but it also failed to even disassemble this function. This code sets up an exception handler covertly by first setting the register EAX to the value 40106C
❷, and then adding 14h
to it to build a pointer to the function 401080
. A divide-by-zero exception is triggered by setting ECX to zero with xor ecx, ecx
followed by div ecx
at ❸, which divides the EAX register by ECX.
Let’s use the C key in IDA Pro to turn the data at location 401080 into code and see what was hidden using this trick.
00401080 mov esp, [esp+8] 00401084 mov eax, large fs:0 0040108A mov eax, [eax] 0040108C mov eax, [eax] 0040108E mov large fs:0, eax 00401094 add esp, 8 00401097 push offset aMysteryCode ; "Mystery Code" 0040109C call printf