IDA逆向技巧(持续更新)

读者如果发现错误,请指正,可以讨论,谢谢。

7.1 IDA与OD的联合使用

一边使用OD下断点,找到关键代码位置,然后使用IDA静态看流程,需要注意的是IDA的需要与OD基地址对齐,这样才方便在IDA中查找代码位置,具体方式如下:

Edit --->  Segment ----- Rebase  Program  : 设置基地址;

在WINdbg中基地址在“符号”中查看;在OD中基地址在Model中查看;

IDA中G + 地址,即可找对对应位置;

IDA的逆向分析水平就是一个逆向工程师的水平;

 

7.2 数据不小心转换成为函数,如何处理?

如果不小心使用P将数据识别成了函数,可以使用D,转换成数据,再用C转换成为代码;

毕竟IDA不存在撤销功能;

 

7.3 IDA如何创建结构体?

7.3.1 结构体的创建   

IDA中结构体成员一般出现在:类初始化的地方(XX.dll);

根据成员的多少,我们创建结构体的大小,注意虚表vt占4个字节;一般有三个地方有助于我们创建结构体:初始化函数成员列表、memcpy函数的使用、数据库字段;

在初始化函数中看见最后一个成员为V1[239],可以 使用“Reset Point   Type” 可以改为:v1-> 1036,这样就能看到成员在的偏移,就可以设置结构体大小为1036 + 4.。

创建了结构体后,structure窗口中可以N改写字段的名称;

7.3.2 结构体的使用

创建好结构体后,我们就可以使用Y来修改分析的数据类型;

7.3.3 结构体的导入和导出使用

有时我们需要在一个数据库中创建结构体,在另外的一个数据库中使用,具体方法:

在数据库A的localtype视图中选中需要导出的数据类型,右键 --- “export to header”,如果是多个结构体写到同一个文件,请选择追加方式(Append),导出到一个.h文件;

接下来是导入到另外一个数据库,具体方法:

Ctrl + F9 :”选择一个C文件头去解析”,选择需要导入的一个文件,这样我们就会可以在LocalType中查看我们自建的结构体;

7.3.4 创建结构体  例子

一般方法构建结构体:在structure窗口,Insert 一个结构,自己命名(如struct_4),在end处使用D添加结构体数据,在成员处D 切换占有的字节数(db、dw、dd),在成员处使用Y来改变识别类型(int、float、double、LPVOID、BYTE field_60[1024]、)
使用: 在相应的rdata处,使用Y改变当前位置的识别类型为struct_4;
例如:

创建结构体数组: 
00000000 struc4          struc ; (sizeof=0x28, mappedto_194)
00000000                                         ; XREF: .data:stru_A47A220/r
00000000 field_0         dw ?
00000002 field_2         db ?
00000003 field_3         db ?
00000004 field_4         dd ?
00000008 field_8         dd ?
0000000C field_C         db ?
0000000D field_D         db ?
0000000E field_E         db ?
0000000F field_F         db ?
00000010 field_10        dd ?
00000014 field_14        dd ?
00000018 field_18        dd ?
0000001C field_1C        dd ?
00000020 field_20        dd ?
00000024 field_24        dd ?
00000028 struc4          ends
例子: struc4 stru_A47A220[77]
数据区的 stru_A47A220    struc4 <0FC39h, 0, 0, 100h, 0, 0, 0, 0, 0, 1.0, 1.0, 0, 0, 1, 1>....

7.4 IDA如何创建虚表

虚表的创建方式与结构体很类似,关键是要找到虚表的位置,需要使用 OD来进行跟踪,虚表的地址一般存在于EAX中,对应找过去就能知道是那个库里面的函数集合;

虚表的获取: 都是先得到this 指针,然后得到虚表指针,再成员函数调用需要传递this指针到ecx,最后间接调用,具体: 

mov  ecx, dword ptr[ebp - 14h]  ; ecx 得到this指针

mov  edx, dword ptr [ecx]  ; 得到虚表地址,this指针指向的第一个4字节就是虚表地址; edx 就是虚表地址; 

mov esi,esp

mov ecx, dword ptr [ebp -14h]     ;成员函数调用,需要ecx保存this指针;

call dword ptr [edx + 4]      ;间接调用 虚函数;

 

 

 

可以直接写一个虚函数的调用例子,使用IDA来分析;

7.4.1  创建虚表 例子
建立虚表:
00000000 functionVT struc ; (sizeof=0x34, align=0x4, copyof_192)
00000000 sub_10001110    dd ?
00000004 sub_10002210    dd ?
00000008 sub_100010D0    dd ?
0000000C sub_10001000    dd ?
00000010 sub_10001090    dd ?....
functionVT ends
上面这个函数的虚表来自于rdata 中的虚函数列表:
.rdata:0A46F570 ??_7CJ2190_PID_Decoder@@6B@ dd offset sub_A411110
.rdata:0A46F570                                         ; DATA XREF: sub_A452B00+C
.rdata:0A46F574                 dd offset f_baseFuncton_sub_E282210 ;
.rdata:0A46F578                 dd offset sub_A4110D0
.rdata:0A46F57C                 dd offset sub_A411000
.rdata:0A46F580                 dd offset sub_A411090
…..

