汇编与反汇编之小技巧
大家都知道,对于位于当前调试目标中的指令,可以使用WinDBG的u命令进行反汇编。u命令的参数是要反汇编代码的地址值或者符号,如果不指定,那么WinDBG会使用当前程序指针寄存器所指向的代码,例如:
0:001> u
ntdll!DbgBreakPoint:
7c901230 cc int 3
7c901231 c3 ret
如果要想将汇编指令翻译为机器码,那么应该使用a命令,a命令使用的参数格式与u命令相同,只不过参数的含义代表的是要产生的机器码要存放的起始地址。例如执行a 0x400000命令后,WinDBG会启动交互式编辑提示符(Input>),而后便可以输入汇编指令,没输入一条后,按回车,然后可以继续输入下一条,结束时直接按回车。
举个实际的例子,启动记事本程序,然后将WinDBG附加上去,此时执行u命令看到的就是上面的反汇编结果,也就是EIP指向的是ntdll中的DbgBreakPoint函数,这个函数只有两条指令。接下来执行a命令,在Input提示符后输入nop然后按回车,而后再按回车结束汇编操作。
此时再执行u ntdll!DbgBreakPoint命令,可以看到:
0:001> u
ntdll!DbgBreakPoint:
7c901230 90 nop
7c901231 c3 ret
可见本来的INT 3指令被替换为nop指令(机器码为90)了。
输入g命令恢复记事本程序执行,然后再按Ctrl+Break试图将其中断到调试器,发现不立刻反应了,这是因为远程中断所依赖的ntdll!DbgBreakPoint函数的INT 3指令被我们替换为NOP(空指令)了。不过当WinDBG发现中断操作超时后会使用挂起的方式来中断(感兴趣的读者,可以参考《软件调试》的10.6.7节)。
上面是基本的反汇编和汇编用法。下面再说一种特殊的用法。
如果我们在日志文件或者其它环境中看到一段机器码,那么如何将其翻译为汇编指令呢?u命令是不支持后面直接跟随机器码的。
这时一种简单的方法就是找一段内存,然后将要反汇编的机器码输入到这段内存中,然后再使用u命令。哪里找这段内存呢?这段内存必须可以写。通常可以选择栈。具体来说,先使用r命令观察目前的栈顶地址,即ESP寄存器的值。为了不破坏栈上的数据,应该使用比ESP小的空闲区域。
例如:
0:001> r
eax=7ffd7000 ebx=00000001 ecx=00000002 edx=00000003 esi=00000004 edi=00000005
eip=7c901230 esp=00beffcc ebp=00befff4 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000246
ntdll!DbgBreakPoint:
7c901230 90 nop
上面的esp=00beffcc,如果我们要反汇编十几个字节的机器码,那么就可以使用00beff00开始的一段,即输入eb 00beff00,开始交互式编辑内存,输入要反汇编的机器码。
89.1E.83.C9.FF.F0.0F.C1.08.FF.75.08.E8.FD.0A.FD.FF.8B.45.08.5E.5B.5D.C2.08.00.CC.CC.CC.CC.CC.
输入结束后,再执行u 00beff00
0:001> u 00beff00
00beff00 891e mov dword ptr [esi],ebx
00beff02 83c9ff or ecx,0FFFFFFFFh
00beff05 f00fc108 lock xadd dword ptr [eax],ecx
00beff09 ff7508 push dword ptr [ebp+8]
00beff0c e8fd0afdff call 00bc0a0e
00beff11 8b4508 mov eax,dword ptr [ebp+8]
00beff14 5e pop esi
00beff15 5b pop ebx