IDA 使用初体验
IDA 使用报告
IDA简介
IDA是 交互式反编译专业版,International disassemble professional,采用递归向下反编译器,目的是尽可能呈现接近原代码的代码。IDA可以分析的文件格式也非常多 包括windows下的pe格式文件(.exe .dll .sys)linux 下的elf文件(.elf .so )mac系统的常见文件格式,以及一些不常见到系统的文件格式。
反汇编算法有线性扫描(linear sweep)和递归下降(recursive descent)两种主要的反汇编算法。
线性扫描:反汇编从第一个代码段的第一个字节开始,以线性模式扫描整个代码段,逐条反汇编每条指令,直到遍历全部字节。此方法对于长度固定的指令集(MIPS)反汇编会更加容易,因为可以方便的的定位到随后的指令。缺点就是无法正确的将嵌在代码中的数据分离出。
递归下降:根据一条指令是否被另一条指令引用来决定是否对其进行反汇编。
IDA Pro就属于递归下降反汇编器。
IDA界面
打开想要逆向的可执行文件,会显示一个Load a new file的界面。
这里可以选择
(1).程序的类型;
(2).处理器的类型;
(3).加载的段地址和偏移量;
(4).是否允许分析;
(5.)一些加载选项;
(6).内核和处理器的一些选项;
(7).windows系统dll所在的目录。
默认选择PE文件就可以,对于一些网络数据包或者其他格式的文件,可以使用二进制加载,自己进行解析。
工作界面
IDA View-A 反汇编窗口
初始是图形视图,有一个Graph overview窗口,可以在里面用鼠标拖动定位到程序的某个位置。也可以在IDA View-A中点右键选择Text view改成文字视图,这样能看到段和偏移量,快捷切换为空格键。
HexView-A 十六进制窗口
直接查看程序的二进制内容(以hex值表示)一般用于直接查找存在程序中的明文字符串和动态调试时的内容查看
Exports/Import导出/入表
Exports是导出表(这个程序中能让外面调用的函数),Imports是导入表(程序中调用到的外面的函数)
Functions_wiondow 函数表
在逆向分析中往往都是直接利用Function_windows 查找关键函数开始对整个程序进行分析
该窗口提供ctrl+F 的搜索功能 例如可以直接ctrl+f 定位到main函数
ida会自动识别函数的作用并命名,当碰到未知作用函数时会用sub_
开头的函数名来表示。
还有像字符串窗口(可以查看程序全局出现过的字符串),f5调出ida自动生成的伪c代码等等常见的功能。
静态调试
程序用die扫一遍,发现是32位,用32位ida打开。
找到main函数,可以寒暑表查找,或者在字符串表中找可读字符串来定位。
ida f5反汇编得到c源码,下面为部分关键源码:
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
for ( i = 0; i < 100; ++i )
{
if ( (unsigned int)i >= 0x64 )
j____report_rangecheckfailure();
Destination[i] = 0;
}
sub_41132F("please enter the flag:", v7); //读入函数sub_41132F
sub_411375("%20s", (char)Str);
v3 = j_strlen(Str);
v4 = (const char *)sub_4110BE(Str, v3, v14); //sub_4110BE加密函数
strncpy(Destination, v4, 0x28u);
v11 = j_strlen(Destination);
for ( j = 0; j < v11; ++j )
Destination[j] += j;
v5 = j_strlen(Destination);
if ( !strncmp(Destination, Str2, v5) )
sub_41132F("rigth flag!\n", v8);
else
sub_41132F("wrong flag!\n", v8);
return 0;
}
输入的字符串经过 sub_4110BE函数 进行加密,然后再通过一个 for 循环进行变换,然后与 str 进行比较
跳转定位,查看 Str2 的字符串,获得字符串e3nifIH9b_C@n@dH
:
跟进加密函数 sub_4110BE
得到如下:
从加密程序可以看出 aAbcdefghijklmn 这个变量是加密程序的关键变量,那我们接着跟进查看 aAbcdefghijklmn 这个变量
这是典型的base加密需要用到的字母表。再结合前面的加密代码由 3 个字符变成 4 个字符,还有移位啥的,不难猜出这是一个 base64 加密的加密函数。
for ( j = 0; j < v11; ++j )
Destination[j] += j;
然后根据for循环中的替换方式,写出解密脚本:
import base64
s = "e3nifIH9b_C@n@dH"
flag = ""
for i in range(0,len(s)):
flag += chr(ord(s[i]) - i)
print(base64.b64decode(flag))
动态调试
广州羊城杯,REVERSE的RE-BabySmc
SMC,即Self Modifying Code,动态代码加密技术,指通过修改代码或数据,阻止别人直接静态分析,然后在动态运行程序时对代码进行解密,达到程序正常运行的效果。
而计算机病毒通常也会采用SMC技术动态修改内存中的可执行代码来达到变形或对代码加密的目的,从而躲过杀毒软件的查杀或者迷惑反病毒工作者对代码进行分析。
通常来说,SMC使用汇编去写会比较好,因为它涉及更改机器码,但SMC也可以直接通过C、C++来实现。
程序是无壳64位。
扔入IDA64
中查看信息,查看main函数,f5反汇编之后发现是乱码:
这就是cms自解码导致的,自解码用动态调试就可以较为轻松的结题。在main函数Input Your Flag :
处加入断点,开始动态调试。
ida动态调试界面图:
一步一步往下走,输入flag值,然后进入一个跳转:
byte_7FF7BDFD1085
变量就是底下一段数据,然后call了sub_7FF7BDFD1E30
函数。
执行完这个函数后发现,代码已经成功自解码了,我们现在看到的就是正常的汇编代码。
通过自解码获得的汇编代码,找到密文部分,右键create function,就可以进行反汇编了。
反汇编后的代码
我们可以查看字符串表,查找我们输入的字符串aaaaaaaaaaaaaa
,对我们的的输入字符串加入一个读写断点,哪里有用到这个字符串就会进行跳转到那条语句。
m128i_i8
这个函数调用了我们输入的字符串,然后将我们的输入值所得到的输出值与密文进行比较。
>>2 >>4 <<6 &0x3 &0xf &0x3f v77 == 1 v77 == 2看上去基本就是base64的加密3 * 8
变4 * 6
的实现了。
现在来理一下
1.base64加密
2.修改了码表
3.加密完会有个异或
那么解密顺序就是
1.异或得出原来的数据
2.修改码表得到进行base64解密
getflag代码:
#include <stdio.h>
#include <string.h>
int main(void)
{
unsigned char key[] = "H>oQn6aqLr{DH6odhdm0dMe`MBo?lRglHtGPOdobDlknejmGI|ghDb<4";
int data[] =
{
0xE4, 0xC4, 0xE7, 0xC7, 0xE6, 0xC6, 0xE1, 0xC1, 0xE0, 0xC0,
0xE3, 0xC3, 0xE2, 0xC2, 0xED, 0xCD, 0xEC, 0xCC, 0xEF, 0xCF,
0xEE, 0xCE, 0xE9, 0xC9, 0xE8, 0xC8, 0xEB, 0xCB, 0xEA, 0xCA,
0xF5, 0xD5, 0xF4, 0xD4, 0xF7, 0xD7, 0xF6, 0xD6, 0xF1, 0xD1,
0xF0, 0xD0, 0xF3, 0xD3, 0xF2, 0xD2, 0xFD, 0xDD, 0xFC, 0xDC,
0xFF, 0xDF, 0x95, 0x9C, 0x9D, 0x92, 0x93, 0x90, 0x91, 0x96,
0x97, 0x94, 0x8A, 0x8E
};
int xr[] = {0xA6, 0xA3, 0xA9, 0xAC};
int i, j;
unsigned int v3;
int flag[100] = {0};
for(i = 0; i < 56; i++)
{
key[i] ^= xr[i % 4];
// printf("%d ",key[i]);
}
// printf("\n");
for(i = 0; i < 56; i++)
for(j = 0; j < 64; j++)
if(key[i] == data[j])
{
key[i] = j;
// printf("%d:%d ",i,j);
break;
}
for(i = 0, j = 0; i < 56; i += 4, j += 3)
{
v3 = key[i+3] + (key[i+2] << 6) + (key[i+1] << 12) + (key[i] << 18);
flag[j] = (v3 >> 16) & 0xFF;
flag[j+1] = (v3 >> 8) & 0xFF;
flag[j+2] = v3 & 0xFF;
}
for(i = 0; i < 100; i++)
printf("%c",flag[i]);
return 0;
}