逆向: 熟悉IDA
Posted iishuu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了逆向: 熟悉IDA相关的知识,希望对你有一定的参考价值。
题目来源:
南邮CTF :: RE :: Hello,RE(应该是)
XDUCTF :: ??? :: ????????(不知道不知道不知道)
总而言之我会在百度网盘再上传一份: >>百度网盘, 提取码 3dcv <<
0> 总结
1> 查看.exe是32位还是64位的: notepad++打开,Ctrl-F找"PE", 若位PE……L则为32位,若为PE……d则为64位。
2> IDApro常用快捷键:
1> F5 : 嘿嘿嘿
2> R : 将看不懂的数据转化为可能看懂的数据
3> Y : 修改变量/函数类型
4> N : 修改变量/函数名
5> X : 查看变量/函数被引用情况
6> U : 将此处还原到初始状态
7> D : 在汇编语言界面,改变数据类型
8> TAB : 切换显示形式 ……(待续)
9> F2 : 设置断点
10> G : 动态调试界面,跳转到目标地址
3> 常规操作:
1> 修参数:名称 && 类型
2> 动态调试:断点 && 栈
1> 准备:
操作系统:windows //建议win7或以上
IDA //可以去"吾爱破解"论坛自行下载。
C语言编译器 或者 IDA的插件 "Lazy IDA" //由于当时没时间配置LazyIDA,所以一些步骤只好自己写。
notepad++ //官网传送门
2> 0.exe
1> 用Notepad++打开0.exe,发现"PE @#$%^&*( L ", 说明这个可执行文件是32位的。 //若为"PE[email protected]#$%^&*() d" ,则说明是64位的。
2> 拖到idaq(32位)里,一路ok
3> 选中_main函数,F5
4> 选中v5等的那些数字,按R,就可以把她们变为可以阅读的文字。
5> 显然这就是每一小节倒置后的flag,可以手动敲出来。
不过,由于是用strcmp函数比较输入数据和答案是否匹配,我们还可以尝试动态调试。
6> 在第二步的那个界面里(IDA VIEW-A),选中_strcmp, 摁X看看哪里调用了这个家伙。
7> 设置断点(F2), 效果如图:
8> 在上边的小条条里选中"Local Win32 debugger",按绿色的运行按钮开始动态调试。
9> 随便输一串啥东西,回车确定。发现不动弹了,这是正常的。
10> 在右下角的stack view里我们可以发现两个神奇的地址。
11> 复制,选中它左边的窗口(Hex View-1),按G跳转到地址,贴进去,OK.
12> 找到了之前丢进去的东西。
13> 那么第二个地址就应该是flag了,如法炮制,GET~
3> 1.exe
1> 先运行一下。
发现输入之后闪了一下就推出了,恼火。
2> 不行,恼火,Win+R开cmd,重新运行一次。
发现了可以用来定位的字符。
3> notepad打开,是32位的。
4> 拖进IDA(32位),发现没有main函数。
` //我起初以为哪个start函数就是主函数,然而盯着屏幕看了几分钟之后我发现我错了。 看不懂
5> 这时,最开始我们见到的那些文字就有用了。在view--open subviews-strings 里搜索。
6> 随便点一个好了(双击)
7> 摁X看引用,OK.
8> F5,发现sub_401020()就是main
9> 选中函数名,摁N重命名,改为"main”
10> 我们可以看到主函数干了很多骚操作,乱七八糟的不想看下去。
11> 不过,粗略一读,可以发现sub_401450就是printf,摁N重命名。
12> 在那个带有"flag字样的if语句里",我们可以发现,显示对一个名字叫Str的东西做了sub_4013C0,然后把它和flag格式的头和尾巴作比较。
看看sub_401340,就是exit.
于是我们大胆猜测,sub_4013C0就是scanf,而这个if语句就是在判断输入内容是否符合flag的格式。
` 别问我为什么能猜到,从初中到大学的所有数学证明题的做法不都是"我就是知道该这样做"吗?(啧啧啧)
13> 摁N改名,顿时清晰(了一些)。
14> 验证的语句是"strcmp",所以改名。
15> 小结
先让用户输入flag,判断是否符合格式,若不符合则退出程序。
然后把flag去掉格式,复制到DST //DST即flag{内的内容},暂且重命名为"flag_body"
再对DST进行sub_4011F0操作(有些复杂,暂不处理)
之后把DST和v4进行一些奇怪的操作
最后判断v4是否符合a23g……那个字符串
如果是,就是。否则退出。
16> 综上所述,再次大胆猜想,
sub_4011F0和sub_401270是加密算法,
而a23……是加密后的flag,
于是,接下来的任务就是看懂加密算法,并写出解密算法。
17> sub_4011F0
可以看到,v2本应是int,却显示为char。(虽说根本没啥卵用就是了)
而根本不需要返回值,所以我们需要无视和result有关的语句。(有插件,之后有机会补上)
1> 函数,判断了一下函数是否满足一个和32有关的条件,然后再把传入的flag和另一个数组亦或。
可以发现,flag的长度为32。
2> 双击查看dword4040,很显然这是一个每个元素大小为4B的数组。
3> 右键,array,将这个家伙转化为数组,32个元素。
4> 显然,这就是与flag异或的数组。
XOR的逆运算还是XOR,所以只需要把这玩意转化一下即可。 //转化方法在后面
18> sub_401270
1> 改变量名(len, i, input, output)
2> 改变量类型(output是个char*)
3> 可以发现,加密原理是根据"a1"这个东西将输入的顺序调换,从而转化为密文。所以,a1即所谓的"key"
4> 返回main,找到所谓"a1",,双击。
5> 转化为int数组,先如下图操作,然后右键-array,32个元素。
1> 按d转化数据类型为dd(即整型)
2> 右键-array -32个元素
//若误操作,你会发现没有撤销功能,然而可以按U将该单元重置。
6> 先在我们需要转化的两组key就都在这里了。
` 那么……
7> 然后就可以进行解密操作了。
19> sub_401270的解密算法
注释:
0> 伪代码
1> key2即映射规则,即dword_4040C0
2> output和input分别是这条函数的输出数据和输入数据。
加密算法:
for(int i=0; i<32; i++) output[key2[i]] = input[i];
解密算法:
for(int i=0; i<32; i++) output[i] = input[key2[i]]
20> sub_4011F0的解密算法
注释:
0> 伪代码
1> key1即dword_404040
2> " ^ "是异或(XOR),逆运算还是异或。
加密算法:
for(int i=0; i<len; i++) flag[i] ^= key1[i];
解密算法:(同上)
for(int i=0; i<len; i++) flag[i] ^= key1[i];
21> 如何将形如"53h"的十六进制数丢进C++ lazy IDA 或者 自己写代码。
注释:
1> C++只能识别形如"0x53"的十六进制数而不能识别形如"53h"的十六进制数
2> 写一段处理字符串的函数即可
3> 因为懒得写,所以手工处理了一部分。只需把"dd"去掉;把换行符换为" ,"即可。
3.5> 因为懒得整合,所以把处理key的函数和主函数分开写了。需要手工复制粘贴一下,不过好在代码行数少,DEBUG比较方便。
4> 代码如下,在程序所在目录下新建in.txt,把东西丢进去,略作处理,保存,运行程序即可。
手工处理之后的key1:
53h, 45h, 5Ch, 1Eh, 50h, 13h, 2Fh, 78h, 4, 53h, 58h, 4Ah, 43h, 1, 41h, 2Ah, 8, 40h, 67h, 2Fh, 0Ch, 4Ah, 12h, 2Eh, 41h, 6Ch, 5, 54h, 40h, 12h, 5Bh, 4Fh
手工处理之后的key2:
4, 0Fh, 0Bh, 1Eh, 0Eh, 14h, 1Fh, 9, 17h, 2, 19h, 1Ch, 12h, 10h, 0, 8, 11h, 1, 15h, 3, 0Ah, 1Dh, 0Ch, 16h, 18h, 0Dh, 1Bh, 5, 7, 6, 13h, 1Ah
代码:
#include<cstdio> #include<cstdlib> int main() { freopen("out2.txt", "w", stdout); char ans[41] = "23gjf13au98hk3a1090zp8qjs41h39jp"; char flag[41]; int key2[41] = {0x4, 0x0F, 0x0B, 0x1E, 0x0E, 0x14, 0x1F, 0x9, 0x17, 0x2, 0x19, 0x1C, 0x12, 0x10, 0x0, 0x8, 0x11, 0x1, 0x15, 0x3, 0x0A, 0x1D, 0x0C, 0x16, 0x18, 0x0D, 0x1B, 0x5, 0x7, 0x6, 0x13, 0x1A}; int key1[41] = {0x53, 0x45, 0x5C, 0x1E, 0x50, 0x13, 0x2F, 0x78, 0x4, 0x53, 0x58, 0x4A, 0x43, 0x1, 0x41, 0x2A, 0x8, 0x40, 0x67, 0x2F, 0x0C, 0x4A, 0x12, 0x2E, 0x41, 0x6C, 0x5, 0x54, 0x40, 0x12, 0x5B, 0x4F}; for(int i=0; i<32; i++) { flag[i] = ans[key2[i]]; } for(int i=0; i<32; i++) { flag[i] ^= key1[i]; } printf("%s ", flag); fclose(stdout); system("out2.txt"); return 0; }
22> 整合,运算:
注释:
1> flag:原程序的输入数据,同时也是本程序的输出数据
2> ans:本程序的输入数据,同时也是原程序的输出数据
3> 运行即可,自动弹出盛放有flag的txt
代码:
#include<cstdio> #include<cstdlib> using namespace std; int main() { freopen("out2.txt", "w", stdout); char ans[41] = "23gjf13au98hk3a1090zp8qjs41h39jp"; char flag[41]; int key2[41] = {0x4, 0x0F, 0x0B, 0x1E, 0x0E, 0x14, 0x1F, 0x9, 0x17, 0x2, 0x19, 0x1C, 0x12, 0x10, 0x0, 0x8, 0x11, 0x1, 0x15, 0x3, 0x0A, 0x1D, 0x0C, 0x16, 0x18, 0x0D, 0x1B, 0x5, 0x7, 0x6, 0x13, 0x1A}; int key1[41] = {0x53, 0x45, 0x5C, 0x1E, 0x50, 0x13, 0x2F, 0x78, 0x4, 0x53, 0x58, 0x4A, 0x43, 0x1, 0x41, 0x2A, 0x8, 0x40, 0x67, 0x2F, 0x0C, 0x4A, 0x12, 0x2E, 0x41, 0x6C, 0x5, 0x54, 0x40, 0x12, 0x5B, 0x4F}; for(int i=0; i<32; i++) { flag[i] = ans[key2[i]]; } for(int i=0; i<32; i++) { flag[i] ^= key1[i]; } flag[32] = ‘