一.主函数逻辑
1.提示:// positive sp value has been detected, the output may be wrong!,出现了堆栈不平衡的情况
2.VirtualProtect函数的作用是取消encrypt函数所在区域的读写保护,为下方给函数脱壳做准备
3.首先输入一个字符串input,判断长度是否为24,然后复制到str中;
4.wrong函数对input串加密,加密后由omg函数进行判断是否满足条件,但是根据这两条函数得到的flag是假的,也就图一乐
5.真正的flag要根据encrypt和finally函数以及str串(原始的input)来求得
二.wrong函数和omg函数--假flag
1.wrong函数
很普通的一个加密
2.omg函数
很普通的一个判断
其中arr数组的内容可以通过4030c0区域提取得到,选中数据右键->convert->以dword形式输出
3.假flag
根据这两个函数可以写出解题脚本,但是输出的是假flag: flag{fak3_alw35_sp_me!!}
不过这个假flag就是我们要输入的字符串,下面还有作用
#include <stdio.h>
int main()
{
unsigned int input[25] = {
0x00000066, 0x0000006B, 0x00000063, 0x00000064, 0x0000007F, 0x00000061, 0x00000067, 0x00000064,
0x0000003B, 0x00000056, 0x0000006B, 0x00000061, 0x0000007B, 0x00000026, 0x0000003B, 0x00000050,
0x00000063, 0x0000005F, 0x0000004D, 0x0000005A, 0x00000071, 0x0000000C, 0x00000037, 0x00000066,0
};
int chr;
for (int i = 0; i <= 23; ++i)
{
if ((i & 1) != 0)
input[i] += i; //-号修改为+号
else
input[i] ^= i;
printf("%c", input[i]);
}
//flag{fak3_alw35_sp_me!!}
//假密码
return 0;
}
三.encrypt和finally函数--真flag
直接点击encrypt或finally函数会提示错误,并显示汇编代码,如果是框图形式可以按空格键转换一下
查看汇编代码不难发现有sp栈指针错误
1.打开sp指针偏移显示
在options>general>disassembly中,勾选上stackpointer
此时可以发现在地址和十六进制指令中间多了一列蓝色数字,这行数字代表的就是本条指令执行后sp指针的偏移值会是多少(应该是根据bp来计算的)
2.修复sp指针偏移值
我们回到主函数反汇编窗口,按tab键转换为汇编窗口,往下翻找到call 指令部分
选中call __Z7encryptPc这行指令,按alt+k,将新旧sp偏移值改成0;
这样操作后可以发现偏移值正常了,然后对下方的call __Z7finallyPc做相同操作
按f5也可以发现主函数没有了sp指针错误的提示
不过仍然进不去encrypt函数,finally函数也是很丑陋的一大段,这是由于encrypt函数还没有被去壳
3.动态调试得到正确的encrypt和finally函数
回到main函数,在**if ( encrypt(Str) )**这里下断点,开始动态调试,输入上面的假flag即可通过omg判断
(1)得到正确的encrypt函数
在main中点击encrypt函数,仍然不能反汇编,我们找到encrypt函数定义处对应的汇编代码
从定义头一直到endp,还有下方这一块区域,全部选中之后按u(undefined)取消定义,再在定义头按p即可识别为函数
取消定义重新识别后按f5成功得到encrypt函数
在这里可以dump下来403040和Buffer串的内容:
unsigned int dword_403040[19] = {
0x0000000E, 0x0000000D, 0x00000009, 0x00000006, 0x00000013, 0x00000005, 0x00000058, 0x00000056,
0x0000003E, 0x00000006, 0x0000000C, 0x0000003C, 0x0000001F, 0x00000057, 0x00000014, 0x0000006B,
0x00000057, 0x00000059, 0x0000000D
};unsigned char Buffer[]="hahahaha_do_you_find_me?";
值得一提的是动态调试结束后encrypt函数又会识别错误,这是因为每次运行程序会执行脱壳代码,而回到静态调试时会回到脱壳前的状态,所以导致识别错误
(2)得到正确的finally函数
在上一步的基础上,继续往下翻找到__Z7finallyPc,也是相同处理方法(包括下方一大段数据)
finally函数重新识别后在静态分析中也有效
得到反汇编代码:
四.解题脚本
1.flag前一段
根据encrypt函数的内容可以写出这个脚本,得到flag{d07abccf8a410c
显然还缺少了一段,这段需要分析finally函数
#include <stdio.h>
int main()
{
unsigned int dword_403040[19] = {
0x0000000E, 0x0000000D, 0x00000009, 0x00000006, 0x00000013, 0x00000005, 0x00000058, 0x00000056,
0x0000003E, 0x00000006, 0x0000000C, 0x0000003C, 0x0000001F, 0x00000057, 0x00000014, 0x0000006B,
0x00000057, 0x00000059, 0x0000000D
};
unsigned char Buffer[25] = "hahahaha_do_you_find_me?";
unsigned char flag[25] = { 0 };
for (int i = 0; i < 19; i++)
{
flag[i] = dword_403040[i] ^ Buffer[i];
printf("%c", flag[i]);
}
//flag{d07abccf8a410c
return 0;
}
2.flag第二段
这个非常迷惑,分析上面得到的反汇编代码无处下手,因为判断条件中有个随机数,看了其他大佬的wp
"%tp&:"这个字符串是最后一段,flag的最后一个字符是'}'根据题目描述的异或操作,可能是这段字符使用同一个值异或,所以只要由**':'^n='}'**得到这个用于异或的值即可求出其他字符
char n = ':' ^ '}', arr[6] = "%tp&:";
for (int i = 0; i < 5; i++)
{
arr[i] ^= n;
printf("%c", arr[i]);
}
//b37a}
所以flag{d07abccf8a410cb37a}
五.参考文章
1.[网鼎杯 2020 青龙组]jocker(详解)
2.IDA出现"sp-analysis failed"和F5(反编译)失败
3.IDA为什么产生 sp-analysis failed 错误?
4.C/C++ 函数的调用约定详解
5.【汇编 C】C语言常用的三种函数调用约定:__cdecl、__stdcall、__fastcall
6._stdcall与_cdecl区别
对于sp栈指针的修复以及函数调用,栈平衡后续会写一篇文章专门讲解
版权归原作者 OrientalGlass 所有, 如有侵权,请联系我们删除。