新人…
学校比赛,没什么经验,上去见见世面
某公司的月赛题 资源就不放了,只是当笔记
Ida打开 有点小陷阱 巧妙的堆栈运用导致载入ida分析不了 得不到函数的边界
比较幸运win32的程序
od打开
走一遍流程感受下
00AD12E7 . 52 push edx
00AD12E8 . 68 C821AD00 push 5ba358a4.00AD21C8 ; /%50s
00AD12ED . FF15 A420AD00 call dword ptr ds:[<&MSVCR100.scanf>] ; \scanf
00AD12F3 . 83C4 24 add esp,0x24
00AD12F6 . 50 push eax
00AD12F7 . 33C0 xor eax,eax
00AD12F9 . 74 03 je short 5ba358a4.00AD12FE
00AD12FB . 83C4 1E add esp,0x1E
00AD12FE > 58 pop eax ; 5ba358a4.00AD21B8
00AD12FF . 8D45 C8 lea eax,dword ptr ss:[ebp-0x38]
00AD1302 . 8D50 01 lea edx,dword ptr ds:[eax+0x1]
00AD1305 > 8A08 mov cl,byte ptr ds:[eax]
00AD1307 . 40 inc eax
00AD1308 . 84C9 test cl,cl
00AD130A .^ 75 F9 jnz short 5ba358a4.00AD1305
00AD130C . 2BC2 sub eax,edx ; 计算长度 33
00AD130E . 83F8 21 cmp eax,0x21
00AD1311 . 75 7D jnz short 5ba358a4.00AD1390
00AD1313 . 8D45 C8 lea eax,dword ptr ss:[ebp-0x38]
00AD1316 . E8 45FEFFFF call 5ba358a4.00AD1160
00AD131B . 8BF0 mov esi,eax
00AD131D . 8D85 C8FEFFFF lea eax,dword ptr ss:[ebp-0x138] ; 对比 flag{this_is_not_the_flag_hahaha}
00AD1323 . 8D50 01 lea edx,dword ptr ds:[eax+0x1]
00AD1326 > 8A08 mov cl,byte ptr ds:[eax]
00AD1328 . 40 inc eax
00AD1329 . 84C9 test cl,cl ; 计算对比的长度
00AD132B .^ 75 F9 jnz short 5ba358a4.00AD1326
00AD132D . 2BC2 sub eax,edx
00AD132F . 50 push eax
00AD1330 . 8D85 C8FEFFFF lea eax,dword ptr ss:[ebp-0x138] ; 对比长度 和对比地址 此时esi为输入变化后的
00AD1336 . 50 push eax
00AD1337 . 8DBD C8FDFFFF lea edi,dword ptr ss:[ebp-0x238]
00AD133D . E8 BEFCFFFF call 5ba358a4.00AD1000
00AD1342 . 8BC6 mov eax,esi ; 5ba358a4.00AD21B6
00AD1344 . 83C4 08 add esp,0x8
00AD1347 . 8D50 01 lea edx,dword ptr ds:[eax+0x1]
00AD134A . 8D9B 00000000 lea ebx,dword ptr ds:[ebx]
00AD1350 > 8A08 mov cl,byte ptr ds:[eax]
00AD1352 . 40 inc eax
00AD1353 . 84C9 test cl,cl
00AD1355 .^ 75 F9 jnz short 5ba358a4.00AD1350
00AD1357 . 2BC2 sub eax,edx ; 计算变化后的长度
00AD1359 . 50 push eax ; 长度
00AD135A . 56 push esi ; 第一次变化结果
00AD135B . 8D85 C8FDFFFF lea eax,dword ptr ss:[ebp-0x238]
00AD1361 . E8 7AFDFFFF call 5ba358a4.00AD10E0 ; 第二次变化的函数
00AD1366 . 83C4 08 add esp,0x8
00AD1369 . 33C0 xor eax,eax
00AD136B . 81EE 0421AD00 sub esi,5ba358a4.00AD2104
00AD1371 > 8A8C06 0421AD>mov cl,byte ptr ds:[esi+eax+0xAD2104] ; esi 在前面变化 索引着数组
00AD1378 . 3A88 0421AD00 cmp cl,byte ptr ds:[eax+0xAD2104]
00AD137E 75 23 jnz short 5ba358a4.00AD13A3
00AD1380 . 40 inc eax
00AD1381 . 83F8 2C cmp eax,0x2C ; 数组长度为44 正好是变化后的长度
00AD1384 .^ 7C EB jl short 5ba358a4.00AD1371
00AD1386 . 68 7C21AD00 push 5ba358a4.00AD217C ; Congratulation!!!!!!\n
00AD138B . FFD3 call ebx ; msvcr100.printf
00AD138D . 83C4 04 add esp,0x4
00AD1390 > 8B4D FC mov ecx,dword ptr ss:[ebp-0x4]
00AD1393 . 5F pop edi ; 5ba358a4.00AD21B8
00AD1394 . 5E pop esi ; 5ba358a4.00AD21B8
00AD1395 . 33CD xor ecx,ebp
00AD1397 . 33C0 xor eax,eax
00AD1399 . 5B pop ebx ; 5ba358a4.00AD21B8
00AD139A . E8 0D000000 call 5ba358a4.00AD13AC
00AD139F . 8BE5 mov esp,ebp
00AD13A1 . 5D pop ebp ; 5ba358a4.00AD21B8
00AD13A2 . C3 retn
00AD13A3 > 6A 00 push 0x0 ; /status = 0x0
00AD13A5 . FF15 9820AD00 call dword ptr ds:[<&MSVCR100.exit>] ; \exit
00AD13AB . CC int3
00AD13AC $ 3B0D 0030AD00 cmp ecx,dword ptr ds:[0xAD3000]
00AD13B2 . 75 02 jnz short 5ba358a4.00AD13B6
00AD13B4 . F3: prefix rep:
00AD13B5 . C3 retn
od走一遍过下来暂时得到的结论
两个关键函数 1 2;
1:把输入的字符串33字节 经过运算重新 申请空间 变成 44字节的字符串
2: 把44字节字符串 再次运算 变成 44字节的其他字符串 设为res
于是再次ida找到那两个函数 看看能不能看伪代码(能简单的就不要搞复杂)
特别棒可以得到伪代码
这是第一个关键函数
char *__usercall sub_401160@(const char *a1@)
{
const char *v1; // ebx
signed int v2; // esi
int v3; // edi
unsigned int v4; // eax
char *v5; // ecx
unsigned int v6; // edx
signed int v7; // ecx
int v8; // esi
unsigned int v9; // eax
signed int v10; // ecx
bool v11; // zf
signed int v13; // [esp+Ch] [ebp-14h]
const char *v14; // [esp+10h] [ebp-10h]
char *v15; // [esp+14h] [ebp-Ch]
char Dest[4]; // [esp+18h] [ebp-8h]
char *v17; // [esp+1Ch] [ebp-4h]
v1 = a1;
v2 = strlen(a1) / 3 + (strlen(a1) % 3 != 0);
v15 = (char *)malloc(4 * v2 + 1);
memset(v15, 0, 4 * v2 + 1);
if ( v2 > 0 )
{
v17 = v15;
v14 = v1;
v13 = v2;
do
{
v3 = 0;
*(_DWORD *)Dest = 0;
strncpy(Dest, v1, 3u);
v4 = 0;
v5 = &Dest[strlen(Dest) + 1];
v6 = v5 - &Dest[1];
if ( v5 != &Dest[1] )
{
v7 = 16;
do
{
v8 = Dest[v4++] << v7;
v7 -= 8;
v3 |= v8;
}
while ( v4 < v6 );
}
v9 = 0;
v10 = 18;
do
{
if ( v6 + 1 <= v9 )
v17[v9] = 61;
else
v17[v9] = byte_402138[(v3 >> v10) & 0x3F];
v10 -= 6;
++v9;
}
while ( v10 > -6 );
v17 += 4;
v1 = v14 + 3;
v11 = v13-- == 1;
v14 += 3;
}
while ( !v11 );
}
return v15;
}
这是第二个关键函数
int __usercall sub_4010E0@(int result@, int a2, unsigned int a3)
{
int v3; // ecx
int v4; // esi
unsigned int v5; // edi
unsigned __int8 v6; // dl
v3 = 0;
v4 = 0;
v5 = 0;
if ( a3 )
{
do
{
v3 = (v3 + 1) % 256;
v6 = *(_BYTE *)(v3 + result);
v4 = (v6 + v4) % 256;
*(_BYTE *)(v3 + result) = *(_BYTE *)(v4 + result);
*(_BYTE *)(v4 + result) = v6;
*(_BYTE *)(v5++ + a2) ^= *(_BYTE *)((v6 + *(unsigned __int8 *)(v3 + result)) % 256 + result);
}
while ( v5 < a3 );
}
return result;
}
由于我是新手,对已有的加密算法还不太敏感于是乎。。。
手动转成了C/C++代码
第一个函数 (简化版)转换
(把输入的33个字符运算转换为新的44个字符)
void first(char* orgin_buf) {
char beiBuf[] = "ABCDEFGHIJSTUVWKLMNOPQRXYZabcdqrstuvwxefghijklmnopyz0123456789+/";
char *v5;
int v6;
int i = 0;
int k = 0;
char change_buf[45] = { 0 };
bool nbk = false;
int v13 = 11;
while (!nbk)
{
int v3 = 0;
char Change4Buf[4] = { 0 };
strncpy(Change4Buf, &orgin_buf[i], 3);
v5 = Change4Buf + 4;
if (v5 != &Change4Buf[1])
{
int bit = 16;
for (int i_ = 0; i_<3; i_++)
{
int temp = Change4Buf[i_] << bit;
bit -= 8;
v3 |= temp;
}
}
for (int i_ = 18, j_ = 0; i_>-6; i_ -= 6, j_++) {
if (4 <= j_)
change_buf[k + j_] = 61;
else
change_buf[k + j_] = beiBuf[(v3 >> i_) & 0x3F];
}
i += 3;
k += 4;
nbk = (v13 == 1);
v13--;
}
cout << change_buf << endl;
}
第二个函数 (简化版转换,已经把用到的data段初始化好数据直接cp了)
其中 first_addr 这个地址里的数据由第一个函数 得到的44字符
void Eff4010e0(char* first_addr)
{
char result[] = "\x66\x32\xCA\xA0\xBF\x98\x2D\x76\xF1\x59\x2A\x4A\xF4\x30\xAD\xD2"
"\x1D\x02\xD8\x23\x89\x5D\x83\x38\x09\xF2\x74\x65\x40\x19\xC6\xDD"
"\x18\xD3\x8F\x6C\x8B\xC0\xC5\x54\x2E\x81\x10\xC4\x26\x56\x5F\x53"
"\x80\x43\x27\x62\xEA\x3D\xE6\x00\xE7\xB7\x50\x94\x90\x4C\x3F\x9D"
"\x07\xE0\xA3\x9C\x4E\x0F\x9F\xFE\x5B\x8E\xDE\x88\x72\x2F\xC1\x67"
"\x31\x70\x8D\xFD\xBE\x64\xC3\xBD\x6B\x7A\xCF\x0C\x34\x1F\x6F\x01"
"\xF0\x7C\x5E\xA4\x1E\x49\x8C\x75\x1C\xE3\x20\x48\x28\x79\xA5\x7F"
"\xF5\xEC\x4F\x78\x58\x11\xF7\xCD\x91\x13\xFC\xB8\x2C\x04\xEE\xD5"
"\x08\x44\xA9\xE1\xB1\x42\x84\x29\xA7\x47\x97\x7E\xE8\xB3\x60\x0B"
"\xF9\x4B\x3C\x77\x17\x03\x82\x69\x87\xD4\x95\x1A\x33\x25\x6E\xCC"
"\xD6\xBB\x99\xB0\x85\x41\xB2\x0D\xDB\x35\x3B\x5C\xF8\xED\x9E\xA6"
"\x96\x39\x63\x0A\x1B\x93\x21\x46\x12\xD0\xB4\x22\x51\xC9\x61\xD1"
"\x2B\xAA\x45\x06\x05\xCE\xFA\x92\x68\xAB\x36\xDA\xC8\xE2\x37\xD9"
"\xA2\x5A\xD7\x6A\xB5\xFF\xE9\xBA\x52\x15\xF6\xBC\x9A\xB6\xEF\x6D"
"\xCB\x4D\xAE\xE4\xA1\xAC\xEB\x0E\x71\x7B\xF3\x24\xC2\xFB\x7D\x86"
"\x55\xAF\x3A\xDF\x3E\x14\xB9\x9B\x16\xDC\x73\x57\xE5\xC7\x8A\xA8"
"\x66";
int i = 0;
int v3 = 0; // ecx
int v4 = 0; // esi
unsigned char v6; // dl
for (i; i<44; i++) {
v3 = (v3 + 1) % 256;
v6 = result[v3];
v4 = (v6 + v4) % 256;
result[v3] = result[v4];
result[v4] = v6;
unsigned char t = result[v3];
unsigned char temp = (v6 + t);
temp = temp % 256;
first_addr[i] ^= result[temp];
}
cout << first_addr << endl;
return;
}
分析发现
第二个函数下来后的结果设为res,res的44位 必须 和
byte_402104 44位一致
“\x20\xC3\x1A\xAE\x97\x3C\x7A\x41\xDE\xF6\x78\x15\xCB\x4B\x4C\xDC”
“\x26\x55\x8B\x55\xE5\xE9\x55\x75\x40\x3D\x82\x13\xA5\x60\x13\x3B”
“\xF5\xD8\x19\x0E\x47\xCF\x5F\x5E\xDE\x9D\x14\xBD”
又发现第二个函数是xor 所以直接把这段44长度的数据带入 自己写的Eff4010e0()函数
得到字符串
ZeptZ3l5UHQra25nd19yYzMrYR5wX2Jtc2P2VF9gYNM9
在分析的过程中有怀疑第一个函数是base64编码,
但是 程序里把
000000000000000000000000000000000
编码为:
UDAoUDAoUDAoUDAoUDAoUDAoUDAoUDAoUDAoUDAoUDAo
正确的结果应该为:MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw
所以怀疑第一个函数是base64加了一点点手脚,
于是 又有怀疑又有源码的我
char beiBuf[] = “ABCDEFGHIJSTUVWKLMNOPQRXYZabcdqrstuvwxefghijklmnopyz0123456789+/”;
感觉这个备选字符有问题
按26个字母的顺序摆摆正
//char beiBuf[] = "ABCDEFGHIJSTUVWKLMNOPQRXYZabcdqrstuvwxefghijklmnopyz0123456789+/";
char beiBuf[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
发现 这个函数出来的结果正式base64 所以确定动了手脚的地方就是这;
于是 只要把
"ZeptZ3l5UHQra25nd19yYzMrYR5wX2Jtc2P2VF9gYNM9"
转换成正确的base64 即可解码得到flag
char beiBuf2[] = "ABCDEFGHIJSTUVWKLMNOPQRXYZabcdqrstuvwxefghijklmnopyz0123456789+/"; //错误
char beiBuf[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; //正确
char check[] = "ZeptZ3l5UHQra25nd19yYzMrYR5wX2Jtc2P2VF9gYNM9";
for (int i = 0; i < strlen(check); i++)
{
int index = 0;
for (int j = 0; j < strlen(beiBuf2); j++)
{
if (beiBuf2[j] == check[i])
{
index = j;
break;
}
}
cout << beiBuf[index];
}
cout << endl;
正确的结果
ZmxhZ3t5MHVfa25vd19yYzRfYW5kX2Jhc2U2NF9oYSR9
然后解码base64就是flag