建立类:
00000000 basestructure struc ; (sizeof=0x51C, align=0x4, mappedto_193)
00000000 VT              dd ?                    ; offset
00000004 field_4         dd ?
00000008 field_8         dd ?
basestructure…ends
将:VT 识别为:functionVT*  就可以得到basestructure的虚表;
例如:
result =(*(unsigned __int8 *)(*(_DWORD *)a1 + 0x20))(a1);
将a1定义为 basestructure * 类型,就能识别成为:
result = a1->VT->sub_1000CAD0(a1);
优点:看到调用的函数,方便静态分析;

7.4.2  快捷添加虚表结构体的快捷的方式

我们知道编译后虚表存在数据区,找到虚表头就可以看到当前类中的所有虚函数列表;这样我们再LocalType中 创建一个结构体,XXXVT ,将所有的虚表名称off_XXX 都赋值到结构体中,添加上类型int ,确定,然后双击结构体名,此时,会提示是否导入结构体到数据库,YES,然后再structure 窗口中就可以看到定义的虚表结构体了,这样,就可以Y来识别虚表了;

这样通过C的方式导入结构体的方式比在structure窗口中逐个添加成员要快很多; 

 

  

7.5 IDA如何确定数据是数组类型?

Data区中被引用的地方一般是数组;

见:《如何提取DLL中的数据》

见:《如何访问DLL的函数》

7.6 IDA 如何使用UTF-8 显示中文字符

Alt + A 可以改变字符显示 格式,在DLL中所有的data区都是使用十六进制的数值显示的;

Alt + A : 设置---- “ASCII String style ”-----“set  default encoding”。

如果想要在Hex -rays的伪代码中显示出字符串,可以设置:“Edit”---plugins ---- Option----Analysis --- “Print only constant string literals”

 

7.7 IDA 的强大之处---- 交叉引用

X:一个地方引用另外一个地方;

体现:双击、X、subView-Cross ref

在IDA-View-A 模式下,Xrefs graph to :表示该函数被其他哪些函数调用的信息;Xrefs graph  from :函数调用其他函数的信息;

 

7.8 IDA 反编译失败1--- Decomplication failure

IDA经常会解析错误,我们要学会如何处理这些错误,才能得到伪代码;

可能原因1:函数约定的错误;

可能原因2:函数参数识别错误;

解决方法:分析函数的参数个数和调用约定,Y重新定义函数;

如果判断不出是哪种约定,可以D成数据,再用tab进行转成伪代码,可以看到反编译后成为了Jumpout(xxxxx),屏蔽了细节,这种方式,只是用来随便看看代码;

 

7.9 IDA 反编译失败2--- SP- analysis failed

使用IDA -- Options ---Disassembly ---Display disassenmly  Line Parts ---- Stack Pointer,勾上可以看到SP,正常情况下SP的值在函数头和尾都是0000 ,表示堆栈是平衡的;

如果函数识别错误,Alt + p: 可以指定函数的头尾;

 

7.10 IDA 反编译失败3--- Positive sp value has  been found

有些汇编指令无函数名,表示是虚函数,IDA很容易出错;

如: call  dword   ptr[eax + 4]

V解决:ALT + K,设置SP当前为0x4X,这里X表示的是Call 之前有几个push。这样做能识别代码,能分析,但是会引起堆栈不平衡。

7.11 IDA 自动优化: Debug 和Release版本的不同

常量、简单算法,在生成Release 版本的时候被优化; 相对而言, debug版本中有很多的无关逻辑的代码,例如:判断越界的函数等; 

7.12 IDA 可以识别当前文件的类型

C#: Mircosoft.net  assembly [peldw]

7.13 IDA 如何修改数据库

 方式一、找到需要修改的位置,Edit---Patch pragram ----Assemble ;加入汇编语言,例如:给函数的返回值直接修改为2,反回;

mov eax,2

retn

如果是stdCal 的话,需要使用retn 4

修改后,在函数头使用P,函数对齐后,当前函数直接返回 2,而原先的逻辑都被注释掉了; 

方式二、使用UE打开,直接找到需要修改的函数的字节码,修改字节码也是一样的,例如 0x90 ----nop;

再使用Edit--- patch  program ----Apply patches  to imput file ,OK 即可;

7.14 IDA Send Data 的识别

从Write File回溯,SendData一般被很多的地方调用;

