先看下文件信息,没有加壳,32位程序
运行一下,又是一道字符串比较的题目
用IDA32位打开,分析一下
// 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, 200u, 4u, &flOldProtect) )
exit(1);
scanf("%40s", Str);
v7 = strlen(Str);
if ( v7 != 24 ) // flag长度为24
{
puts("Wrong!");
exit(0);
}
strcpy(Destination, Str);
wrong(Str);
omg(Str);
for ( i = 0; i <= 186; ++i )
*((_BYTE *)encrypt + i) ^= 0x41u;
if ( encrypt(Destination) )
((void (__cdecl *)(char *))finally)(Destination);
return 0;
}
这里有个函数,详情可以看这个VirtualProtect函数,大概就是设置这一段内容可以改变
if ( !VirtualProtect(encrypt, 200u, 4u, &flOldProtect) )
下面这里将这段地址都异或了一个0x41
for ( i = 0; i <= 186; ++i )
*((_BYTE *)encrypt + i) ^= 0x41u;
而且无法访问encrypt这段地址,然后看一下汇编代码
这里就是经过异或后的结果
这几个函数对输入的字符串进行变换
scanf("%40s", Str);
v7 = strlen(Str);
if ( v7 != 24 ) // flag长度为24
{
puts("Wrong!");
exit(0);
}
strcpy(Destination, Str);
wrong(Str);
omg(Str);
跟进查看
char *__cdecl wrong(char *a1)
{
char *result; // eax
int i; // [esp+Ch] [ebp-4h]
for ( i = 0; i <= 23; ++i )
{
result = &a1[i];
if ( (i & 1) != 0 )
a1[i] -= i;
else
a1[i] ^= i;
}
return result;
}
int __cdecl omg(char *a1)
{
int result; // eax
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 )
result = puts("hahahaha_do_you_find_me?");
else
result = puts("wrong ~~ But seems a little program");
return result;
}
这得到的肯定不是真的字符串,写个脚本输出一下看看
#include
#include
#include
int main()
{
int i,j,k;
int a[] = {0x66,0x6B,0x63,0x64,
0x7F,0x61,0x67,0x64,
0x3B,0x56,0x6B,0x61,
0x7B,0x26,0x3B,0x50,
0x63,0x5F,0x4D,0x5A,
0x71,0x0C,0x37,0x66};
for(i = 0 ; i <= 23 ; i++)
{
if((i & 1) != 0)
a[i] += i;
else
a[i] ^= i;
printf("%c",a[i]);
}
return 0;
}
运行结果
参考了一下大佬的博客,这个部分原来是代码加密后的结果,叫SMC加密
这段代码是对加密后的代码进行自解密,除了动态调试,还可以用SMC代码字修改来解决问题,具体参考这个SMC代码自修改基础练习题以及防反编译之SMC(自修改代码)
for ( i = 0; i <= 186; ++i )
*((_BYTE *)encrypt + i) ^= 0x41u;
然后用动态调试来解决问题,可以参考这个IDA动调exe,因为是第一次用IDA动调,所以写得详细一点
步骤:1.找到文件夹IDA目录下的dbgsrv文件夹,找到对应版本的win_32或者64remote.exe,点击运行。
这个程序是32位的,所以我运行了这个win32remote,不知道win64行不行
2.打开ida,将exe拖入,Debugger → Select a debugger → Remote windows debugger
3.Debugger → process options → 填写三行(第一,二行为 文件的路径,第三行为空,也可设置为文件对应的根目录) 和 Hostname设为127.0.0.1(代表本机)这里前面三行都是自动填好的,所以只要填那个Hostname就行。端口是自动设置的,别改,改了就出问题
先运行我们要调试的软件,然后Debugger → Attach to process… → 选中我们要调试的程序 → attach
先运行起来
找不到的话可以搜索一下
然后就能正常debug了,然后就是要下断点,不下断点一下输入flag后一下就结束了,要下断点就得把这个Use source-level debugging 给选上
先下个断点,可以打开反汇编面板,然后按f5查看伪代码
在if这里下一个断点,然后一直f8跳过
然后输入之前得到的假flag
继续运行,这里会调用那个encrypt,按f7跟进
既然执行到这里了,那么后面的代码已经解密完成,选取401500到401589,然后按c,选择force强制转换为代码。
(如果将解密后的数据全部选中一次性转为代码,转换成伪代码的时候后半部分就无法正常转为伪代码)
然后选中这些红色部分的代码,按p转为函数然后就能愉快的f5了
转换后的代码
// positive sp value has been detected, the output may be wrong!
int __usercall sub_401502@<eax>(int a1@<ebp>)
{
unsigned __int32 v1; // eax
v1 = __indword(0x57u);
*(_DWORD *)(a1 - 32) = 1;
qmemcpy((void *)(a1 - 108), &unk_403040, 0x4Cu);
for ( *(_DWORD *)(a1 - 28) = 0; *(int *)(a1 - 28) <= 18; ++*(_DWORD *)(a1 - 28) )
{
if ( (char)(*(_BYTE *)(*(_DWORD *)(a1 - 28) + *(_DWORD *)(a1 + 8)) ^ Buffer[*(_DWORD *)(a1 - 28)]) != *(_DWORD *)(a1 + 4 * *(_DWORD *)(a1 - 28) - 108) )
{
puts("wrong ~");
*(_DWORD *)(a1 - 32) = 0;
exit(0);
}
}
if ( *(_DWORD *)(a1 - 32) == 1 )
puts("come here");
return *(_DWORD *)(a1 - 32);
}
稍微有点绕,就是一串字符异或Buffer里的字符后要等于unk_403040
Buffer
unk_403040
右键转到hex窗口
批量替换是个好东西
搞个小脚本把原来的字符串弄出来
#include
#include
#include
int main()
{
int i,j,k;
int b[] = { 0x0E,0x0D,0x09,0x06,
0x13,0x05,0x58,0x56,
0x3E,0x06,0x0C,0x3C,
0x1F,0x57,0x14,0x6B,
0x57,0x59,0x0D};
char s[] = "hahahaha_do_you_find_me?";
char flag[25];
for(i = 0 ; i < 19; i++)
{
flag[i] = s[i] ^ b[i];
}
flag[i] = '\0';
printf("%s\n",flag);
return 0;
}
运行结果: flag{d07abccf8a410c
明显后面缺了一块,继续按照上面的步骤将下面一段数据强转为代码
然后在开始的位置右键创建一个函数,然后继续f5大法
// positive sp value has been detected, the output may be wrong!
int __usercall sub_4015A0@<eax>(int a1@<ebp>)
{
unsigned int v1; // eax
int v2; // eax
int result; // eax
*(_BYTE *)(a1 - 21) = 37;
*(_BYTE *)(a1 - 20) = 116;
*(_BYTE *)(a1 - 19) = 112;
*(_BYTE *)(a1 - 18) = 38;
*(_BYTE *)(a1 - 17) = 58;
v1 = time(0);
srand(v1);
v2 = rand();
*(_DWORD *)(a1 - 12) = v2 / 100;
result = v2 - 100 * *(_DWORD *)(a1 - 12);
*(_DWORD *)(a1 - 12) = result;
*(_DWORD *)(a1 - 16) = 0;
if ( *(int *)(a1 - 16) <= 4 )
{
if ( (*(_BYTE *)(a1 - 21 + *(_DWORD *)(a1 - 16)) != *(_BYTE *)(*(_DWORD *)(a1 - 16) + *(_DWORD *)(a1 + 8))) == *(_DWORD *)(a1 - 12) )
result = puts("Really??? Did you find it?OMG!!!");
else
result = puts("I hide the last part, you will not succeed!!!");
}
return result;
}
这代码挺奇怪的,估计是我方法有问题,这是别的大佬弄出来的代码
这个大佬的博客推出了后面几位的方法,可以去看他的博客
大概就是说后面的5位数异或了一位数然后等于 %tp&:
(大佬的博客上面的)
25h,74h,70h,26h,3Ah
通过猜测,最后一位应该是},再猜0x3A(:)和 } 之间是异或关系,得到的值为71,再将之前的四个值都和71异或,得到b37a}
然后再试一下SMC解,从这个大佬那里嫖了个代码,存为idc文件
然后运行,接着重复上面的强转代码操作,一样能看到伪代码
然后再看下finally的代码,emmm更奇怪了
// positive sp value has been detected, the output may be wrong!
int __usercall sub_4015A0@<eax>(__int32 a1@<eax>, int a2@<edx>, int a3@<ebp>, int a4@<edi>)
{
__int32 v4; // eax
unsigned int v5; // eax
int v6; // ebp
unsigned int v7; // eax
int v8; // eax
int result; // eax
__time32_t *v10; // [esp-4h] [ebp-4h]
v4 = _InterlockedExchange((volatile __int32 *)(a2 + 4 * a3), a1);
v5 = _InterlockedExchange(MK_FP(v4, 4 * a3 - 1408989387), v4);
*(_DWORD *)(a4 - 2023248124) ^= v5;
if ( !__SETP__(v5, 82) )
JUMPOUT(0x40153B);
v6 = a3 + 1;
v7 = time(v10);
srand(v7);
v8 = rand();
*(_DWORD *)(v6 - 12) = v8 / 100;
result = v8 - 100 * *(_DWORD *)(v6 - 12);
*(_DWORD *)(v6 - 12) = result;
*(_DWORD *)(v6 - 16) = 0;
if ( *(int *)(v6 - 16) <= 4 )
{
if ( (*(_BYTE *)(v6 - 21 + *(_DWORD *)(v6 - 16)) != *(_BYTE *)(*(_DWORD *)(v6 - 16) + *(_DWORD *)(v6 + 8))) == *(_DWORD *)(v6 - 12) )
result = puts("Really??? Did you find it?OMG!!!");
else
result = puts("I hide the last part, you will not succeed!!!");
}
return result;
}