目录
堆栈不平衡:
动态调试查看函数源码:
今天写了一道buu上的题目,学到新知识,记录一下
// positive sp value has been detected, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
char Str[50]; // [esp+12h] [ebp-96h] BYREF
char Destination[80]; // [esp+44h] [ebp-64h] BYREF
DWORD flOldProtect; // [esp+94h] [ebp-14h] BYREF
size_t v7; // [esp+98h] [ebp-10h]
int i; // [esp+9Ch] [ebp-Ch]
__main();
puts("please input you flag:");
if ( !VirtualProtect(encrypt, 0xC8u, 4u, &flOldProtect) )// 用于更改已分配的虚拟内存区域的访问权限。它的作用是保护或取消保护已分配的内存区域,以控制读、写或执行内存的能力。
exit(1);
scanf("%40s", Str); // 输入数据
v7 = strlen(Str);
if ( v7 != 24 ) // 字符长度的判断
{
puts("Wrong!");
exit(0);
}
strcpy(Destination, Str); // Str字符串复制到Destination中
wrong(Str); // 加密函数,得到假的flag
omg(Str); // 判断
for ( i = 0; i <= 186; ++i ) // 真正加密部分
*(encrypt + i) ^= 0x41u;
if ( encrypt(Destination) )
finally(Destination);
return 0;
}
打开附件,可以看到主函数逻辑,但是可以发现函数出现 positive sp value has been detected, the output may be wrong!字样,搜索可以发现出现此字符串是堆栈不平衡问题导致的。
某些函数在使用 f5 进行反编译时,会提示错误 "sp-analysis failed",导致无法正确反编译。原因可能是在代码执行中的 pop、push 操作不匹配,导致解析的时候 esp 发生错误。如用push + n条指令 + retn来实际跳转,而IDA会以为retn是函数要结束,结果它分析后发现调用栈不平衡,因此就提示sp analysis failed.
可以在Option->General->Disassembly中将Stack Point勾选上
操作完可以看到1处出现了一竖排的绿色数据,这就是程序的每步操作执行后对应的sp指针的偏移值。可以看到主函数在调用2处的encryptPc函数后,sp指针的偏移值发生了变化
修复堆栈不平衡:选中堆栈变化数据的上一个数据,按ALT+K,修改栈的偏移值为0
操作后可以看到sp指针的偏移值相同了,下面的finallyPc进行同样的操作。
修复好后,重新F5可以发现,函数没有出现sp-analysis failed的字样。
下面对main函数分析
int __cdecl omg(char *a1)
{
int v2[24]; // [esp+18h] [ebp-80h] BYREF
int i; // [esp+78h] [ebp-20h]
int v4; // [esp+7Ch] [ebp-1Ch]
v4 = 1;
qmemcpy(v2, &unk_4030C0, sizeof(v2));
for ( i = 0; i <= 23; ++i )
{
if ( a1[i] != v2[i] )
v4 = 0;
}
if ( v4 == 1 )
return puts("hahahaha_do_you_find_me?");
else
return puts("wrong ~~ But seems a little program");
}
3.omg函数是判断函数,对wrong函数加密后的数据进行判断
int __cdecl omg(char *a1)
{
int v2[24]; // [esp+18h] [ebp-80h] BYREF
int i; // [esp+78h] [ebp-20h]
int v4; // [esp+7Ch] [ebp-1Ch]
v4 = 1;
qmemcpy(v2, &unk_4030C0, sizeof(v2));
for ( i = 0; i <= 23; ++i )
{
if ( a1[i] != v2[i] )
v4 = 0;
}
if ( v4 == 1 )
return puts("hahahaha_do_you_find_me?");
else
return puts("wrong ~~ But seems a little program");
}
尝试写脚本解密
#include
#include
int main(){
int i;
unsigned char a[] =
{
102, 107, 99, 100, 127,
97, 103, 100, 59, 86,
107, 97, 123, 38, 59,
80, 99, 95, 77, 90,
113, 12, 55, 102,
};
for ( i = 0; i <= 23; ++i )
{
if ( (i & 1) != 0 ) // (i & 1) != 0 奇数满足条件
a[i] += i; // 奇数
else
a[i] ^= i;
printf("%c",a[i]); // 偶数
}
return 0;
}
//flag{fak3_alw35_sp_me!!}
哭...得到一个假flag,不过这个flag后面还有用,接着分析
4.下面可以看到encrypt和finallypt函数,这里是真正的加密部分
但是函数无法打开,动调试试,需要注意的是,在动调时需要输入假flag,才能跳过上面函数的判断
在此处下断点(之前尝试在24行处下断点但是失败了.花花枯萎.....),F8步过,F7单步进入encryptPc函数
将encryptPc处的数据里连通下面的一大块数据选中,按U取消定义,再按P重新创建函数,F5,得到函数源码 ,往下找finally函数,下面的finally函数同样操作,得到源码
encryptPc函数源码:
int __cdecl encrypt(char *a1)
{
int v2[19]; // [esp+1Ch] [ebp-6Ch] BYREF
int v3; // [esp+68h] [ebp-20h]
int i; // [esp+6Ch] [ebp-1Ch]
v3 = 1;
qmemcpy(v2, &unk_403040, sizeof(v2));
for ( i = 0; i <= 18; ++i )
{
if ( (a1[i] ^ Buffer[i]) != v2[i] )
{
puts("wrong ~");
v3 = 0;
exit(0);
}
}
puts("come here");
return v3;
}
不难看出a1数组与Buffer数组(跟进得到:hahahaha_do_you_find_me?)进行异或运算,异或后的值与v2进行比较判断,其中V2的值就是unk_403040地址处的值,直接写脚本解密
#include
#include
int main(){
int i;
unsigned char a1[] =
{
14, 13, 9, 6, 19,
5, 88, 86, 62, 6,
12, 60, 31, 87, 20,
107, 87, 89, 13,
};
char Buffer[]="hahahaha_do_you_find_me?";
for(i=0;i<19;i++){
a1[i]^=Buffer[i];
printf("%c",a1[i]);
}
return 0;
}
//flag{d07abccf8a410c
只有一半,剩下的部分在finally函数中,进入finally函数 分析
finaly函数:
unsigned int v1; // eax
char v3[9]; // [esp+13h] [ebp-15h] BYREF
int v4; // [esp+1Ch] [ebp-Ch]
strcpy(v3, "%tp&:");
v1 = time(0);
srand(v1);
v4 = rand() % 100;
v3[6] = 0;
*&v3[7] = 0;
if ( (v3[v3[5]] != a1[v3[5]]) == v4 )
return puts("Really??? Did you find it?OMG!!!");
else
return puts("I hide the last part, you will not succeed!!!");
}
一直看不懂怎么搞的,只能看出来有随机数参与,没看到有啥操作,看了其他大佬的wwp知道,此处是异或运算,异或后的密文是“%tp&:”可以通过flag最后一位一定是"}"来推算异或的key
直接写脚本,解密后面的数据
#include
#include
int main(){
int i;
char miwen[]="%tp&:";
int key=':'^'}';
for(i=0;i<5;i++){
miwen[i]^=key;
printf("%c",miwen[i]);
}
return 0;
}
//b37a}
得到完整flag