题目下载:
链接:https://pan.baidu.com/s/1ArbrvREhcbzVPFsHGoo2yA
提取码:3khv
IDA载入,寻找main函数
sub_401000()函数内部是一个反调试
而nullsub_1无法打开
查看main函数的汇编代码,发现有未定义的函数
继续下滑发现有关键代码
在函数头摁下快捷键P,将其定义为一个函数,摁下F5键发现还是无法F5
找到IDA设置,打开stack pointer
发现函数的最后,retn的栈小于0了,导致不能F5
先在pop 或者call那里,将栈指针调整(快捷键Alt+k),使得retn的栈指针大于等于0
如果出现下图情况(一会出现一会不出现,,玄学问题)
- 点进这个地址,让IDA自动分析一下,退出来就能F5
- 这道题,此call刚开始未定义,然后输出“Successful”的代码在此call的上面,可以判断此call并不是关键代码,因此将其取消定义(右键→undefine)
void __usercall hanshu1(int a1@<ebp>)
{
int v1; // ecx
*(a1 - 132) = 0;
*(a1 - 40) = 89;
*(a1 - 39) = 110;
*(a1 - 38) = 123;
*(a1 - 37) = 107;
*(a1 - 36) = 89;
*(a1 - 35) = 32;
*(a1 - 34) = 119;
*(a1 - 33) = 106;
*(a1 - 32) = 90;
*(a1 - 31) = 91;
*(a1 - 30) = 77;
*(a1 - 29) = 111;
*(a1 - 28) = 91;
*(a1 - 27) = 67;
*(a1 - 26) = 90;
*(a1 - 25) = 42;
*(a1 - 24) = 90;
*(a1 - 23) = 67;
*(a1 - 22) = 119;
*(a1 - 21) = 101;
*(a1 - 20) = 86;
*(a1 - 19) = 110;
*(a1 - 18) = 85;
*(a1 - 17) = 67;
*(a1 - 16) = 89;
*(a1 - 15) = 91;
*(a1 - 14) = 73;
*(a1 - 13) = 121;
*(a1 - 12) = 89;
*(a1 - 11) = 91;
*(a1 - 10) = 42;
*(a1 - 9) = 41;
*(a1 - 8) = 3;
printf(Format);
*(a1 - 76) = 0;
*(a1 - 75) = 0;
*(a1 - 71) = 0;
*(a1 - 67) = 0;
*(a1 - 63) = 0;
*(a1 - 59) = 0;
*(a1 - 55) = 0;
*(a1 - 51) = 0;
*(a1 - 47) = 0;
scanf(aS, a1 - 76); // a1-76 为用户的输入
*(a1 - 128) = a1 - 76; // a1-128 是一个指针,指向用户输入
*(a1 - 144) = *(a1 - 128) + 1; // a1-144存储用户输入第二个字符
do
*(a1 - 113) = *(*(a1 - 128))++;
while ( *(a1 - 113) );
*(a1 - 140) = *(a1 - 128) - *(a1 - 144);
*(a1 - 148) = hanshu2((a1 - 76), *(a1 - 140), (a1 - 136));// a1-140 为编码之前的长度
// a1-136 为base64编码后的长度
for ( *(a1 - 124) = 0; *(a1 - 124) < 33; ++*(a1 - 124) )// a1-124 为计数器 <33 33位
*(a1 + *(a1 - 124) - 112) = *(*(a1 - 124) + *(a1 - 148)) ^ 3;// base64编码后与3异或
// 赋给a1-112
for ( *(a1 - 120) = 0; *(a1 - 120) < *(a1 - 136); ++*(a1 - 120) )// a1-120为计数器,循环次数为base64编码后的长度
{
if ( *(a1 + *(a1 - 120) - 112) != *(a1 + *(a1 - 120) - 40) )// base64编码后,再与3异或,然后与a1-40比较
{
printf(asc_40302C);
exit(0);
}
if ( *(a1 + *(a1 - 120) - 112) == *(a1 + *(a1 - 120) - 40) )
++*(a1 - 132); // 如果不相等,a1-132就不加,不加的话,下面判断就会失败
}
if ( *(a1 - 132) == *(a1 - 136) )
printf(aSuccessful);
v1 = a1 ^ *(a1 - 4);
JUMPOUT(loc_40121B);
}
可以发现他把所有的变量都用指针来表示了,,,
定义了一个字符串,(从a1-40到a1-8)
这是最后与变换后的flag比较的字符串
hanshu2()是一个base64加密
可以参考Base16,Base32,Base64编码详细学习
查看其字符表,发现是一个自定义字符表的base64加密
其中&
=&
是一个转义字符串
转义字符串(Escape String),即字符实体(Character Entity)分成三部分:第一部分是一个&符号,英文叫ampersand;第二部分是实体(Entity)名字或者是#加上实体(Entity)编号;第三部分是一个分号。
总体流程为,程序将用户的输入进行base64加密,然后与3异或,最后与存储在栈上的字符串进行比对
写脚本
因为text长度为33,而base64将原字符串长度变为4/3,所以密文长度要能被4整除
所以密文的长度应该为36,需要加上三个"="
import base64
text = [89,110,123,107,89,32,119,106,90,91,77,111,91,67,90,42,90,67,119,101,86,110,85,67,89,91,73,121,89,91,42,41,3]
for i in range(len(text)):
text[i] ^= 3
text[i] = chr(text[i])
text = ''.join(text)
text += "==="
print(base64.b64decode(text.translate(str.maketrans("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz)!@#$%^&*(+/","ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")).encode('utf-8')))
最后得出flag为<mark>flag{base_f4ck_Reverse}</mark>