Bradley Grainger home

Profiling JScript, Part 0

As is my wont, I recently started looking at JScript.dll disassembly again and wondering about the possibility of implementing a JScript profiler. It seems that with XP SP2 I now have symbols for JScript.dll on my system, which makes it a lot easier to understand the code. (Symbols provide function names and parameter lists for those functions.) While reading the assembly listings, I noticed that most functions look like this:

mov     edi,edi
push    ebp
mov     ebp,esp
   ...
ret
nop
nop
nop
nop
nop

The second and third lines are standard x86 function prologue (setting up the stack), but I couldn't understand the apparently-useless mov edi, edi (or edi = edi in C++) instruction at the begining of every function. There's also a five-byte gap between functions (that seems to be always five bytes, and not a variable number, e.g., to force functions to align on 16-byte boundaries). A quick Google search turned up Ishai's blog post "Why does the compiler generate a MOV EDI, EDI instruction at the beginning of functions?". In short, this extraneous two-byte instruction (and the five bytes of nop (no operation, i.e., do nothing) at the end) are inserted to enable hot-patching of the application. The five nop bytes can be replaced with a "long jump" instruction (the jmp opcode, and a 32-bit address to jump to), while the two-byte mov edi, edi instruction can be replaced with a "short jump" instruction (the jmp opcode, and an 8-bit offset) that jumps to the long jump. But why use mov edi, edi instead of two nop instructions? If you are patching the application while it's running, it's theoretically possible that one thread could have executed the first nop but not the second. If your patch thread then changes both bytes (to the short jump), the thread that's about to read the second nop instruction will instead read the offset your patch just inserted, and attempt to interpret that offset as a valid x86 opcode. Bad things would almost certainly happen. mov edi, edi takes two bytes, and so it's impossible for the instruction pointer to be pointing at the second byte. This should enable us to hook certain functions in JScript.dll very easily. Since JScript.dll is loaded into our process's address space, we can change the bytes making up its code easily. We first get the address of our profiling function and overwrite the five nop bytes with a jump to that address. We then change the mov edi, edi instruction to jump to our long jump. Now, whenever that function is called, execution will quickly pass to our profiling function. We just have to make sure we return to the right instruction with the stack exactly as we found it. How hard could that be?