7.15 IDA 取一个富含意义的名称,有助于理解。

7.16 IDA 跟踪技巧:注释+ 改名 :  雁过留声 很重要...

7.17 IDA  变量静态分析

7.17.1  X : type = W/O  时可以找到当前变量写的地方,可以直接看到关键逻辑,进而进行猜测; 

7.17.2  数组的静态分析

dword_1002FEBE8 = HeapAlloc(hHeap, 8u, 8*g_count);

表示:分配内存8*count,每个元素有8字节,分配的内存往往与数据库对应; 

*(_BYTE*)(dword_10031AC4 + 8*V7 + 1) = g_DataArry[V8].size

表示:元素8字节,V7是偏移元素个数,1表示元素内偏移,_BYTE 表示取出的是一个字节,赋值给size;

7.17.3  函数的静态分析

1、看懂函数流程后确定函数的意义;

2、如果调用dll中的函数,GetProAddress,函数名往往都是直接可以看到的; 

 

7.18  根据lib文件制作sig文件(参考IDA权威指南)

Sig文件能使得IDA识别更多的符号,便于分析,F5刷新;

1、使用IDA打开lib,可以看出lib里面有多少个obj文件;

2、使用link.exe 将lib中的obj文件导出来(这个link.exe 来自于哪里?)

命令:link -lib /extract:testCplusplusCollect.obj  testCplusplusCollect.lib
得到的是testCplusplusCollect.obj
没找到Link.exe , 
其实,这个obj文件在lib文件编译生成时会在debug目录生成(我是直接拷过来的哈);

听说VC里面的Lib.exe 也有这个功能。听说可以直接使用pcf.exe [lib文件名].lib ,会生成[lib文件名].pat 。
    没试过,读者可以试试;

3、使用pcf.exe 将obj文件制作成为pat文件。

从IDA安装目录中找到pcf.exe,拷贝到testCplusplusCollect.lib 同级目录下; 
命令:pcf testCplusplusCollect.lib
在同级目录得到了testCplusplusCollect.pat。

4、使用sigmake.exe将PAT文件转换成为sig文件。
IDA_65_Utils\flair65\bin\win\sigmake.exe
命令:sigmake.exe  testCplusplusCollect.pat  testCplusplusCollect.sig
在同级目录得到了testCplusplusCollect.sig。

5、在IDA中去加载sig文件
将testCplusplusCollect.sig 拷贝到IDA的sig目录,再使用FILE -> LoadFile àFILTER signature File;
 

7.19  IDA反编译失败:too big  Funtion

解决方法: MAX_FUNCSIZE 改大即可;

7.20 结构体:如果知道了结构体的C表示,IDA如何添加

在TypeLocal(shift + F1 )的界面,insert 跳出输入对话框,在里面复制结构体就行了。

双击添加后的类型,提示“是否加入当前的数据库”,选择可以,那么在structure试图中就能得到我们想要的类型,可以在反编译试图中使用Y设置此类型;

 

7.30 如果将全局的字符串,显示在代码中?

Edit ---plugins --- Hex-Rays decomplier -Option -
Print only  constant string literals ”把这个勾去掉就能,将全局的字符串给显示出来了。

7.31 打标签与修改名字

修改名字:N
打标签:JUMP/Mark postion 中“标记当前的位置”然后可以使用Ctrl + M 来快速跳转到标签的位置;

以前我一直这么做的: 分析到了哪个函数,就将函数名字N一下,改成YY开头,这样我就可以在FUnctionList中快速找到我分析过的函数;

7.32 如何解决IDA key黑名单检测

提示:“sorry, this database has been  created by a pirate version of IDA”

这是因为IDA的key 黑名单检测,解决方法如下:十六进制工具将ida.dll 或 ida64.dll的地址0x277560 ~ 0x277750 的数据改为0 即可通过检测;这部分是所有的黑名单Key的MD5值。


7.33 如何使得IDA将所有的数值都用十六进制表示;

插件: Edit  ---Plugins --- Hex-Rays Decompiler ---- options ---“Default   radix”  填上16即可;

7.34 如何在伪代码中将全局的dword_454670 改为字符串显示?

将数据A转换为c-typle 字符串;然后:在伪代码中F5 就能将dword_454670 转成:

 

7.35  F5 反编译后提示: “Out of memory”

有时候函数太大,会这样提示,打开Hex-ray的选项,提示我们可以在 HexXXX.cfg中修改max_funcszie.

好像只有7.0之后的IDA配置文件中有这个属性。

 

7.36  F5 反编译的代码分支太多,代码太长,如何做?

将分析过的分支缩进,方法: 右键--collapse item 就行,如果想看里面的内容,一样操作选uncollapse;

 

 

 

 

 

 

 

 

 

 

 

 


 

展开阅读全文

没有更多推荐了,返回首页