攻防世界逆向入门题之流浪者
继续开启全栈梦想之逆向之旅~
这题是攻防世界逆向入门题的流浪者
下载附件,照例扔入exeinfope中查看信息:
32位PE文件无壳,照例扔入ida32中查看伪代码:
是没有主函数的题型,那就运行程序提取有用信息:
信息提取完了,一个弹框,一个判断,根据字符串我们可以找到弹框所在函数,弹框是messagebox函数,而import中只有一个messagebox,双击跟踪即可:
查看该函数被谁调用,用IDA权威指南学到的新技巧,function call窗口:
找到比较函数,但是没有找到输入函数,继续查看被谁调用,继续function call窗口:
找到函数了。
按照流程走,我们要判断是和输入有关的生成型flag还是简单的存储型flag,答案是前者,而且是明文密文对照类型,那就开始代码分析:
v4 = (CWnd *)((char *)this + 100);
v1 = CWnd::GetDlgItem(this, 1002); //这些系统函数一开始吓到我了,虽然系统函数一直不是什么考点,但还是认为这里会有和输入相关的东西,后来下载了API的chm文档查了一下作用,的确,后面的GetBuffer应该就是获取用户输入了,但只是获取而已,相当于scanf,知道即可,还是没什么考点
CWnd::GetWindowTextA(v1, v4);
v2 = sub_401A30((char *)v8 + 100);
Str = CString::GetBuffer((CWnd *)((char *)v8 + 100), v2);
if ( !strlen(Str) )
return CWnd::MessageBoxA(v8, &byte_4035DC, 0, 0); //弹框函数,这里犯下第一个错误,IDA双击进去的数据都是db类型的,而我们一开始看到的弹框显示的是中文,所以我们要改类型为dd类型才可以,不然就显示不了中文,所以&byte_4035DC跟踪进去后要用热键D改为dd类型再转字符。
for ( i = 0; Str[i]; ++i ) //把输入的字符串逐个判断条件并根据不同条件修改,
{
if ( Str[i] > 57 || Str[i] < 48 )
{
if ( Str[i] > 122 || Str[i] < 97 )
{
if ( Str[i] > 90 || Str[i] < 65 )
sub_4017B0(); //有一个不是弹框范围内就返回失败
else
v5[i] = Str[i] - 29; //范围在65~90中,输出结果在36~61中
}
else
{
v5[i] = Str[i] - 87; //范围在97~122中,输出结果在10~35中
}
}
else
{
v5[i] = Str[i] - 48; //范围在48~57中,输出结果在0~9中
}
}
return sub_4017F0((int)v5); //把修改后的数组结果作为参数赋给后面密文对照函数。
int __cdecl sub_4017F0(int a1)
{
int result; // eax
char Str1[28]; // [esp+D8h] [ebp-24h] BYREF
int v3; // [esp+F4h] [ebp-8h]
int v4; // [esp+F8h] [ebp-4h]
v4 = 0;
v3 = 0;
while ( *(int *)(a1 + 4 * v4) < 62 && *(int *)(a1 + 4 * v4) >= 0 )
{
Str1[v4] = aAbcdefghiabcde[*(_DWORD *)(a1 + 4 * v4)]; //aAbcdefghiabcde双击跟踪是abcdefghiABCDEFGHIJKLMNjklmn0123456789opqrstuvwxyzOPQRSTUVWXYZ'的62位长度字符串,也就不难解释while判断条件的<62了,这里还犯下第二,第三个错误,第二个错误是一开始没看出来这是个数组,*(_DWORD *)(a1 + 4 * v4)是取索引而已,a1是数组头地址,现在要知道带方括号的[]基本都是数组取字符! 第三个错误是这里*(_DWORD *)(a1 + 4 * v4)型我是真的搞不懂,明明a1是int型,还是数组头地址,它前面的(_DWORD *)把它又变成了uint32型地址,先不说这多此一举,关键是int型地址+1就是加4个字节啊!这里直接+4*v4,那不是一下就跳过4个a1数组元素了吗!关键是我调试IDA既然没有问题!!!好吧,只能认为是IDA分析出错了,
++v4;
}
Str1[v4] = 0;
if ( !strcmp(Str1, "KanXueCTF2019JustForhappy") ) //从字典中获取的字符与明文对比,符合就是flag
result = sub_401770();
else
result = sub_4017B0();
return result;
}
所以现在就是字典和明文的加密关系逆向题了,这里犯下第四个错误,这类明文字典密文题目逆向要从密文出发,找到对应的字典下标,再用下标数组反逻辑逆向出明文:
第一步从密文出发,找到对应的字典下标:
key1="abcdefghiABCDEFGHIJKLMNjklmn0123456789opqrstuvwxyzOPQRSTUVWXYZ"
key2="KanXueCTF2019JustForhappy"
suoyin=[]
suoyin2=[]
v4=0
for i in range(len(key2)):
for a in range(len(key1)):
if key2[i] == key1[a]:
suoyin.append(a)
print(suoyin)
#print(len(suoyin))
这是我的做法,逐个对比,找出下标,当然后面还学到更好的.index(str)方法,后面会讲。
输出:
[19, 0, 27, 59, 44, 4, 11, 55, 14, 30, 28, 29, 37, 18, 44, 42, 43, 14, 38, 41, 7, 0, 39, 39, 48]
这就是字典索引了,然后后面逆向出明文时就犯错了,逆向,是从底部出发向上走,也是是我们一开始掌握的是结果,要从条件中有关结果的判断往上走,而不是从0~1000这样从上往下加密然后提取出对应条件的下标,虽然结果一样,但是流程就差太多了:
脚本:
suoyin2=[19, 0, 27, 59, 44, 4, 11, 55, 14, 30, 28, 29, 37, 18, 44, 42, 43, 14, 38, 41, 7, 0, 39, 39, 48]
v5=0
flag=""
for i in suoyin2: //这里的判断条件是从从条件中有关结果的判断往上走,因为前面写出了结果的范围,所以我们应该用结果的范围向上走。
if i >= 0 and i <= 9:
v5=i+48
elif i >= 10 and i <= 35:
v5=i+87
elif i >= 36:
v5=i+29
flag+=chr(v5)
print(flag)
输出:
j0rXI4bTeustBiIGHeCF70DDM
别人更好的利用.index获取索引下标的脚本:
table = "abcdefghiABCDEFGHIJKLMNjklmn0123456789opqrstuvwxyzOPQRSTUVWXYZ"
s = "KanXueCTF2019JustForhappy"
ff = []
for i in s:
ff.append(table.index(i)) //这里我不得不说真的秒,我是一时想不到,用字符串内置函数.index(str)完美输出索引,比我快多了。
flag = ""
for i in ff:
if 0 <= i <= 9:
flag += chr(i + 48)
elif 9 < i <= 35:
flag += chr(i + 87)
elif i > 36:
flag += chr(i + 29)
print (flag)
总结:
1:弹框函数,这里犯下第一个错误,IDA双击进去的数据都是db类型的,而我们一开始看到的弹框显示的是中文,所以我们要改类型为dd类型才可以,不然就显示不了中文,所以&byte_4035DC跟踪进去后要用热键D改为dd类型再转字符。
2:3:
aAbcdefghiabcde双击跟踪是abcdefghiABCDEFGHIJKLMNjklmn0123456789opqrstuvwxyzOPQRSTUVWXYZ’的62位长度字符串,也就不难解释while判断条件的<62了,这里还犯下第二,第三个错误。
第二个错误是一开始没看出来这是个数组,*(_DWORD )(a1 + 4 * v4)是取索引而已,a1是数组头地址,现在要知道带方括号的[]基本都是数组取字符!
第三个错误是这里(_DWORD *)(a1 + 4 * v4)型我是真的搞不懂,明明a1是int型,还是数组头地址,它前面的(_DWORD )把它又变成了uint32型地址,先不说这多此一举,关键是int型地址+1就是加4个字节啊!这里直接+4v4,那不是一下就跳过4个a1数组元素了吗!关键是我调试IDA既然没有问题!!!好吧,只能认为是IDA分析出错了。
4:所以现在就是字典和明文的加密关系逆向题了,这里犯下第四个错误,这类明文字典密文题目逆向要从密文出发,找到对应的字典下标,再用下标数组反逻辑逆向出明文:
解毕!敬礼!