IDA实例教程详解.docx

上传人:b****1 文档编号:23288695 上传时间:2023-05-16 格式:DOCX 页数:17 大小:164.60KB
下载 相关 举报
IDA实例教程详解.docx_第1页
第1页 / 共17页
IDA实例教程详解.docx_第2页
第2页 / 共17页
IDA实例教程详解.docx_第3页
第3页 / 共17页
IDA实例教程详解.docx_第4页
第4页 / 共17页
IDA实例教程详解.docx_第5页
第5页 / 共17页
点击查看更多>>
下载资源
资源描述

IDA实例教程详解.docx

《IDA实例教程详解.docx》由会员分享,可在线阅读,更多相关《IDA实例教程详解.docx(17页珍藏版)》请在冰豆网上搜索。

IDA实例教程详解.docx

IDA实例教程详解

IDA实例教程详解

作者:

笨笨雄(转载)

1软件环境

静态分析有很多好处,例如加壳的程序(尽管对于高手来说这并不会耗费太多时间),我们不需要寻找OEP,也不需要解除自校验,只要修复IAT,DUMP下来就可以动手分析了。

假如你需要修改程序,可以使用内存补丁技术。

动态与静态,调试器与反汇编器结合可以简化分析任务,帮助我们理解代码。

因此掌握一种反汇编器是非常必要的。

IDA可以说是这方面的首选工具,它为我们提供了丰富的功能,以帮助我们进行逆向分析。

这从IDA复杂的工作界面便可以知道。

种类繁多的工具栏

在分辨率不高的情况,这些工具栏与反汇编窗口挤在小屏幕里,看起来不爽。

我一般把它关闭(查看=>工具栏=>主工具栏)以获得更好的视觉效果。

当我们需要这些功能的时候,直接使用快捷键就可以了。

下面是常用快捷键的清单:

快捷键

功能

注释

C

转换为代码

一般在IDA无法识别代码时使用这两个功能整理代码

D

转换为数据

A

转换为字符

N

为标签重命名

方便记忆,避免重复分析。

添加注释

R

把立即值转换为字符

便于分析立即值

H

把立即值转换为10进制

Q

把立即值转换为16进制

B

把立即值转换为2进制

G

跳转到指定地址

X

交叉参考

便于查找API或变量的引用

SHIFT+/

计算器

ALT+ENTER

新建窗口并跳转到选中地址

这四个功能都是方便在不同函数之间分析(尤其是多层次的调用)。

具体使用看个人喜好

ALT+F3

关闭当前分析窗口

ESC

返回前一个保存位置

CTRL+ENTER

返回后一个保存位置

在工具栏下面的便是工作窗口。

主要的窗口分页有“IDAView-A”、“Name”、“Strings”、“Exports”和“Imports”。

对于后面3项相信大家都不会陌生了,它们分别是字符参考,输出函数参考和输入函数参考。

Name是命名窗口,在那里可以看到我们命名的函数或者变量。

这四个窗口都支持索引功能,可以通过双击来快速切换到分析窗口中的相关内容,使用起来十分方便。

简单输入几个字符即可定位目标

IDAView-A是分析窗口,支持两种显示模式,除了常见的反汇编模式之后,还提供图形视图以及其他有趣的功能。

IDA的反汇编窗口

一般我们在分析的时候,并不关心程序的机械码,所以IDA为我们自动隐藏了这些信息。

如果你有需要,可以通过以下步骤来设置:

选项=>常规=>反汇编=>显示反汇编行部分=>机械码字节数=>修改为你允许显示的大小

现在让我们以论坛脱壳版块置顶帖的那个经典为例,看看图形视图的表现。

首先我们到以下连接下载:

你能通过图形视图及其缩略图快速找到壳的出口吗

如图所示,标签40EA0E便是壳的出口代码的地址。

在OD中直接跳到该地址,下断点,然后运行到该处,再单步便能看到OEP了。

假如希望通过跳转法找OEP,相信图形视图比你在OD一个一个跳转跟随,要快得多。

再来看看这个壳的另类脱法。

直接运行该程序,DUMP下来,再使用IMPORTREC的IATAutoSearch功能修复输入表。

用IDA打开修复了输入表的DUMP文件。

在IMPORT窗口随便选一个API,随便通过交叉参考跳转到一个函数的代码。

此处为文件输入表的位置

我选了RegQueryValueExA,通过交叉参考,来到Sub_402488处的函数代码。

用鼠标拖动缩略图中的虚线框到上方,便能看到该CALL的头部了。

然后按下图指示操作:

在函数标记上点击鼠标右键

处于最上层的函数,便是OEP了,使用PE工具修改文件入口为10CC。

现在函数可以正常工作了。

这个方法的原理是通常我们写程序都有如下流程:

