[Paper]逆向分析点滴
Author: void#ph4nt0m.org
Date: 2007.05.15
Publish Date: 2007.06.04
http://www.ph4nt0m.org
征女友!有意者请点击
Index
========================================
1. IDA的使用
2. 编译器优化
3. Viusal C++, Borland Delphi程序的逆向分析
4. 算法识别技巧(常用的加解密算法)
1. IDA使用
=========================================
工欲善其事,必先利其器.在开始前,先熟悉下IDA的使用.
[1] 先说一些常用的功能.
(1)编译器设置(选项->编译器)用来指定IDA所分析文件是什么编译器生成的,如Visual C++,Borland C++,Delphi.这有什么用呢?
比如下面是Delphi的反汇编片段,如果编译器设定为Viusal C++:
code:00479A8F lea edx, [ebp+var_40]
code:00479A92 mov eax, [ebp+someString]
code:00479A95 call @Sysutils@Trim$qqrx17System@AnsiString
code:00479A95
code:00479A9A mov edx, [ebp+var_40]
code:00479A9D lea eax, [ebp+someString]
code:00479AA0 call @System@@LStrLAsg$qqrpvpxv
code:00479A92 mov eax, [ebp+someString]
code:00479A95 call @Sysutils@Trim$qqrx17System@AnsiString
code:00479A95
code:00479A9A mov edx, [ebp+var_40]
code:00479A9D lea eax, [ebp+someString]
code:00479AA0 call @System@@LStrLAsg$qqrpvpxv
函数参数类型和个数很难一眼看出来.把编译器设定为Delphi后,一目了然:
code:00479A8F lea edx, [ebp+var_40]
code:00479A92 mov eax, [ebp+someString]
code:00479A95 call Sysutils::Trim(System::AnsiString)
code:00479A95
code:00479A9A mov edx, [ebp+var_40]
code:00479A9D lea eax, [ebp+someString]
code:00479AA0 call System::__linkproc__ LStrLAsg(void *,void *)
code:00479A92 mov eax, [ebp+someString]
code:00479A95 call Sysutils::Trim(System::AnsiString)
code:00479A95
code:00479A9A mov edx, [ebp+var_40]
code:00479A9D lea eax, [ebp+someString]
code:00479AA0 call System::__linkproc__ LStrLAsg(void *,void *)
(2)签名(查看->打开下级查看->签名,右键->Apply new signature...添加签名文件)用来指定加载IDA的库函数签名文件.这个是IDA的非常有用的一个功能.逆向分析就像玩填字游戏,从提示线索出发,补完整个词句.而逆向分析的线索是什么呢?库函数就是其中之一.无论是MFC还是Delphi编写的程序,都要用到大量的库函数,而这些库函数就是我们分析的线索之一.IDA的FLIRT库函数签名能够识别出大部分的库函数并在汇编窗口中标注其名称.分析用户函数时,根据这些库函数,我们可以大致确定用户函数的作用.
但是IDA的库函数签名也不是万能的,也会遗漏或者错误把用户函数识别成库函数.怎么办?
如果你分析的文件较大或者CPU较慢时,观察导航器(查看->工具栏->导航器->导航器)就会发现IDA扫描分析会给指令,正则函数和库函数"染色",而库函数往往是在连续区域.我的经验是,如果IDA把这个连续区域外的函数标注成库函数,很大可能就是误识别,而把此连续区域内的函数"染"成用户函数,有可能就是遗漏,未识别.
(3)创建MAP文件(文件->创建文件->创建MAP文件),能够导出库函数名,用户函数(自己命名的),字符串名等(但是不能导出添加的注释信息.哪位知道如何导出IDA的注释,告诉俺一下:-).导出map文件的目的主要是用于ollydbg,因为od自身不具有识别库函数的功能,所以在用od调试delphi程序的时候,往往误入歧途跟进库函数里面,浪费时间.要在od中导入map,需要一个插件LoadMap,它可以很方便的导入map.导入之后,库函数都会标注上名称,调试起来就容易些了.
另外还有个OD的插件GODUP(IDA签名载入程序),也可以载入IDA的签名文件来识别库函数,也不错.
[2] IDA的一些常用的快捷键.
C 如果你发现一段数据没有被IDA识别成代码,那么可以手动转换.
G 跳转到地址,就是od的Ctrl+G.
Ctrl+Enter/Esc 就是od的+/-.
N 重命名.
U 如果一段数据被IDA错误识别成代码,用U可以撤销转换.
X 显示交叉参考. 对函数,可查看该函数的所有调用者;对局部变量,可查看函数里涉及到该局部变量的所有指令.
Y 在函数名上点Y,用来设置函数类型.
: 添加注释.
; 可重复注释,在逆向过程中不断添加注释和重命名分析过的函数很重要,好记性不如烂笔头嘛.
Shift+/ 在IDA中选中需要计算的数字,Shift+/即可调用计算器计算,比较方便.不用老去Win+R->calc了.
2. 编译器优化
====================================
编译器优化体现在很多方面,下面举例说明:
看看下列指令(这是从rc4的密钥初始化中截取的片段):
.text:0041E208 and edx, 800000FFh
.text:0041E20E jns short loc_41E218
.text:0041E210 dec edx
.text:0041E211 or edx, 0FFFFFF00h
.text:0041E217 inc edx
.text:0041E218
.text:0041E20E jns short loc_41E218
.text:0041E210 dec edx
.text:0041E211 or edx, 0FFFFFF00h
.text:0041E217 inc edx
.text:0041E218
其实这是edx%256,如果求余运算的求余数是2^n,如16,256等,就会优化成上述形式,因为用除法指令进行求余运算需要的时钟周期较多,执行效率不高,所以编译器尽量避免使用除法指令,就像上面所示.
再看另一个片段:
.text:0040100D mov ecx, eax ; eax是被除数
.text: 0040100F mov eax, 55555556h
.text:00401014 imul ecx ;
.text:00401016 mov eax, edx ;\注意imul是有符号数乘法,这相当于edx>>31
.text:00401018 shr eax, 1Fh ;/,这样eax存放的是符号位
.text:0040101B add edx, eax ;加上符号位, 对负数相当于向下舍入为一个较小数
.text: 0040100F mov eax, 55555556h
.text:00401014 imul ecx ;
.text:00401016 mov eax, edx ;\注意imul是有符号数乘法,这相当于edx>>31
.text:00401018 shr eax, 1Fh ;/,这样eax存放的是符号位
.text:0040101B add edx, eax ;加上符号位, 对负数相当于向下舍入为一个较小数
这段实际上是用乘法来模拟除法运算,翻译上面的汇编代码可以得到(var*0x55555556)>>32,即var*0.333333,所以模拟的除法运算是var/3.
Viusal C++, Borland Delphi程序的分析
======================================
[1] Viusal C++程序的分析
先将编译器设定为Visual C++,载入签名如vc32rtf,vc32mfc等.待IDA扫描分析完毕就可以开始工作了.
逆向前,先要说下函数有4种调用约定,即stdcall,cdecl,fastcall,pascal.它们之间的区别体现在参数入栈顺序和清理入栈参数的方式上.
stdcall的参数入栈顺序是从右到左,且在函数返回前清理入栈参数,在反汇编代码上体现为retn xx,比如压入2个寄存器作为参数,函数返回时就是retn 8.采用stdcall约定的有WINAPI,以及CALLBACK回调函数等.
cdecl的参数入栈顺序也是从右到左,但是在函数返回后清理入栈参数,在反汇编代码上体现为add esp, xx,比如压入3个寄存器作为参数,返回时就是add esp, 0Ch.采用cdecl约定的有c标准函数库等.
可能有人要问stdcall和cdecl貌似没什么区别嘛,这样做不是多此一举吗?呵呵,想想最常用的printf函数族,发现什么了?它的入栈参数个数是不固定的,也就是说在编译期才能确定入栈参数,所以用stdcall是无法实现这类不固定参数的函数,只能用cdecl.
pascal的参数入栈顺序与上面二者相反,是从左到右.在函数返回前清理入栈参数,这与stdcall一致.
fastcall的特点就是采用寄存器来传递部分函数参数,但具体细节依赖于编译器.如Visual C++编译器的fastcall约定是前两个参数依次用ecx,edx,第3个开始push入栈.
归纳如下:
调用约定 | 入栈参数清理 | 参数入栈顺序 |
cdecl | 调用者处理 | 右->左 |
stdcall | 函数自己处理 | 右->左 |
pascal | 函数自己处理 | 左->右 |
fastcall | 函数自己处理 | 依赖于编译器 |
直接调用Windows SDK写的C代码编译后是比较好分析的,只要熟悉Windows API基本就能找到和分析自己感兴趣的部分.比较棘手的是MFC一类C++代码的逆向问题.因为出现call dword ptr [esi+XXh]一类的虚函数调用,不能一路很顺利的跟下去.当然,你可以从调用点回溯到类实例创建的地方,从而知道调用的是什么函数.不过这样比较麻烦,投机取巧的办法,是用od到虚函数调用的地方前下断,然后由this指针得到虚函数表地址(this指针指向的类实例存储结构的第一项就是虚函数表地址),偏移XXh得到虚函数地址.
[2] Borland Delphi程序的分析
先把编译器设定为Delphi,载入签名d5vcl等.待IDA扫描分析完毕.
Delphi的函数调用是fastcall.Borland Delphi的fastcall约定是前3个参数依次用eax,edx,ecx传递,第4个开始push入栈.如果是虚函数,第一个参数eax就是this指针. 形象点就是function(eax, edx, ecx, push...).
举个例子:
code:004531CC lea eax, [ebp+var_30]
code: 004531CF push eax ; 第4个参数
code:004531D0 lea ecx, [ebp+var_38] ; 第3个参数
code:004531D3 mov edx, esi ; 第 2个参数
code:004531D5 mov eax, ebx ; 第1个参数,this指针
code:004531D7 mov edi, [eax] ; edi <- vmt ptr
code:004531D9 call dword ptr [edi+0Ch] ; 虚函数调用
code: 004531CF push eax ; 第4个参数
code:004531D0 lea ecx, [ebp+var_38] ; 第3个参数
code:004531D3 mov edx, esi ; 第 2个参数
code:004531D5 mov eax, ebx ; 第1个参数,this指针
code:004531D7 mov edi, [eax] ; edi <- vmt ptr
code:004531D9 call dword ptr [edi+0Ch] ; 虚函数调用
先前说过库函数是我们分析一个用户函数的重要线索,因为Delphi对Windows API做了封装,所以在Delphi程序里鲜有直接调用Windows API,基本是对库的调用,所以在分析的时候需要常翻Delphi Help.
另外,Delphi的字符串处理方式和C库有很大不同,没有熟知的str*函数族.推荐阅读看雪上firstrose整理的"Delphi的内部字符串处理函数/过程不完全列表"一文.
算法识别技巧
===================================
这里指的识别比较窄,就是一些通用加解密算法的识别.
算法识别当然依靠算法的特征.其中最明显的特征莫过于通用算法使用的一些初始化数据了.
比如下面这段代码截取自Blowfish的初始化函数:
code:004513A0 mov eax, offset S_Box_blowfish
code:004513A5 mov ecx, 1000h
code:004513AA call @Move
code:004513AF lea edx, [esi+103Ch]
code:004513B5 mov eax, offset P_Box_blowfish
code:004513A5 mov ecx, 1000h
code:004513AA call @Move
code:004513AF lea edx, [esi+103Ch]
code:004513B5 mov eax, offset P_Box_blowfish
我们跳转到S_Box_blowfish处,可以看到如下的初始化数据(BTW:其实这是pi的16进制表示)
data:0048D308 P_Box_blowfish dd 243F6A88h, 85A308D3h, 13198A2Eh, 3707344h, 0A4093822h, 299F31D0h, 82EFA98h, 0EC4E6C89h
data:0048D308 dd 452821E6h, 38D01377h, 0BE5466CFh, 34E90C6Ch, 0C0AC29B7h, 0C97C50DDh, 3F84D5B5h, 0B5470917h
data:0048D308 dd 9216D5D9h, 8979FB1Bh
data:0048D350 S_Box_blowfish dd 0D1310BA6h, 98DFB5ACh, 2FFD72DBh, 0D01ADFB7h, 0B8E1AFEDh, 6A267E96h, 0BA7C9045h, 0F12C7F99h
data:0048D308 dd 452821E6h, 38D01377h, 0BE5466CFh, 34E90C6Ch, 0C0AC29B7h, 0C97C50DDh, 3F84D5B5h, 0B5470917h
data:0048D308 dd 9216D5D9h, 8979FB1Bh
data:0048D350 S_Box_blowfish dd 0D1310BA6h, 98DFB5ACh, 2FFD72DBh, 0D01ADFB7h, 0B8E1AFEDh, 6A267E96h, 0BA7C9045h, 0F12C7F99h
假设此时你还不知道这个算法是什么,我的做法是把其中一段初始化数据,比如S盒的开始这段0D1310BA6h, 98DFB5ACh, 2FFD72DBh, 0D01ADFB7h提交到http://www.google.com/codesearch去查询.呵,看到什么了?google返回了blowfish的代码.现在你可以初步确定这个算法是Blowfish了.
有时候仅靠算法的初始化数据是不够,因为在google codesearch命中的结果太多了.比如:
code:0047EEB7 mov ecx, 9E3779B9h ; Magic Number
code:0047EEBC mov esi, ecx
code:0047EEBE mov edx, ecx
code:0047EEC0 mov [esp+18h+delta1], ecx
code:0047EEC4 mov [esp+18h+delta2], ecx
code:0047EEC8 mov [esp+18h+delta3], ecx
code:0047EECC mov [esp+18h+delta4], ecx
code:0047EED0 mov [esp+18h+delta5], ecx
code:0047EEBC mov esi, ecx
code:0047EEBE mov edx, ecx
code:0047EEC0 mov [esp+18h+delta1], ecx
code:0047EEC4 mov [esp+18h+delta2], ecx
code:0047EEC8 mov [esp+18h+delta3], ecx
code:0047EECC mov [esp+18h+delta4], ecx
code:0047EED0 mov [esp+18h+delta5], ecx
这里仅有一个初始化数据9E3779B9,提交到google codesearch命中了600个结果.这时就结合算法的特征了,比如这里将9E3779B9赋值给esi,edx,delta[1~5]共7个变量.我们在google返回的gnubg-0.14.3/lib/isaac.c里面找到了这样的特征:
70: r=ctx->randrsl;
a=b=c=d=e=f=g=h=0x9e3779b9; /* the golden ratio */
alpha.gnu.org/gnu/gnubg/gnubg-0.14.3.tar.gz - GPL - C
a=b=c=d=e=f=g=h=0x9e3779b9; /* the golden ratio */
alpha.gnu.org/gnu/gnubg/gnubg-0.14.3.tar.gz - GPL - C
呵呵,原来这是Isaac伪随机数算法.
这里只是为了说明的方便,所以省略了很多,我当时判断这个算法的时候,将整个Issac_Rand_Initial函数和Issac的c代码粗略的比对了一遍后,在od中断下Issac_Rand_Initial,将输入的种子扔到c代码中编译测试,与od的结果一致,确定为Issac伪随机数算法.
=======[EOF]=======
2 条评论:
真是经典啊 ,学习ing...
coach factory outlet
oakley vault
polo ralph lauren
nike air max 90
nike air force 1
oakley sunglasses
jordans for sale
lebron 12
ray ban sunglasses
true religion jeans
ed hardy outlet
adidas shoes
polo outlet
timberland outlet
toms wedges
coach outlet store online
coach outlet store online clearances
designer handbags
ghd flat iron
air huarache
oakley canada
kate spade outlet
cheap jordan shoes
true religion outlet
nike running shoes for women
toms shoes
oakley sunglasses
oakley sunglasses
gucci handbags
celine bags
vans sneakers
chenyingying2016718
发表评论