IDA Pro是一款强大的反汇编软件,特有的IDA视图和交叉引用,可以方便理解程序逻辑和快速定位代码片断,以方便修改。
IDA视图
示例程序
下面会通过修改示例程序的输出字符串,来讲解如何使用IDA Pro。
#include main() { int n; scanf ("%d",&n); if (n > 0) printf("a > 0"); //后面会用IDA Pro把'a'改成'n' else printf("n < 0"); }
编译后的程序下载:demo
运行IDA Pro
运行IDAPro,并使用PE文件的方式打开示例的test.exe文件。IDAPro会新建一个工程,并开始反汇编程序。反汇编完成后,在[IDA-View]窗口中,可以看到程序逻辑的树形图,如下:
树形图把条件分支清晰地显示出来了,绿色线连着的表示条件为true时执行的逻辑,而红色线表示条件为false时执行的逻辑。右下角有IDA视图的缩略图,在上面点击可以快速定位到视图的指定位置。IDA的工具栏有几个按钮对定位代码很重要,如下图所示:
从左到右分别是:
定位代码片断
假设我们现在接到个任务,需修正程序,把输出“a > 0”修正为“n >0”。示例程序比较简单,直接看IDA视图我们就能找到需修改的代码片断,但实际处理时,可能程序有几m大,通过一个个看IDA视图已没法有效找到相关的执行代码片断,这时怎么办?使用字符串窗口和IDA强大的交叉引用! 点击工具栏的[Open stringswindows]按钮,可以看到如下的程序字符串:
程序的字符串较少,可以很快地看到我们需要的字符串“a > 0”在数据段00403003位置。假如字符串多到已不能肉眼定位查找,因为字符串窗口是没有查找功能的,这时需要借助其他的文本编辑器,如notepad,editplus等。在字符串窗口内右键,选择菜单[copy]命令,会把字符串窗口的所有内容复制到剪贴板,再粘贴到记事本中查找就可以了。
该位置的字符串后面会注释有DATA XREF的字样,这是程序中引用到该字符串的代码片断的地址!在该行上右键,选择[Jump tocross reference...]项,会立即跳转到引用该字符串的代码片断位置!
最后定位的代码片断 上图显示的汇编指令即是我们要找的代码片断,这时点击[HexView-A]窗口,会切换到二进制浏览模式,并高亮了汇编代码的二进制格式指令,如下图所示:
已找到需修改的代码片断,剩下的只需把a改成n。
修改程序文件
在IDA中,可以在[Hex View-A]窗口右键选择[Edit]来修改二进制指令。修改后通过右键选择[CommitChange]可以看到修改后的IDA视图。但需要注意的是,这种方式的修改并不会更新原始程序文件,实际只是修改了IDA的项目文件!IDA中只适合做一些验证性的修改,确保正确后再使用其他工具修改原始程序文件。在IDA中验证修改正确后,可以使用UltraEdit或HexWorkshop来修改原始程序文件。下面会以UltraEdit为例来说明如何修改。
用UltraEdit直接打开程序文件,UltraEdit会以16进制模式显示程序文件。UltraEdit显示的地址和IDA显示的地址是不同的,为了找到对应代码片断在UltraEdit中的实际地址,需要使用到UltraEdit的查找功能。在IDA中复制需修改的16进制模式显示的指令,在UltraEdit中打开查找,粘贴并查找该16进制字符串,UltrEdit会很快定位到该指令处,如下图所示:
在IDA中使得右键来复制
在UltraEdit打开查找功能
找到了UltraEdit的对应位置 现在我们要把“a > 0”改成“n >0”,a对应的ASCII码是61,而n对应的ASCII码是6E,只需把61改成6E就可以了,修改后保存。
再次运行,可以看到结果已改变!
示例只是修改了字符串,只需更改数据段内容就可以了,不用更改指令。假如需要更改指令,需要参考< ahref="http://courses.engr.illinois.edu/ece390/resources/opcodes.html#Main">8086指令操作表写出对应指今的16进制形式,再修改。
参考资料:
http://blog.youkuaiyun.com/liquanhai/article/details/5479141
http://www.youtube.com/watch?v=Gl2S0YPRb9s
http://www.woodmann.com/crackz/Tutorials/Flores1.htm
http://courses.engr.illinois.edu/ece390/resources/opcodes.html
http://faydoc.tripod.com/cpu/conventions.htm
http://hi.baidu.com/onepc/blog/item/bb217259aa539a212834f0f1
GE:000105D0AllIrpComplete procnear |
PAGE:00010503 ; Attributes: bp-based frame
PAGE:00010503
PAGE:00010503ConExeToSys
PAGE:00010503
PAGE:00010503var_28
PAGE:00010503var_24
PAGE:00010503SourceString
PAGE:00010503var_1C
PAGE:00010503 UnicodeString
PAGE:00010503 DestinationString= STRING ptr -10h;变量3=>同上
PAGE:00010503var_8
PAGE:00010503var_4
PAGE:00010503arg_4
PAGE:00010503
这个是主要的分发例程,看变量有N个。
PAGE:00010503
PAGE:00010504
PAGE:00010506
PAGE:00010509
PAGE:00010510
PAGE:00010513
+0x040Tail
上面是dt_IRP -r得出的信息 60h偏移处是 _IO_STACK_LOCATION结构的地址
PAGE:00010516
var_8这个变量就是pIoStackLocation=IoGetCurrentIrpStackLoca
PAGE:00010519
PAGE:0001051C
这个结构太难看了,windbg及ddk定义的结构,太多共用体之类的了。
共用体的最大存储字节是其中的元素最大的那个为准。
PAGE:0001051F
var_1C =>uControlCode
PAGE:00010522
PAGE:00010525
PAGE:00010528
ebp+var_4=>uInBufferLength;
PAGE:0001052B
var_1C=>uControlCode
PAGE:0001052E
控制码传给var_28变量
PAGE:00010531
用这个变量与222000h比较[这个即是应用层传来的控制码]
PAGE:00010538
相等跳到
PAGE:0001053A
PAGE:0001053C ;---------------------------------------------------------------------------
PAGE:0001053C
PAGE:0001053Cloc_1053C:
PAGE:0001053C
PAGE:0001053F
+0x00cAssociatedIrp
PAGE:00010542
传给sourcestring
pIrp->AssociatedIrp.SystemBuffer;
PAGE:00010545
PAGE:00010548
PAGE:00010549
PAGE:0001054C
PAGE:0001054D
PAGE:00010553
PAGE:00010555
PAGE:00010558
PAGE:00010559
PAGE:0001055C
PAGE:0001055D
RtlAnsiStringToUnicodeSt
PAGE:00010563
PAGE:00010566
PAGE:0001056A
PAGE:0001056C
PAGE:00010571
PAGE:00010576
PAGE:00010579
PAGE:0001057B ;---------------------------------------------------------------------------
PAGE:0001057B
PAGE:0001057Bloc_1057B:
PAGE:0001057B
PAGE:0001057E
PAGE:0001057F
PAGE:00010582
PAGE:00010583
PAGE:00010588
PAGE:0001058B
PAGE:0001058C
PAGE:00010592
PAGE:00010594 ;---------------------------------------------------------------------------
PAGE:00010594
PAGE:00010594loc_10594:
PAGE:00010594
#defineSTATUS_INVALID_VARIANT
PAGE:0001059B
PAGE:0001059Bloc_1059B:
PAGE:0001059B
PAGE:0001059B
PAGE:0001059E
PAGE:000105A1
PAGE:000105A4
PAGE:000105A7
PAGE:000105AE
PAGE:000105B0
PAGE:000105B3
PAGE:000105B9
PAGE:000105BC
PAGE:000105BE
PAGE:000105BF
PAGE:000105BFConExeToSys
PAGE:000105BF
总结:反汇编的主要是一些变量分配及堆栈平衡及一些结构成员的赋值。多看应会慢慢熟悉。还有各种流程控制语句要多练。
PAGE:0001057B
PAGE:0001057E
PAGE:0001057F
PAGE:00010582
PAGE:00010583
这里可以看到,当压入一个UnicodeString字符时,会把缓冲区及长度分别压入,所以这种字符串不用以0为标志作为结尾。
SetReg
INIT:00010938
INIT:00010938var_54
INIT:00010938var_50
INIT:00010938var_4C
INIT:00010938var_48
INIT:00010938var_44
INIT:00010938var_40
INIT:00010938var_3C
INIT:00010938 DestinationString= UNICODE_STRING ptr -38h
INIT:00010938KeyHandle
INIT:00010938 ObjectAttributes= OBJECT_ATTRIBUTES ptr-2Ch
INIT:00010938ValueName
INIT:00010938Handle
INIT:00010938Disposition
INIT:00010938Data
INIT:00010938arg_0
INIT:00010938
INIT:00010938
INIT:00010939
INIT:0001093B
INIT:0001093E
INIT:00010943
INIT:00010946
INIT:00010947
INIT:0001094D
INIT:00010954
INIT:0001095B
INIT:00010962
INIT:00010965
INIT:00010968
INIT:0001096F
INIT:00010976
INIT:00010979
INIT:0001097A
INIT:0001097F
INIT:00010982
INIT:00010983
INIT:00010989
INIT:0001098C
INIT:00010990
INIT:00010992
INIT:00010997
INIT:0001099C
INIT:0001099F
INIT:000109A4 ;---------------------------------------------------------------------------
INIT:000109A4
INIT:000109A4loc_109A4:
INIT:000109A4
INIT:000109AB
INIT:000109AE
INIT:000109B1
INIT:000109B8
INIT:000109BB
INIT:000109BE
INIT:000109C5
INIT:000109CC
INIT:000109CF
INIT:000109D0
INIT:000109D2
INIT:000109D4
INIT:000109D6
INIT:000109D9
INIT:000109DA
INIT:000109DF
INIT:000109E2
INIT:000109E3
INIT:000109E9
INIT:000109EC
INIT:000109F0
INIT:000109F2
INIT:000109F6
INIT:000109F8
INIT:000109FD
INIT:00010A02
INIT:00010A05
INIT:00010A07 ;---------------------------------------------------------------------------
INIT:00010A07
INIT:00010A07loc_10A07:
INIT:00010A07
INIT:00010A0B
INIT:00010A0D
INIT:00010A12
INIT:00010A17
INIT:00010A1A
INIT:00010A1Aloc_10A1A:
INIT:00010A1A
INIT:00010A1A
INIT:00010A1F
INIT:00010A22
INIT:00010A23
INIT:00010A29
INIT:00010A30
INIT:00010A33
INIT:00010A34
INIT:00010A3A
INIT:00010A3D
INIT:00010A41
INIT:00010A42
INIT:00010A45
INIT:00010A46
INIT:00010A48
INIT:00010A4A
INIT:00010A4D
INIT:00010A4E
INIT:00010A51
INIT:00010A52
INIT:00010A58
INIT:00010A5B
INIT:00010A5C
INIT:00010A62
INIT:00010A65
INIT:00010A66
INIT:00010A6C
INIT:00010A6Cloc_10A6C:
INIT:00010A6C
INIT:00010A6E
INIT:00010A6F
INIT:00010A6FSetReg
=====================================================================
3 IDA反汇编工具初探
对于程序员来说,增长自己编程功力的一个好方法是阅读其它人开发的程序的源码,从而把别人的技术来消化成为自己知识,这是不是很象吸星大法?
但开源的程序毕竟是在少数,大多数程序都只会分发可执行文件及相关文件,这时我们要想查看此程序的代码,就只有把它反汇编,当然这需要一定的汇编功底,但是一个好的反汇编工具能为你阅读反汇编出来的程序提供非常大的帮助。
了解反汇编的朋友也一定知道WINDASM这个有名的反汇编工具,比如我们用WINDASM反汇编一个程序,在其程序入口点反汇编得到如下代码:
//*********************** Program Entry Point*****************
:00401000 6A00 push 00000000
:00401002 E8FF050000 call 00401606
:00401007 A316304000 mov [00403016], eax
:00401007 E8EF050000 call 00401600
:00401011 A30E304000 mov [0040300E], eax
:00401016 6A0A push 0000000A
:00401018 FF350E304000 push dword ptr [0040300E]
:0040101E 6A00 push 00000000
:00401020 EF3516304000 push dword ptr [00403016]
:00401026 E806000000 call 00401031
:0040102B 50 push eax
:0040102c E8C9050000 call 004015FA
如果不联系上下文及知道这是程序入口的话,很难看出来这一段代码到底是干什么的,但IDA就不一样了,它不但会反汇编程序,并会尽量分析程序,并加上相应的注释(正因为这样,IDA反汇编一个大的程序会花非常长的时间),请看下面一段IDA反汇编出来的代码,是不是明了多了?
.text:00401000 push 0 ;lpModuleName
.text:00401002 call GetModuleHandleA
.text:00401007 mov hInstance, eax
.text:0040100C call GetCommandLineA
.text:00401011 mov dword_0_40300E, eax
.text:00401016 push 0Ah
.text:00401018 push dword_0_40300E
.text:0040101E push 0
.text:00401020 push hInstance
.text:00401026 call sub_0_401031
.text:0040102B push eax ;uExitCode
.text:0040102C call ExitProcess
IDA反汇编程序后,会生成一个.idb文件,里面保存了反汇编出来的代码及注释及IDA的一些其它相关数据,我们可以直接在IDA中写自己的分析结果和注释并保存,下次直接打开.idb文件就可以了,例如上面
.text:00401000 push 0 ;lpModuleName
.text:00401002 call GetModuleHandleA
.text:00401007 mov hInstance, eax
我们可以看出来实际上就是hInstance=GetModuleHandleA(nil);我们可以在后面直接加上注释,在.text:00401007这一行最后面的空白处点右键,在弹出的菜单中选择"注释",然后在弹出的窗口中填上"取得当前模块实例句柄",这一行就会变为
.text:00401007 mov hInstance, eax ;取得当前模块实例句柄
这样就为我们的反汇编出的代码增加了可读性.
IDA不但可以在当前代码中加注释,还可以更改其默认的符号名,比如
.text:00401011 mov dword_0_40300E, eax
其中的dwrd_0_40300E可以看出来是存放取得的命令行的缓冲区指针(可以双击符号名,函数名跳到其定义处),在dword_0_40300E上面点右键,选取"重命名",然后在弹出的窗口中填入lpCommandline,点确定,这样程序中所有使用到了dword_0_40300E这个变量的地方都会将dword_0_40300E替换为lpCommandline.如下所示:
.text:00401011 mov lpCommandline, eax
.text:00401016 push 0Ah
.text:00401018 push lpCommandline
我们再来看.text:00401026 call sub_0_401031这一行
可以从上面的代码看出来,这是调用的WinMain函数,在sub_0_401031上面点右键,选取"重命名",然这个函数命名为WinMain,这时IDA就将所有sub_0_401031符号变为WinMain,并且自动加上函数定义,并会在函数调用时入栈的参数后面加上其对应的变量注释,这时我们反汇编出来的这一段代码就成了下面这个样子的了:
.text:00401000 start proc near
.text:00401000 push 0 ;lpModuleName
.text:00401002 call GetModuleHandleA
.text:00401007 mov hInstance, eax ;取得当前模块实例句柄
.text:0040100C call GetCommandLineA
.text:00401011 mov lpCommandline, eax
.text:00401016 push 0Ah ;nShowCmd
.text:00401018 push lpCommandline ;lpCmdLine
.text:0040101E push 0 ;hPrevInstance
.text:00401020 push hInstance ;hInstance
.text:00401026 call WinMain
.text:0040102B push eax ;uExitCode
.text:0040102C call ExitProcess
是不是一目了解了呢?
当我们通过阅读源码,能确定某一个子函数的作用及传入的参数类型时,我们可以双击这个函数名,跳到函数定义处,在函数定义处点右键,使用"设置函数类型"功能来编辑函数定义(C++语法),这样所有调用到这个函数的地方都会在入栈的参数后面加上其对应的变量注释.还可以通过在函数定义处后面空白处点右键加上"可重复注释",这样所有调用此函数的地方都会在后面加上这个重复的注释.
如果想查看某个变量或函数被调用的情况,可以通过在函数或变量名上点右键,点击"查看操作数交叉索引处"功能,就可以在打开的窗口中查看到所有调用其的代码,并可通过双击跳到这段代码处.这是一个很有用的功能,能帮助你快速的搞清函数及变量的调用关系.
按下F12还可以查看到程序的流程图,CTRL+12可以查看到函数的调用图.
IDA还拥有符号调试技术,能识别常见编释器编释的程序,例如下面反汇编出的VC6.0的程序代码段:
.text:00405427 push edx
.text:00405428 call _swscanf
.text:0040542D lea eax, [esp+38h+arg_40]
.text:00405431 push offset unk_0_5DB1A4 ;const wchar_t *
.text:00405436 push eax ;const wchar_t *
.text:00405437 call _wcscmp
.text:0040543C add esp, 1Ch
.text:0040543F test eax, eax
.text:00405441 jz short loc_0_405459
.text:00405443 lea ecx, [esp+24h+arg_40]
.text:00405447 push offset unk_0_5DB18C ;const wchar_t *
.text:0040544C push ecx ;const wchar_t *
.text:0040544D call _wcscmp
就检查到了其调用了MFC类库中的函数,并把它们替换成了相应的函数名.
还可以调用IDA导出.MAP文件,来配合其它动态调试工具如SOFT-ICE来进行代码分析.
IDA是一个非常强大的反汇编工具,这里只是讨论了一下它的一些基本的应用,希望能起到抛砖引玉的作用
汇编语言里 eax, ebx, ecx, edx, esi, edi, ebp,esp这些都是什么意思啊?
eax, ebx, ecx, edx, esi, edi, ebp, esp等都是X86汇编语言中CPU上的通用寄存器的名称,是32位的寄存器。如果用C语言来解释,可以把这些寄存器当作变量看待。
比方说:
这些32位寄存器有多种用途,但每一个都有“专长”,有各自的特别之处。
EAX
EBX
ECX
EDX
ESI/EDI
EBP
push ebp;
mov ebp,esp;
sub esp, xxx;
...
这样一来,EBP 构成了该函数的一个框架, 在EBP上方分别是原来的EBP, 返回地址和参数. EBP下方则是临时变量.函数返回时作
ESP