Mainproc

","打开IAT符号文件");

CustEa=AskAddr(0,"目标IAT地址");

filehandle=fopen(fileName,"r");

for(ea=CustEa;zcount<2;ea=ea+4){

if(Dword(ea)!

=0){

Sbuffer=readstr(filehandle);

if(strlen(Sbuffer)<2){

00404C2C00404C2C00404C00404C404C6A00404C00404C00404C00404C00404C00404C00404C00404C00404C4A00404C00404C4F00404C4F00404C00404C00404C404C

4560F

0F

0F

0A0C8F5A5A83C

0A0C8F5A5A83C0a0a

0045639F004563A004563A004563A004563A004563C004563C004563C4563C4563C004563C004563F004563F4563F004563F004563F004563F004563F4563F0045640A0045640A45640A0045640Aseg005:

0045640A;Resendthelasttransmission

seg005:

0045640A

seg005:

0045640D;seg005:

0045640D

修复之后的代码

除了“subeax,[esp-8+arg_4]”(实际上是subeax,[esp])看起来有点怪之后,一切正常。

作为一个壳,在解决了花指令之后,剩下的问题便只有反调试代码和解密(解压缩)代码了。

例如上面列出的代码是通过时间校验检查调试器,一旦检查到,便使用特权级指令,让程序发生异常,无法继续运行下去。

当然,我们在静态的环境下,反调试技巧对于我们来说,毫无意义。

尽管如此,我们仍然需要知道程序会在什么时候运行到什么地方,最常见的利用系统的机制莫过于SEH了,现在来看看下面代码:

seg005:

00456A9Bcall$+5

seg005:

00456AA0adddwordptr[esp+0],136Fh

seg005:

00456AA7pushlargedwordptrfs:

0

seg005:

00456AAEmovlargefs:

0,esp

设置SEH的代码

“call$+5”指令后堆栈里的内容便是它的下一条指令在内存中的地址。

这是病毒常用的重定位技巧。

shift+/输入0x00456AA0+0x136F便能计算出异常处理函数的地址(457E0F)了。

seg005:

0045745Cxoreax,eax

seg005:

0045745Emovzxeax,byteptr[eax]

产生异常的代码

现在我们应该跳到457E0F继续分析。

我想你已经了解如何在静态环境下跟踪程序的流程,现在就让我们跟着程序的流程把解密相关的代码找出来。

seg005:

00459191pushecx

seg005:

00459192xorecx,ecx

seg005:

00459194call$+5

seg005:

00459199popedi

seg005:

0045919Aaddedi,9C4h

seg005:

004591A0popedx

seg005:

004591A1addedx,15h

seg005:

004591A4loc_4591A4:

;CODEXREF:

sub_459149+6Bj

seg005:

004591A4movzxeax,byteptr[ecx+edi]

seg005:

004591A8xoreax,edx

seg005:

004591AAmov[ecx+edi],al

seg005:

004591ADincecx

seg005:

004591AEcmpecx,93h

seg005:

004591B4jbshortloc_4591A4

解密代码

容易看出这就是解密代码,在循环之中,且有修改内存的指令。

至于解密的KEY,其实就是00459191处ECX的值+15h。

我希望你还记得到达这里之前曾经看过下面代码:

seg005:

004587B6moveax,[esp+0Ch]

seg005:

004587BAxorecx,ecx

seg005:

004587BCxorecx,[eax+4]

seg005:

004587BFxorecx,[eax+8]

seg005:

004587C2xorecx,[eax+0Ch]

seg005:

004587C5xorecx,[eax+10h]

这一段是检查硬件断点的代码,假如没有设置硬件断点,那么ECX的结果应该是0。

假如你不能理解为什么,我建议你看看SEH以及关于反硬件断点的一些文章。

在知道解密代码的所有关键要素之后,就可以开始动手写脚本了。

 

#include""

staticmain(){

autoStartAddr,cKey,Cbuffer,Counter;

StartAddr=0x00459199+0x9c4;

cKey=0x15;

for(Counter=0;Counter<0x93;Counter++){

Cbuffer=Byte(StartAddr)^cKey;

00461F00461F00461F

0046200F0046204C

0046206A0046206F0046207A

0046206A

00461F9C

0046206F

lse语句。

if(CF==1){

CF=0;

while(ECX!

=0){

PatchByte(DeCodeAddr,Byte(EDX));

EDX++;

DeCodeAddr++;

ECX--;

}

}

else{

while(Counter!

=1){

PatchDword(DeCodeAddr,Dword(EDX));

EDX=EDX+4;

DeCodeAddr=DeCodeAddr+4;

if(ECX<=4){

ECX=ECX-4;

break;

}

ECX=ECX-4;

}

DeCodeAddr=DeCodeAddr+ECX;

}

//反汇编代码的循环入口(4528DE)与我们转换的循环入口不同(4528E9)

//跟开始的时候一样,入口之前的代码放到循环外面。

IsNotZero=EBX&0x7FFFFFFF;

if(IsNotZero==0){

CF=1;

EBX=Dword(MyAddr);

MyAddr=MyAddr+4;

}

HigtBitflat=EBX&0x;

EBX=EBX+EBX;

EBX=EBX+CF;

CF=0;

}

}

至此,我们成功将004528D0到004529A1处的代码转换成C代码。

在完成如此复杂的代码还原之后,004529A6到004529D8处的反汇编代码只是小菜一碟。

里面的代码也很好理解,将符合E801和E901的机械码解密。

位移指令可以通过借用程序中的一个闲置的Dword,使用IDC提供的Pactch系列指令来模拟,详见。

在完成最后的解密代码后,便是IAT的修复了。

现在看看下面代码:

004529DAleaedi,[esi+50000h]

004529E0loc_4529E0:

004529E0moveax,[edi]

004529E2oreax,eax

004529E4jzshortloc_452A22

004529E4

004529E6movebx,[edi+4]

004529E9leaeax,[eax+esi+549B0h]

004529F0addebx,esi

004529F2pusheax

004529F3addedi,8

004529F6calldwordptr[esi+54A3Ch]

004529FCxchgeax,ebp

004529FDloc_4529FD:

004529FDmoval,[edi]

004529FFincedi

00452A00oral,al

00452A02jzshortloc_4529E0

00452A02

00452A04movecx,edi

00452A06pushedi

00452A07deceax

00452A08repnescasb

00452A0Apushebp

00452A0Bcalldwordptr[esi+54A40h]

00452A11oreax,eax

00452A13jzshortloc_452A1C

00452A13

00452A15mov[ebx],eax

00452A17addebx,4

00452A1Ajmpshortloc_4529FD

在分析该处代码之前,显然应该先把ESI的值计算出来。

鼠标点击ESI,以高亮显示该寄存器,向上滚动反汇编窗口,发现从004529A6popesi处开始,ESI便没有被修改过,而该处对应于:

seg005:

0046206Fmovesi,offsetunk_447000

seg005:

00462074leaedi,[esi-46000h]

seg005:

0046207Apushedi

可见ESI=0x401000,容易计算出004529F6和00452A0B处CALL的地址分别为455A3Ch和455A40h。

跳转到该地址:

 

显然,这里便是壳填充IAT的地方了。

那么004529DAleaedi,[esi+50000h]中,EDI便是保存API名字的数据表。

做脱壳机的任务就留给读者作课后练习,正如前面介绍的那样,只需要API的名字为相关IAT地址重命名,便能分析了。

也就是说00452A0B处,调用GetProcAddress,跟踪它的参数lpProcName(00452A06pushedi),以及它的返回值(00452A15mov[ebx],eax),当然这里的跟踪,可以象刚才那样手动确认,也可以通过与调试器配合快速得出结果。

不难得出下面脚本:

#include""

staticmain(){

autoESI,EDI,EAX,EBX,Counter,cBuffer,BufLen,straa;

ESI=0x447000-0x46000;

EDI=ESI+0x50000;

Counter=MaxEA()-MinEA();

MakeUnknown(MinEA(),Counter,1);//将整个程序标记未分析

AnalyzeArea(MinEA(),MaxEA());//分析整个程序

Counter=0;

while(Counter!

=1){

EAX=Dword(EDI);

if(EAX==0)break;

EBX=Dword(EDI+4);

EBX=EBX+ESI;

EDI=EDI+8;

while(Counter!

=1){

EAX=Byte(EDI);

EDI++;

if(EAX==0)break;

cBuffer=GetString(EDI,-1,ASCSTR_C);

straa=cBuffer+"_";//IDA不允许重复命名,加上“_”避免重复

MakeNameEx(EBX,straa,SN_AUTO);

EBX=EBX+4;

EDI=EDI+strlen(cBuffer);

EDI++;

}

}

}

注意解密后,必须将整个程序标记为未分析,并重新分析,然后才能进行重命名。

程序的OEP

到此,静态脱壳完毕。

从这个例子也可以知道,对于掌握反汇编器的人来说,除非反调试机制与解密KEY关联,否则根本就没有强度可言。

然而,IDA博大精深,还有更多强大的功能,本文也只是抛砖引玉而已。

下面给出几个链接,方便大家更进一步学习:

IDA的官方网站:

看雪论坛9月翻译专题:

&threadid=31023

IDAPro的插件开发SDK:

&threadid=31441

IDA逆向工程入门:

&threadid=40765

IDA简易教程:

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 考试认证 > IT认证

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1