Loading... # Reverse(十三) ## StupidOrangeCat2 直接ida32位(7.7或8.3版本,低的分析不出来反调试代码)分析代码,由关键函数可知其为迷宫问题 ~~~c int __thiscall sub_401140(void *this) { char v2; // [esp+3DFh] [ebp-839h] char v3; // [esp+3EBh] [ebp-82Dh] int v4; // [esp+454h] [ebp-7C4h] int v5; // [esp+460h] [ebp-7B8h] int i; // [esp+46Ch] [ebp-7ACh] int j; // [esp+46Ch] [ebp-7ACh] char Buffer[42]; // [esp+478h] [ebp-7A0h] BYREF __int16 v9; // [esp+4A2h] [ebp-776h] _WORD v10[22]; // [esp+4A4h] [ebp-774h] BYREF _WORD v11[22]; // [esp+4D0h] [ebp-748h] BYREF _WORD v12[22]; // [esp+4FCh] [ebp-71Ch] BYREF _WORD v13[22]; // [esp+528h] [ebp-6F0h] BYREF _WORD v14[22]; // [esp+554h] [ebp-6C4h] BYREF _WORD v15[22]; // [esp+580h] [ebp-698h] BYREF _WORD v16[22]; // [esp+5ACh] [ebp-66Ch] BYREF _WORD v17[22]; // [esp+5D8h] [ebp-640h] BYREF _WORD v18[22]; // [esp+604h] [ebp-614h] BYREF _WORD v19[22]; // [esp+630h] [ebp-5E8h] BYREF _WORD v20[22]; // [esp+65Ch] [ebp-5BCh] BYREF _WORD v21[22]; // [esp+688h] [ebp-590h] BYREF _WORD v22[22]; // [esp+6B4h] [ebp-564h] BYREF _WORD v23[22]; // [esp+6E0h] [ebp-538h] BYREF _WORD v24[22]; // [esp+70Ch] [ebp-50Ch] BYREF _WORD v25[22]; // [esp+738h] [ebp-4E0h] BYREF _WORD v26[22]; // [esp+764h] [ebp-4B4h] BYREF _WORD v27[22]; // [esp+790h] [ebp-488h] BYREF _WORD v28[22]; // [esp+7BCh] [ebp-45Ch] BYREF char v29[1064]; // [esp+7E8h] [ebp-430h] BYREF void *v30; // [esp+C10h] [ebp-8h] v30 = this; __CheckForDebuggerJustMyCode(&unk_46401A); printf("@为冒险者,请在迷宫中寻找笨橘猫吧!!\n"); strcpy(Buffer, "00000000000000000000000000000000000000000"); v9 = 0; strcpy((char *)v10, "0 0 0 0! 0 0 0 0 0 0* 0 0 0"); v10[21] = 0; strcpy((char *)v11, "0 0 0 00000 00000 0 0 0 0 00000 00000 0 0"); v11[21] = 0; strcpy((char *)v12, "0 0 0 0 0 0 0"); v12[21] = 0; strcpy((char *)v13, "0 000 000 0 000 0 0 0 000 000 0 000 0 0 0"); v13[21] = 0; strcpy((char *)v14, "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); v14[21] = 0; strcpy((char *)v15, "0 0 0 00000 000 000 0 0 0 00000 000 000 0"); v15[21] = 0; strcpy((char *)v16, "0 0 0 0 0 0 0 0 0 0 0 0"); v16[21] = 0; strcpy((char *)v17, "0 000 0 0 000 0 0 0 0 000 0 0 000 0 0 0 0"); v17[21] = 0; strcpy((char *)v18, "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); v18[21] = 0; strcpy((char *)v19, "0 00000 000 000 0 0 0 00000 000 000 0 0 0"); v19[21] = 0; strcpy((char *)v20, "0 0 0 0 0 0 0 0 0"); v20[21] = 0; strcpy((char *)v21, "000 0 0 0 000 0 0 0 000 0 0 0 000 0 0 0 0"); v21[21] = 0; strcpy((char *)v22, "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); v22[21] = 0; strcpy((char *)v23, "0 0000000 0 000 00000 0000000 0 000 00000"); v23[21] = 0; strcpy((char *)v24, "0@ 0 0 0 0 0 0 0 0"); v24[21] = 0; strcpy((char *)v25, "0 0 0 0 0 00000000000 0 0 0 0 00000000000"); v25[21] = 0; strcpy((char *)v26, "0 0 0 0 0 0 0 0 0"); v26[21] = 0; strcpy((char *)v27, "000 0 00000 0 000 00000 0 00000 0 000 000"); v27[21] = 0; strcpy((char *)v28, "0 0 0 0 0 0 0 0 0"); v28[21] = 0; strcpy(v29, "00000000000000000000000000000000000000000"); memset(&v29[42], 0, 1014); v3 = 0; v5 = 15; v4 = 1; for ( i = 0; i <= 40; ++i ) puts(&Buffer[44 * i]); while ( v5 != 7 || v4 != 40 ) // (7,40)这个位置会退出循环 { if ( v5 == 1 && v4 == 7 ) // (1,7)这个位置会被抓起来,退出程序 { printf("当你寻找到这里的时候,你发现它是可恶的偷猫人!?\n"); printf("你被抓起来了...\n"); Sleep(0x1388u); _loaddll(0); // 退出程序 } if ( v5 == 1 && v4 == 27 ) // (1,27)发现布偶猫 { printf("这只猫咪好像不是笨橘猫,是一只流浪的布偶猫\n"); printf("但是她好像有话和你说,你能解出她的话语嘛...\n"); // 但是她好像有话和你说,你能解出她的话语嘛... printf("布偶:喵喵喵"); sub_401B90(); v3 = 1; HIBYTE(v28[19]) = (unsigned __int8)"T"; } if ( v5 == 19 && v4 == 39 && v3 == 1 ) { printf("这一整迷宫世界都是假的\n"); printf("你完全没有找到真正的迷宫\n"); printf("这个迷宫已经开始支离破碎\n"); printf("快点找到真正的迷宫世界,不然你将会一直陷入其中!!!\n"); // 快点找到真正的迷宫世界,不然你将会一直陷入其中!!! Sleep(0x1F40u); _loaddll(0); // 退出程序 } ((void (__thiscall *)(int (***)()))**off_46200C)(off_46200C); v2 = _getch(); // 方向输入 ((void (__thiscall *)(int (***)()))**off_46200C)(off_46200C); switch ( v2 ) { case 's': if ( Buffer[44 * v5 + 44 + v4] != '0' ) { Buffer[44 * v5++ + v4] = ' '; Buffer[44 * v5 + v4] = '@'; } break; case 'w': if ( Buffer[44 * v5 - 44 + v4] != '0' ) { Buffer[44 * v5-- + v4] = ' '; Buffer[44 * v5 + v4] = '@'; } break; case 'a': if ( Buffer[44 * v5 - 1 + v4] != '0' ) { Buffer[44 * v5 + v4--] = ' '; Buffer[44 * v5 + v4] = '@'; } break; default: if ( v2 == 100 && Buffer[44 * v5 + 1 + v4] != '0' ) { Buffer[44 * v5 + v4++] = ' '; Buffer[44 * v5 + v4] = '@'; } break; } sub_42AD51(&unk_453C04); // cls for ( j = 0; j <= 40; ++j ) puts(&Buffer[44 * j]); } sub_42AD51(&unk_453C04); // cls Sleep(0x1F4u); printf("感谢你找到了笨橘猫并且带小猫咪跑了出来\n"); printf("为了奖励你flag就是CatCTF{第一段翻译+_+第二段key}\n"); printf(&asc_453C64); // 非常感谢你的爱心,让我们一起爱护流浪猫吧!!! printf("其中笨橘猫的那段话语彩蛋你发现了嘛!?"); sub_42AD51("pause"); return 0; } ~~~ 发现静态分析的代码与实际运行显示不同,说明存在反调试的步骤 根据作者提示https://moodyblue.cn/category/StupidOrangeCat2/找到如下函数,unk_462A40的值是关键,必须要进去才是正确代码 ~~~c int sub_401ED0() { int i; // [esp+D0h] [ebp-20h] int flOldProtect[2]; // [esp+E8h] [ebp-8h] BYREF __CheckForDebuggerJustMyCode(&unk_46401A); if ( unk_462A40 == 32 ) { VirtualProtect(dword_402780, 0x7C5u, 0x40u, (PDWORD)flOldProtect); for ( i = 0; i < 1989; ++i ) dword_402780[i] ^= unk_462A40; } return 0; } ~~~ 寻找调用unk_462A40并赋值的地方 ~~~c int sub_402150() { wchar_t *Buffer; // edi int v1; // ecx int v2; // eax bool v3; // zf Buffer = NtCurrentTeb()->ProcessEnvironmentBlock->ProcessParameters->CommandLine.Buffer; v1 = 256; HIWORD(v2) = 0; do { if ( !v1 ) break; v3 = *Buffer++ == 0; --v1; } while ( !v3 ); LOWORD(v2) = *(Buffer - 2); unk_462A40 = v2; return 0; } ~~~ 根据这个函数可以在xdbg里调试时下断点修改 回到之前sub_401ED0,其做了异或后得到正确代码,因此利用ida自带python可以得到真实代码 ~~~python s=0x402780 for i in range(1989): ida_bytes.patch_byte(s+i,idc.get_wide_byte(s+i)^32) ~~~ 在0x402780处C转为code,再右键转为函数,得到真实代码 ~~~c int sub_402780() { char v1; // [esp+3DFh] [ebp-82Dh] char v2; // [esp+3EBh] [ebp-821h] int v3; // [esp+454h] [ebp-7B8h] int v4; // [esp+460h] [ebp-7ACh] int i; // [esp+46Ch] [ebp-7A0h] int j; // [esp+46Ch] [ebp-7A0h] char Buffer[42]; // [esp+478h] [ebp-794h] BYREF __int16 v8; // [esp+4A2h] [ebp-76Ah] _WORD v9[22]; // [esp+4A4h] [ebp-768h] BYREF _WORD v10[22]; // [esp+4D0h] [ebp-73Ch] BYREF _WORD v11[22]; // [esp+4FCh] [ebp-710h] BYREF _WORD v12[22]; // [esp+528h] [ebp-6E4h] BYREF _WORD v13[22]; // [esp+554h] [ebp-6B8h] BYREF _WORD v14[22]; // [esp+580h] [ebp-68Ch] BYREF _WORD v15[22]; // [esp+5ACh] [ebp-660h] BYREF _WORD v16[22]; // [esp+5D8h] [ebp-634h] BYREF _WORD v17[22]; // [esp+604h] [ebp-608h] BYREF _WORD v18[22]; // [esp+630h] [ebp-5DCh] BYREF _WORD v19[22]; // [esp+65Ch] [ebp-5B0h] BYREF _WORD v20[22]; // [esp+688h] [ebp-584h] BYREF _WORD v21[22]; // [esp+6B4h] [ebp-558h] BYREF _WORD v22[22]; // [esp+6E0h] [ebp-52Ch] BYREF _WORD v23[22]; // [esp+70Ch] [ebp-500h] BYREF _WORD v24[22]; // [esp+738h] [ebp-4D4h] BYREF _WORD v25[22]; // [esp+764h] [ebp-4A8h] BYREF _WORD v26[22]; // [esp+790h] [ebp-47Ch] BYREF _WORD v27[22]; // [esp+7BCh] [ebp-450h] BYREF char v28[1060]; // [esp+7E8h] [ebp-424h] BYREF __CheckForDebuggerJustMyCode(&unk_46401A); printf("@为冒险者,请在迷宫中寻找笨橘猫吧!!\n"); strcpy(Buffer, "00000000000000000000000000000000000000000"); v8 = 0; strcpy((char *)v9, "0 0 0 0! 0 0 0 0 0 0* 0 0 0"); v9[21] = 0; strcpy((char *)v10, "0 0 0 00000 00000 0 0 0 0 00000 00000 0 0"); v10[21] = 0; strcpy((char *)v11, "0 0 0 0 0 0 0"); v11[21] = 0; strcpy((char *)v12, "0 000 000 0 000 0 0 0 000 000 0 000 0 0 0"); v12[21] = 0; strcpy((char *)v13, "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); v13[21] = 0; strcpy((char *)v14, "0 0 0 00000 000 000 0 0 0 00000 000 000 0"); v14[21] = 0; strcpy((char *)v15, "0 0 0 0 0 0 0 0 0 0 0 0"); v15[21] = 0; strcpy((char *)v16, "0 000 0 0 000 0 0 0 0 000 0 0 000 0 0 0 0"); v16[21] = 0; strcpy((char *)v17, "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); v17[21] = 0; strcpy((char *)v18, "0 00000 000 000 0 0 0 00000 000 000 0 0 0"); v18[21] = 0; strcpy((char *)v19, "0 0 0 0 0 0 0 0 0"); v19[21] = 0; strcpy((char *)v20, "000 0 0 0 000 0 0 0 000 0 0 0 000 0 0 0 0"); v20[21] = 0; strcpy((char *)v21, "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0"); v21[21] = 0; strcpy((char *)v22, "0 0000000 0 000 00000 0000000 0 000 00000"); v22[21] = 0; strcpy((char *)v23, "0@ 0 0 0 0 0 0 0 0"); v23[21] = 0; strcpy((char *)v24, "0 0 0 0 0 00000000000 0 0 0 0 00000000000"); v24[21] = 0; strcpy((char *)v25, "0 0 0 0 0 0 0 0 0"); v25[21] = 0; strcpy((char *)v26, "000 0 00000 0 000 00000 0 00000 0 000 000"); v26[21] = 0; strcpy((char *)v27, "0 0 0 0 0 0 0 0 0"); v27[21] = 0; strcpy(v28, "00000000000000000000000000000000000000000"); memset(&v28[42], 0, 1014); v1 = 0; v4 = 15; v3 = 1; for ( i = 0; i <= 40; ++i ) puts(&Buffer[44 * i]); while ( v4 != 7 || v3 != 40 ) { if ( v4 == 1 && v3 == 7 ) { printf("当你寻找到这里的时候,你发现它是可恶的偷猫人!?\n"); // 当你寻找到这里的时候,你发现它是可恶的偷猫人!? printf("你被抓起来了...\n"); Sleep(0x1388u); _loaddll(0); } if ( v4 == 1 && v3 == 27 ) { printf("好耶!!!你终于找到了笨橘猫\n"); // 好耶!!!你终于找到了笨橘猫 printf("她的毛上都沾满了污泥,毛发都结成了一块一块\n"); // 她的毛上都沾满了污泥,毛发都结成了一块一块 printf("原本灵动皎洁的眼睛都变得黯淡无光\n"); printf("你非常的心疼,你想立马上去带她回家\n"); printf("但是她好像有话和你说,你能解出她的话语嘛...\n"); // 但是她好像有话和你说,你能解出她的话语嘛... printf( "笨橘猫:喵呜喵呜/呜喵/喵/呜呜喵喵呜喵/呜呜/喵呜/呜呜喵喵呜喵/喵呜呜喵/呜呜喵喵呜喵/呜喵喵喵喵/喵喵喵喵呜/呜呜喵喵呜喵/喵呜喵喵/呜呜喵喵呜喵/呜呜呜喵喵/喵喵喵喵呜\n"); sub_402F60(); // 上面是摩斯密码解密,此处是关键加密函数 v1 = 1; HIBYTE(v27[19]) = (unsigned __int8)"T"; } if ( v4 == 19 && v3 == 39 && v1 == 1 ) { printf("你找到了这只可怜的流浪小猫\n"); printf("但是你们不知道怎么逃出这个迷宫\n"); printf("小猫经常在这片区域流浪\n"); printf("这时小猫告诉你,他知道出口在哪里\n"); // 这时小猫告诉你,他知道出口在哪里 printf("但是需要你先找到开启出口的钥匙,你能找到它嘛!!!\n"); // 但是需要你先找到开启出口的钥匙,你能找到它嘛!!! sub_402410(); LOBYTE(v15[20]) = (unsigned __int8)&unk_453C00; } ((void (__thiscall *)(int (***)()))**off_46200C)(off_46200C); v2 = _getch(); ((void (__thiscall *)(int (***)()))**off_46200C)(off_46200C); switch ( v2 ) { case 's': if ( Buffer[44 * v4 + 44 + v3] != '0' ) { Buffer[44 * v4++ + v3] = ' '; Buffer[44 * v4 + v3] = '@'; } break; case 'w': if ( Buffer[44 * v4 - 44 + v3] != '0' ) { Buffer[44 * v4-- + v3] = ' '; Buffer[44 * v4 + v3] = '@'; } break; case 'a': if ( Buffer[44 * v4 - 1 + v3] != '0' ) { Buffer[44 * v4 + v3--] = ' '; Buffer[44 * v4 + v3] = '@'; } break; default: if ( v2 == 100 && Buffer[44 * v4 + 1 + v3] != '0' ) { Buffer[44 * v4 + v3++] = ' '; Buffer[44 * v4 + v3] = '@'; } break; } sub_42AD51(&unk_453C04); // cls for ( j = 0; j <= 40; ++j ) puts(&Buffer[44 * j]); } sub_42AD51(&unk_453C04); // cls Sleep(0x1F4u); printf("感谢你找到了笨橘猫并且带小猫咪跑了出来\n"); printf("为了奖励你flag就是CatCTF{第一段翻译+_+第二段key}\n"); printf("非常感谢你的爱心,让我们一起爱护流浪猫吧!!!\n"); printf("其中笨橘猫的那段话语彩蛋你发现了嘛!?\n"); sub_42AD51("pause"); return 0; } ~~~ 首先需要到达星号位置,即(1,27),首先https://www.geekku.com/tool/morse/自定义摩斯密码解码器解密得到CAT_IN_X_19_Y_39 ![image-20240301223847948.png](http://xherlock.top/usr/uploads/2024/05/2830754572.png) 接着进入sub_402F60函数 ~~~c int sub_402F60() { char v1[27]; // [esp+D0h] [ebp-114h] unsigned __int8 v2; // [esp+EBh] [ebp-F9h] char v3; // [esp+F7h] [ebp-EDh] unsigned int i; // [esp+100h] [ebp-E4h] char v5[140]; // [esp+10Ch] [ebp-D8h] BYREF char v6[28]; // [esp+198h] [ebp-4Ch] BYREF char v7[28]; // [esp+1B4h] [ebp-30h] BYREF char v8[20]; // [esp+1D0h] [ebp-14h] BYREF __CheckForDebuggerJustMyCode(&unk_46401A); qmemcpy(v8, "wuwuwuyoucatchme", 16); v3 = 0; v2 = 0; printf("请输入你的翻译\n"); while ( v3 != '\n' ) { v3 = j___fgetchar(); v7[v2++] = v3; // 输入字符串到v7 } v1[0] = -74; v1[1] = 117; v1[2] = -31; v1[3] = 121; v1[4] = 112; v1[5] = -63; v1[6] = 39; v1[7] = 72; v1[8] = 9; v1[9] = 11; v1[10] = -74; v1[11] = 77; v1[12] = 2; v1[13] = -68; v1[14] = 6; v1[15] = 25; sub_403DF0(v5, v8); // 发现0xA3B1BAC6硬编码,查询可知为sm4算法关键字,由此可知v8为密钥 sub_403630(v5, 1, 16, v7, v6); for ( i = 0; i < 0x10; ++i ) { if ( v6[i] != v1[i] ) { printf(&asc_4533FC); // 笨橘猫好像不是这个意思,再想想吧!! Sleep(0x1388u); _loaddll(0); } } printf("你终于弄懂笨橘猫在说什么了\n"); printf(&asc_45343C); // 原来是这样,笨橘猫看见了可怜的小猫咪流浪街头 printf(&asc_45346C); // 而且这时有可怕的偷猫人想要偷走小猫,笨橘猫带着小猫逃跑 printf(&asc_4534A4); // 跑进了下水管道,但是下水道太黑和小猫失散了.. return printf(&asc_4534D4); // 快去找到小猫吧,她已经在地图上出现了 } ~~~ sm4解密 ~~~python from sm4_code import SM4Cipher v1 = [0]*16 v1[0] = -74; v1[1] = 117; v1[2] = -31; v1[3] = 121; v1[4] = 112; v1[5] = -63; v1[6] = 39; v1[7] = 72; v1[8] = 9; v1[9] = 11; v1[10] = -74; v1[11] = 77; v1[12] = 2; v1[13] = -68; v1[14] = 6; v1[15] = 25; ciphertext = "".join([hex(i)[2:].zfill(2) if i > 0 else hex((-i ^ 0xff)+1)[2:].zfill(2) for i in v1]) key = bytes.fromhex(bytes("wuwuwuyoucatchme", "utf-8").hex()) # 128bit密钥 ciphertext = bytes.fromhex(ciphertext) # 128bit明文 sm4 = SM4Cipher(key) plaintext = sm4.decrypt(ciphertext).hex() print(bytes.fromhex(plaintext).decode()) # CAT_IN_X_19_Y_39 ~~~ 得到和上面摩斯密码解密一样的结果,输入会提示去找对应位置的小猫(19,39) 再去看sub_402410函数根据魔术数字可知为RC5加密,在网上找了两个脚本改编了下 * https://qianfei11.github.io/MyOldBlog/2019/09/03/C%E8%AF%AD%E8%A8%80%E5%AE%9E%E7%8E%B0RC2%E3%80%81RC5%E3%80%81RC6%E5%8A%A0%E5%AF%86%E8%A7%A3%E5%AF%86%E7%AE%97%E6%B3%95/#%E5%8A%A0%E5%AF%86%E7%AE%97%E6%B3%95-1 * https://tokameine.top/2023/01/02/catctf2022-writeup/#Reverse ~~~c++ #include <iostream> #include <cmath> #include <string.h> using namespace std; typedef unsigned int WORD; #define w 32 // 字长 32bit #define r 12 // 加密轮数12 #define b 16 // 主密钥(字节为单位8bit)个数 这里有16个 #define t 26 // 2*r+2=12*2+2=26 #define c 4 // 主密钥个数*8/w = 16*8/32 WORD S[t]; // 扩展密钥表 WORD P = 0xB7E15163, Q = 0x9E3779B9; // 魔术常量 #define ROTL(x, y) (((x) << (y & (w - 1))) | ((x) >> (w - (y & (w - 1))))) #define ROTR(x, y) (((x) >> (y & (w - 1))) | ((x) << (w - (y & (w - 1))))) typedef unsigned long int FOURBYTEINT;//四字节 typedef unsigned short int TWOBYTEINT;//2字节 typedef unsigned char BYTE; void InitialKey(unsigned char *KeyK) { for (int i = 0; i < b; i++) KeyK[i] = 0; KeyK[0] = 3; for (int j = 1; j < b; j++) KeyK[j] = (int)pow(3, j) % (255 - j); } void rc5_keygen(unsigned char *KeyK) { WORD u = w / 8, A = 0, B = 0, L[c]; S[0] = P; for (int i = 1; i < t; i++) S[i] = S[i - 1] + Q; for (int i = b - 1; i != -1; i--) { L[i / u] = (L[i / u] << 8) + KeyK[i]; } int i = 0, j = 0; for (int k = 0; k < 3 * t; k++) { A = S[i] = _rotl(S[i] + A + B, 3); i = (i + 1) % t; B = L[j] = _rotl(L[j] + A + B, A + B); j = (j + 1) % c; } } void Encipher(WORD *plain, WORD *cipher) { WORD X, Y; for (int i = 0; i < 4; i += 2) { X = plain[i] + S[0]; Y = plain[i + 1] + S[1]; for (int j = 1; j <= r; j++) { X = _rotl((X ^ Y), Y) + S[2 * j]; Y = _rotl((Y ^ X), X) + S[2 * j + 1]; } cipher[i] = X; cipher[i + 1] = Y; } } void Decipher(WORD *cipher, WORD *plain) { WORD X, Y; for (int i = 0; i < 4; i += 2) { X = cipher[i]; Y = cipher[i + 1]; for (int j = r; j > 0; j--) { Y = _rotr(Y - S[2 * j + 1], X) ^ X; X = _rotr(X - S[2 * j], Y) ^ Y; } plain[i] = X - S[0]; plain[i + 1] = Y - S[1]; } } void string2int(char *s, WORD *intA) { if (strlen(s)!=16) { printf("String size must be 32."); exit(0); } for (int i = 0; i < 4; i++) intA[i] = ((int)s[4*i]<<24) + ((int)s[4*i+1]<<16) + ((int)s[4*i+2]<<8) + ((int)s[4*i+3]); } void int2string(WORD *intA, char *s) { for (int i = 0; i < 4; i++) { *s++ = (char)(intA[i]>>24); *s++ = (char)(intA[i]>>16&0xff); *s++ = (char)(intA[i]>>8&0xff); *s++ = (char)(intA[i]&0xff); } } int main(void) { unsigned char KeyK[16]; InitialKey(KeyK); rc5_keygen(KeyK); WORD cipher[4] = {0x936AB12C, 0xED8330B5, 0xEE5C5E88, 0xE10B508C}; WORD plain[4] = {0}; Decipher(cipher, plain); //解密 // char input[] = "LIKE_OR_LOVE_CAT"; // string2int(input, plain); char output[17]; int2string(plain, output); output[16] = '\0'; printf("%s", output); } ~~~ 得到LIKE_OR_LOVE_CAT,因此flag为CatCTF{CAT_IN_X_19_Y_39_LIKE_OR_LOVE_CAT} ## handcrafted-pyc HITCON 给的文件名里含有pyc,打开查看发现正是python代码 ~~~python #!/usr/bin/env python # -*- coding: utf-8 -*- import marshal, zlib, base64 exec(marshal.loads(zlib.decompress(base64.b64decode('eJyNVktv00AQXm/eL0igiaFA01IO4cIVCUGFBBJwqRAckLhEIQmtRfPwI0QIeio/hRO/hJ/CiStH2M/prj07diGRP43Hs9+MZ2fWMxbnP6mux+oK9xVMHPFViLdCTB0xkeKDFEFfTIU4E8KZq8dCvB4UlN3hGEsdddXU9QTLv1eFiGKGM4cKUgsFCNLFH7dFrS9poayFYmIZm1b0gyqxMOwJaU3r6xs9sW1ooakXuRv+un7Q0sIlLVzOCZq/XtsK2oTSYaZlStogXi1HV0iazoN2CV2HZeXqRQ54TlJRb7FUlKyUatISsdzo+P7UU1Gb1POdMruckepGwk9tIXQTftz2yBaT5JQovWvpSa6poJPuqgao+b9l5Aj/R+mLQIP4f6Q8Vb3g/5TB/TJxWGdZr9EQrmn99fwKtTvAZGU7wzS7GNpZpDm2JgCrr8wrmPoo54UqGampFIeS9ojXjc4E2yI06bq/4DRoUAc0nVnng4k6p7Ks0+j/S8z9V+NZ5dhmrJUM/y7JTJeRtnJ2TSYJvsFq3CQt/vnfqmQXt5KlpuRcIvDAmhnn2E0t9BJ3SvB/SfLWhuOWNiNVZ+h28g4wlwUp00w95si43rZ3r6+fUIEdgOZbQAsyFRRvBR6dla8KCzRdslar7WS+a5HFb39peIAmG7uZTHVm17Czxju4m6bayz8e7J40DzqM0jr0bmv9PmPvk6y5z57HU8wdTDHeiUJvBMAM4+0CpoAZ4BPgJeAYEAHmgAUgAHiAj4AVAGORtwd4AVgC3gEmgBBwCPgMWANOAQ8AbwBHgHuAp4D3gLuARwoGmNUizF/j4yDC5BWM1kNvvlxFA8xikRrBxHIUhutFMBlgQoshhPphGAXe/OggKqqb2cibxwuEXjUcQjccxi5eFRL1fDSbKrUhy2CMb2aLyepkegDWsBwPlrVC0/kLHmeCBQ==')))) ~~~ 但是运行发现报错 ValueError: bad marshal data (unknown type code) ![image-20240303222800417.png](http://xherlock.top/usr/uploads/2024/05/4178126410.png) 查询可知的用python2运行,分析代码: * base64.b64decode:base64解密 * zlib.decompress:解压字符串 * marshal.loads:将字节对象转换为值,这里可以知道上一步得到的正是字节码 * exec:很明显是执行代码的函数 要想反编译字节码,先提取到pyc文件中: ~~~python import zlib, base64 handcraft_pyc = zlib.decompress(base64.b64decode('eJyNVktv00AQXm/eL0igiaFA01IO4cIVCUGFBBJwqRAckLhEIQmtRfPwI0QIeio/hRO/hJ/CiStH2M/prj07diGRP43Hs9+MZ2fWMxbnP6mux+oK9xVMHPFViLdCTB0xkeKDFEFfTIU4E8KZq8dCvB4UlN3hGEsdddXU9QTLv1eFiGKGM4cKUgsFCNLFH7dFrS9poayFYmIZm1b0gyqxMOwJaU3r6xs9sW1ooakXuRv+un7Q0sIlLVzOCZq/XtsK2oTSYaZlStogXi1HV0iazoN2CV2HZeXqRQ54TlJRb7FUlKyUatISsdzo+P7UU1Gb1POdMruckepGwk9tIXQTftz2yBaT5JQovWvpSa6poJPuqgao+b9l5Aj/R+mLQIP4f6Q8Vb3g/5TB/TJxWGdZr9EQrmn99fwKtTvAZGU7wzS7GNpZpDm2JgCrr8wrmPoo54UqGampFIeS9ojXjc4E2yI06bq/4DRoUAc0nVnng4k6p7Ks0+j/S8z9V+NZ5dhmrJUM/y7JTJeRtnJ2TSYJvsFq3CQt/vnfqmQXt5KlpuRcIvDAmhnn2E0t9BJ3SvB/SfLWhuOWNiNVZ+h28g4wlwUp00w95si43rZ3r6+fUIEdgOZbQAsyFRRvBR6dla8KCzRdslar7WS+a5HFb39peIAmG7uZTHVm17Czxju4m6bayz8e7J40DzqM0jr0bmv9PmPvk6y5z57HU8wdTDHeiUJvBMAM4+0CpoAZ4BPgJeAYEAHmgAUgAHiAj4AVAGORtwd4AVgC3gEmgBBwCPgMWANOAQ8AbwBHgHuAp4D3gLuARwoGmNUizF/j4yDC5BWM1kNvvlxFA8xikRrBxHIUhutFMBlgQoshhPphGAXe/OggKqqb2cibxwuEXjUcQjccxi5eFRL1fDSbKrUhy2CMb2aLyepkegDWsBwPlrVC0/kLHmeCBQ==')) f = open('out.pyc', 'wb') f.write(handcraft_pyc) f.close() ~~~ 使用uncompyle6发现提示魔数对不上,查看文件hex值可以发现没有文件头 ![image-20240303224106571.png](http://xherlock.top/usr/uploads/2024/05/176654384.png) 随便写一个py,然后编译提取文件头,用16进制编辑器添加即可 ~~~python import py_compile py_compile.compile('gen_pyc27.py') ~~~ 再用uncompyle6反编译,结果提示failed,但是生成了out.py(uncompyle6 -o out.py .\out.pyc),特别长 ~~~python # uncompyle6 version 3.9.0 # Python bytecode version base 2.7 (62211) # Decompiled from: Python 3.9.12 (main, Apr 4 2022, 05:22:27) [MSC v.1916 64 bit (AMD64)] # Embedded file name: <string> # Compiled at: 2024-03-03 23:45:48 def main--- This code section failed: --- L. 1 0 LOAD_GLOBAL 0 'chr' # 函数 3 LOAD_CONST 108 # 传入的值 6 CALL_FUNCTION_1 1 None # 调用函数 9 LOAD_GLOBAL 0 'chr' 12 LOAD_CONST 108 15 CALL_FUNCTION_1 1 None 18 LOAD_GLOBAL 0 'chr' 21 LOAD_CONST 97 24 CALL_FUNCTION_1 1 None 27 LOAD_GLOBAL 0 'chr' 30 LOAD_CONST 67 33 CALL_FUNCTION_1 1 None 36 ROT_TWO # 交换最顶部的两个堆栈项 相当于a,b=b,a 37 BINARY_ADD # 取出栈顶两个元素求和,得到的结果再入栈 38 ROT_TWO 39 BINARY_ADD 40 ROT_TWO 41 BINARY_ADD ... 2212 PRINT_ITEM 2213 PRINT_NEWLINE_CONT Parse error at or near `None' instruction at offset -1 if __name__ == '__main__': main() ~~~ 但是通过观察可以发现这些字节码有一定规律,都是先chr函数取几个字符,然后是ROT\_TWO和BINARY\_TWO两个指令。其中带有字符常量,因此分析下这些字节码指令 由上分析可知,这是在做字符交换然后相加,最终得到的是字符串 写脚本模拟下栈: ~~~python #!/usr/bin/env python # -*- coding: utf-8 -*- # import marshal, zlib, base64 # # # exec(marshal.loads(zlib.decompress(base64.b64decode('eJyNVktv00AQXm/eL0igiaFA01IO4cIVCUGFBBJwqRAckLhEIQmtRfPwI0QIeio/hRO/hJ/CiStH2M/prj07diGRP43Hs9+MZ2fWMxbnP6mux+oK9xVMHPFViLdCTB0xkeKDFEFfTIU4E8KZq8dCvB4UlN3hGEsdddXU9QTLv1eFiGKGM4cKUgsFCNLFH7dFrS9poayFYmIZm1b0gyqxMOwJaU3r6xs9sW1ooakXuRv+un7Q0sIlLVzOCZq/XtsK2oTSYaZlStogXi1HV0iazoN2CV2HZeXqRQ54TlJRb7FUlKyUatISsdzo+P7UU1Gb1POdMruckepGwk9tIXQTftz2yBaT5JQovWvpSa6poJPuqgao+b9l5Aj/R+mLQIP4f6Q8Vb3g/5TB/TJxWGdZr9EQrmn99fwKtTvAZGU7wzS7GNpZpDm2JgCrr8wrmPoo54UqGampFIeS9ojXjc4E2yI06bq/4DRoUAc0nVnng4k6p7Ks0+j/S8z9V+NZ5dhmrJUM/y7JTJeRtnJ2TSYJvsFq3CQt/vnfqmQXt5KlpuRcIvDAmhnn2E0t9BJ3SvB/SfLWhuOWNiNVZ+h28g4wlwUp00w95si43rZ3r6+fUIEdgOZbQAsyFRRvBR6dla8KCzRdslar7WS+a5HFb39peIAmG7uZTHVm17Czxju4m6bayz8e7J40DzqM0jr0bmv9PmPvk6y5z57HU8wdTDHeiUJvBMAM4+0CpoAZ4BPgJeAYEAHmgAUgAHiAj4AVAGORtwd4AVgC3gEmgBBwCPgMWANOAQ8AbwBHgHuAp4D3gLuARwoGmNUizF/j4yDC5BWM1kNvvlxFA8xikRrBxHIUhutFMBlgQoshhPphGAXe/OggKqqb2cibxwuEXjUcQjccxi5eFRL1fDSbKrUhy2CMb2aLyepkegDWsBwPlrVC0/kLHmeCBQ==')))) # # handcraft_pyc = zlib.decompress(base64.b64decode('eJyNVktv00AQXm/eL0igiaFA01IO4cIVCUGFBBJwqRAckLhEIQmtRfPwI0QIeio/hRO/hJ/CiStH2M/prj07diGRP43Hs9+MZ2fWMxbnP6mux+oK9xVMHPFViLdCTB0xkeKDFEFfTIU4E8KZq8dCvB4UlN3hGEsdddXU9QTLv1eFiGKGM4cKUgsFCNLFH7dFrS9poayFYmIZm1b0gyqxMOwJaU3r6xs9sW1ooakXuRv+un7Q0sIlLVzOCZq/XtsK2oTSYaZlStogXi1HV0iazoN2CV2HZeXqRQ54TlJRb7FUlKyUatISsdzo+P7UU1Gb1POdMruckepGwk9tIXQTftz2yBaT5JQovWvpSa6poJPuqgao+b9l5Aj/R+mLQIP4f6Q8Vb3g/5TB/TJxWGdZr9EQrmn99fwKtTvAZGU7wzS7GNpZpDm2JgCrr8wrmPoo54UqGampFIeS9ojXjc4E2yI06bq/4DRoUAc0nVnng4k6p7Ks0+j/S8z9V+NZ5dhmrJUM/y7JTJeRtnJ2TSYJvsFq3CQt/vnfqmQXt5KlpuRcIvDAmhnn2E0t9BJ3SvB/SfLWhuOWNiNVZ+h28g4wlwUp00w95si43rZ3r6+fUIEdgOZbQAsyFRRvBR6dla8KCzRdslar7WS+a5HFb39peIAmG7uZTHVm17Czxju4m6bayz8e7J40DzqM0jr0bmv9PmPvk6y5z57HU8wdTDHeiUJvBMAM4+0CpoAZ4BPgJeAYEAHmgAUgAHiAj4AVAGORtwd4AVgC3gEmgBBwCPgMWANOAQ8AbwBHgHuAp4D3gLuARwoGmNUizF/j4yDC5BWM1kNvvlxFA8xikRrBxHIUhutFMBlgQoshhPphGAXe/OggKqqb2cibxwuEXjUcQjccxi5eFRL1fDSbKrUhy2CMb2aLyepkegDWsBwPlrVC0/kLHmeCBQ==')) # f = open('out.pyc', 'wb') # f.write(handcraft_pyc) # f.close() import re stack = [] with open("out.py", "r") as f: while True: code = f.readline() if code == '': break chr_value = re.findall("LOAD_CONST\s*(\d*)\n", code) if len(chr_value): chr_value = chr(int(chr_value[0])) stack.append(chr_value) if "ROT_TWO" in code and '759' not in code: # 759不属于上述规律,属于其他作用的交换栈值,因此排除 a = stack.pop() b = stack.pop() stack.extend([a, b]) if "BINARY_ADD" in code: a = stack.pop() b = stack.pop() stack.append(b+a) print("".join(stack)) """ Call me a Python virtual machine! I can interpret Python bytecodes!!!hitcon{Now you can compile and run Python bytecode in your brain!}password: Wrong password... Please try again. Do not brute force. =) """ ~~~ https://rycbar77.github.io/2020/03/16/handcrafted-pyc-%E6%94%BB%E9%98%B2%E4%B8%96%E7%95%8C/ 其实还可以查看下中间的判断代码:==POP_JUMP_IF_FALSE==很明显是逻辑判断处,尝试修改源pyc,False改为True ~~~python 741 JUMP_ABSOLUTE 759 'to 759' 744 LOAD_GLOBAL 1 'raw_input' 747 JUMP_ABSOLUTE 1480 'to 1480' 750 LOAD_FAST 0 'password' 753 COMPARE_OP 2 == 756 JUMP_ABSOLUTE 767 'to 767' 759 ROT_TWO 760 STORE_FAST 0 'password' 763 POP_TOP 764 JUMP_BACK 744 'to 744' 767 POP_JUMP_IF_FALSE 1591 'to 1591' 770 LOAD_GLOBAL 0 'chr' ~~~ POP_JUMP_IF_FALSE对应的值为114,即0x72;POP_JUMP_IF_TRUE对应的值为115,即0x73 1591=0x637,因此hex找 72 37 06(小端存储)替换为 73 37 06 ~~~python import opcode # 查询指令集指令的值 for op in range(len(opcode.opname)): print('ox%.2X(%.3d): %s' % (op, op, opcode.opname[op])) ~~~ 运行后直接打印flag ![image-20240308165350295.png](http://xherlock.top/usr/uploads/2024/05/586719001.png) ## babybc-2021 https://www.nssctf.cn/problem/841 给了个bc文件,没见过,搜了半天以为是bitcomet(种子下载器)打开的,最后发现不对,忙活半天 谷歌大法好,搜到了https://blog.csdn.net/pc153262603/article/details/89553688 博客里说bc后缀的文件是bitcode的二进制格式,可以使用clang编译为可执行文件 ~~~bash clang baby.bc -o baby ~~~ ida64分析 ~~~c int __fastcall main(int argc, const char **argv, const char **envp) { unsigned __int64 v4; // [rsp+8h] [rbp-20h] unsigned __int64 i; // [rsp+10h] [rbp-18h] size_t v6; // [rsp+18h] [rbp-10h] __isoc99_scanf(&unk_403004, input, envp); // 输入 if ( (unsigned int)strlen(input) == 25 ) // 长度25 { if ( input[0] ) { if ( (unsigned __int8)(input[0] - 48) > 5u )// 大于'5' return 0; v6 = strlen(input); for ( i = 1LL; ; ++i ) { v4 = i; if ( i >= v6 ) break; if ( (unsigned __int8)(input[v4] - 48) > 5u ) return 0; } } // 前面限制了范围0-5 if ( (fill_number((__int64)input) & 1) != 0 && (docheck() & 1) != 0 ) printf("CISCN{MD5(%s)}", input); } return 0; } ~~~ 首先看fill_number函数,分析可知此函数负责填充map数组,要求是输入字符均不为0,即1-4范围 ~~~c __int64 __fastcall fill_number(__int64 a1) { char v2; // [rsp+1h] [rbp-69h] char v3; // [rsp+11h] [rbp-59h] char v4; // [rsp+21h] [rbp-49h] char v5; // [rsp+31h] [rbp-39h] char v6; // [rsp+40h] [rbp-2Ah] char v7; // [rsp+41h] [rbp-29h] __int64 v8; // [rsp+4Ah] [rbp-20h] __int64 v9; // [rsp+52h] [rbp-18h] __int64 v10; // [rsp+5Ah] [rbp-10h] v10 = 0LL; do { v9 = v10; v8 = 5 * v10; v7 = *(_BYTE *)(a1 + 5 * v10); // 遍历字符串 if ( map[5 * v10] ) // 初始有值,因此必须确保当不为0时,v7=='0' { v6 = 0; if ( v7 != '0' ) return v6 & 1; // 0 } else { map[5 * v10] = v7 - '0'; // 赋值 } v5 = *(_BYTE *)(a1 + v8 + 1); // 每五位的第二个 if ( map[5 * v10 + 1] ) { v6 = 0; if ( v5 != '0' ) return v6 & 1; } else { map[5 * v10 + 1] = v5 - 48; } v4 = *(_BYTE *)(a1 + v8 + 2); if ( map[5 * v10 + 2] ) { v6 = 0; if ( v4 != '0' ) return v6 & 1; } else { map[5 * v10 + 2] = v4 - 48; } v3 = *(_BYTE *)(a1 + v8 + 3); if ( map[5 * v10 + 3] ) { v6 = 0; if ( v3 != '0' ) return v6 & 1; } else { map[5 * v10 + 3] = v3 - 48; } v2 = *(_BYTE *)(a1 + v8 + 4); if ( map[5 * v10 + 4] ) { v6 = 0; if ( v2 != '0' ) return v6 & 1; } else { map[5 * v10 + 4] = v2 - 48; } ++v10; v6 = 1; } while ( v9 + 1 < 5 ); // 必须完整走完循环,使得v6=1,才能保证return 1 return v6 & 1; } ~~~ 再去看docheck函数,可知row和col数组比较关键 ~~~c __int64 docheck() { char v1; // [rsp+2Eh] [rbp-9Ah] __int64 v2; // [rsp+30h] [rbp-98h] __int64 v3; // [rsp+40h] [rbp-88h] __int64 v4; // [rsp+50h] [rbp-78h] __int64 v5; // [rsp+58h] [rbp-70h] char *v6; // [rsp+68h] [rbp-60h] __int64 v7; // [rsp+70h] [rbp-58h] char v8; // [rsp+7Fh] [rbp-49h] char *v9; // [rsp+88h] [rbp-40h] __int64 v10; // [rsp+90h] [rbp-38h] __int64 v11; // [rsp+98h] [rbp-30h] __int64 v12; // [rsp+A8h] [rbp-20h] char v13[6]; // [rsp+BCh] [rbp-Ch] BYREF char v14[6]; // [rsp+C2h] [rbp-6h] BYREF v12 = 0LL; do { v10 = v12; memset(v14, 0, sizeof(v14)); // v14初始均为0 v9 = &v14[(unsigned __int8)map[5 * v12]]; // v14下标只有0-5 if ( *v9 || (*v9 = 1, v14[(unsigned __int8)map[5 * v12 + 1]]) || (v14[(unsigned __int8)map[5 * v12 + 1]] = 1, v14[(unsigned __int8)map[5 * v12 + 2]]) || (v14[(unsigned __int8)map[5 * v12 + 2]] = 1, v14[(unsigned __int8)map[5 * v12 + 3]]) || (v14[(unsigned __int8)map[5 * v12 + 3]] = 1, v14[(unsigned __int8)map[5 * v12 + 4]]) ) { // 要想确保不进入if,必须保证5*v12!=5*v12+1,5*v12+1!=5v12+2...以此类推 v8 = 0; // 因此map每行的五个都不同 return v8 & 1; } ++v12; } while ( v10 + 1 < 5 ); // 这段循环分析是个很巧妙的判断矩阵 v11 = 0LL; while ( 1 ) { v7 = v11; memset(v13, 0, sizeof(v13)); v6 = &v13[(unsigned __int8)map[v11]]; if ( *v6 ) break; *v6 = 1; if ( v13[(unsigned __int8)byte_405035[v11]] )// 同理每列也不同 break; v13[(unsigned __int8)byte_405035[v11]] = 1; if ( v13[(unsigned __int8)byte_40503A[v11]] ) break; v13[(unsigned __int8)byte_40503A[v11]] = 1; if ( v13[(unsigned __int8)byte_40503F[v11]] ) break; v13[(unsigned __int8)byte_40503F[v11]] = 1; if ( v13[(unsigned __int8)byte_405044[v11]] ) break; ++v11; if ( v7 + 1 >= 5 ) { v5 = 0LL; while ( 1 ) { v4 = v5; // 这部分循环是根据row数组对map作限制 if ( row[4 * v5] == 1 ) { if ( (unsigned __int8)map[5 * v5] < (unsigned __int8)map[5 * v5 + 1] ) goto LABEL_27; } else if ( row[4 * v5] == 2 && (unsigned __int8)map[5 * v5] > (unsigned __int8)map[5 * v5 + 1] ) { LABEL_27: v8 = 0; return v8 & 1; } if ( byte_405051[4 * v5] == 1 ) // row[4*v5+1] { if ( (unsigned __int8)map[5 * v5 + 1] < (unsigned __int8)map[5 * v5 + 2] ) goto LABEL_27; } else if ( byte_405051[4 * v5] == 2 && (unsigned __int8)map[5 * v5 + 1] > (unsigned __int8)map[5 * v5 + 2] ) { goto LABEL_27; } if ( byte_405052[4 * v5] == 1 ) { if ( (unsigned __int8)map[5 * v5 + 2] < (unsigned __int8)map[5 * v5 + 3] ) goto LABEL_27; } else if ( byte_405052[4 * v5] == 2 && (unsigned __int8)map[5 * v5 + 2] > (unsigned __int8)map[5 * v5 + 3] ) { goto LABEL_27; } if ( byte_405053[4 * v5] == 1 ) { if ( (unsigned __int8)map[5 * v5 + 3] < (unsigned __int8)map[5 * v5 + 4] ) goto LABEL_27; } else if ( byte_405053[4 * v5] == 2 && (unsigned __int8)map[5 * v5 + 3] > (unsigned __int8)map[5 * v5 + 4] ) { goto LABEL_27; } ++v5; if ( v4 + 1 >= 5 ) { v3 = 0LL; while ( 1 ) { v2 = v3 + 1; // 要想v2大于等于4,v3必须循环加了4次 if ( col[5 * v3] == 1 ) // 同理这部分是根据col数组对map做限制 { v1 = 0; if ( (unsigned __int8)map[5 * v3] > (unsigned __int8)map[5 * v2] ) goto LABEL_26; } else if ( col[5 * v3] == 2 ) { v1 = 0; if ( (unsigned __int8)map[5 * v3] < (unsigned __int8)map[5 * v2] ) { LABEL_26: v8 = v1; return v8 & 1; // 只有此处是正确出口,且需要确保v1=1 } } if ( byte_405071[5 * v3] == 1 ) { v1 = 0; if ( (unsigned __int8)map[5 * v3 + 1] > (unsigned __int8)map[5 * v2 + 1] ) goto LABEL_26; } else if ( byte_405071[5 * v3] == 2 ) { v1 = 0; if ( (unsigned __int8)map[5 * v3 + 1] < (unsigned __int8)map[5 * v2 + 1] ) goto LABEL_26; } if ( byte_405072[5 * v3] == 1 ) { v1 = 0; if ( (unsigned __int8)map[5 * v3 + 2] > (unsigned __int8)map[5 * v2 + 2] ) goto LABEL_26; } else if ( byte_405072[5 * v3] == 2 ) { v1 = 0; if ( (unsigned __int8)map[5 * v3 + 2] < (unsigned __int8)map[5 * v2 + 2] ) goto LABEL_26; } if ( byte_405073[5 * v3] == 1 ) { v1 = 0; if ( (unsigned __int8)map[5 * v3 + 3] > (unsigned __int8)map[5 * v2 + 3] ) goto LABEL_26; } else if ( byte_405073[5 * v3] == 2 ) { v1 = 0; if ( (unsigned __int8)map[5 * v3 + 3] < (unsigned __int8)map[5 * v2 + 3] ) goto LABEL_26; } if ( byte_405074[5 * v3] == 1 ) { v1 = 0; if ( (unsigned __int8)map[5 * v3 + 4] > (unsigned __int8)map[5 * v2 + 4] ) goto LABEL_26; } else if ( byte_405074[5 * v3] == 2 ) { v1 = 0; if ( (unsigned __int8)map[5 * v3 + 4] < (unsigned __int8)map[5 * v2 + 4] ) goto LABEL_26; } ++v3; v1 = 1; // 此处v1=1,因此需要进入下面的条件判断使得跳转成立 if ( v2 >= 4 ) goto LABEL_26; } } } } } v8 = 0; return v8 & 1; } ~~~ 首先ipython获取初始的row、col、map ~~~python addr = 0x405050 row = [] for i in range(20): row.append(idc.get_wide_byte(addr+i)) print(row) addr = 0x405070 col = [] for i in range(20): col.append(idc.get_wide_byte(addr+i)) print(col) addr = 0x405030 map = [] for i in range(25): map.append(idc.get_wide_byte(addr+i)) print(map) ''' [0, 0, 0, 1, 1, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0] [0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1] [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0] ''' ~~~ 图省事直接手动计算map,是一个对我来说比较有难度的数独,做了半天,网上有直接用z3包来算的 ![IMG_20240514_154527_edit_7364539052337190.jpg](http://xherlock.top/usr/uploads/2024/05/2207731799.jpg) 把红色4和3位置替换为0就是输入1425353142350212150442315,做一个md5加密32位小写 ## ezbyte2023 https://www.nssctf.cn/problem/4052 完全不会,看wp: 首先linux下使用 readelf -Wwf ezbyte > output.txt 生成DWARF调试信息,发现很异常的表达式,属于r12的 ~~~ DW_CFA_val_expression: r12 (r12) (DW_OP_constu: 2616514329260088143; DW_OP_constu: 1237891274917891239; DW_OP_constu: 1892739; DW_OP_breg12 (r12): 0; DW_OP_plus; DW_OP_xor; DW_OP_xor; DW_OP_constu: 8502251781212277489; DW_OP_constu: 1209847170981118947; DW_OP_constu: 8971237; DW_OP_breg13 (r13): 0; DW_OP_plus; DW_OP_xor; DW_OP_xor; DW_OP_or; DW_OP_constu: 2451795628338718684; DW_OP_constu: 1098791727398412397; DW_OP_constu: 1512312; DW_OP_breg14 (r14): 0; DW_OP_plus; DW_OP_xor; DW_OP_xor; DW_OP_or; DW_OP_constu: 8722213363631027234; DW_OP_constu: 1890878197237214971; DW_OP_constu: 9123704; DW_OP_breg15 (r15): 0; DW_OP_plus; DW_OP_xor; DW_OP_xor; DW_OP_or) ~~~ 根据官方文档解析即可 DW_OP_constu: 2616514329260088143:将一个无符号整数压入堆栈。 DW_OP_constu: 1237891274917891239:将另一个无符号整数压入堆栈。 DW_OP_constu: 1892739:将第三个无符号整数压入堆栈。 DW_OP_breg12 (r12): 0:从 r12 这个寄存器中读取一个值,并将其加上偏移量 0。 DW_OP_plus:从堆栈中弹出两个值,相加后再将结果压入堆栈。 DW_OP_xor:从堆栈中弹出两个值,进行异或运算后再将结果压入堆栈。 DW_OP_xor:重复前面的操作,再进行一次异或运算。 DW_OP_constu: 8502251781212277489:将另一个无符号整数压入堆栈。 DW_OP_constu: 1209847170981118947:将另一个无符号整数压入堆栈。 DW_OP_constu: 8971237:将第三个无符号整数压入堆栈。 DW_OP_breg13 (r13): 0:从 r13 这个寄存器中读取一个值,并将其加上偏移量 0。 DW_OP_plus:从堆栈中弹出两个值,相加后再将结果压入堆栈。 DW_OP_xor:从堆栈中弹出两个值,进行异或运算后再将结果压入堆栈。 DW_OP_xor:重复前面 ...重复 代码解密 ~~~python def decrypt(): r15 = (8722213363631027234 ^ 1890878197237214971) - 9123704 r14 = (2451795628338718684 ^ 1098791727398412397) - 1512312 r13 = (8502251781212277489 ^ 1209847170981118947) - 8971237 r12 = (2616514329260088143 ^ 1237891274917891239) - 1892739 print(hex(r12)) print(hex(r13)) print(hex(r14)) print(hex(r15)) import binascii hexstring = "65363039656662352d653730652d346539342d616336392d6163333164393663" print("flag{" + binascii.unhexlify(hexstring).decode(encoding="utf-8") + "3861}") # print(binascii.unhexlify(hex(r15)[2:]).decode('utf-8'), end='') # print(binascii.unhexlify(hex(r14)[2:]).decode('utf-8'), end='') # print(binascii.unhexlify(hex(r13)[2:]).decode('utf-8'), end=' ') # print(binascii.unhexlify(hex(r12)[2:]).decode('utf-8'), end=' ') decrypt() ~~~ ## moveAside-2023 https://www.nssctf.cn/problem/4053 demovfuscator:http://hexo.iyzyi.com/2020/01/21/demovfuscator%20docker%E9%95%9C%E5%83%8F/ docker配置好环境(我自己的乱了)。使用./demov -g cfg.dot -o patched FILE(源文件)指令,获取patched再分析,逻辑会稍微清晰些,一些函数能够识别 网上分析的很清楚,读代码太难懂,基本都根据一串特殊字符串猜测 ![image-20240515230849618.png](http://xherlock.top/usr/uploads/2024/05/1847519276.png) 猜测异或,发现有一定规律,低四位都会取反,同时之间重复出现的是- 题目没给完整,flag里面都是十六进制字符和- 首先计算出字符映射 ~~~python alphabet = "0123456789abcdef" # 从IDA获取的42长的byte数组 raw = [0x67, 0x9D, 0x60, 0x66, 0x8A, 0x56, 0x49, 0x50, 0x65, 0x65, 0x60, 0x55, 0x64, 0x5C, 0x65, 0x48, 0x50, 0x51, 0x5C, 0x55, 0x67, 0x51, 0x57, 0x5C, 0x49, 0x67, 0x54, 0x63, 0x5C, 0x54, 0x62, 0x52, 0x56, 0x54, 0x54, 0x50, 0x49, 0x53, 0x52, 0x52, 0x56, 0x8C] flag_dict = {0x67:'f', 0x9d:'l', 0x60:'a', 0x66:'g', 0x8a:'{', 0x8c:'}', 0x5c:'-'} # 遍历查看{括号中的可能内容} for j in range(42): print(f"{hex(raw[j])}:", end=' ') # 0x5c对应'-' if raw[j] in flag_dict.keys(): print(flag_dict[raw[j]]) continue # 遍历0x00-0xff 只取其中的奇数作亦或 for i in range(1, 256, 2): s = chr(raw[j] ^ i) #在字母表内 而且运算先后 低1,2,3bit位相同 if s in alphabet and ord(s) & 0xe == raw[j] & 0xe and s not in flag_dict.values(): print(s, end=' ') print() # {103: 'f', 157: 'l', 96: 'a', 102: 'g', 138: '{', 140: '}', 92: '-', 86: '7', 73: '8', 80: '1', 101: ['d', '4'], 85: ['d', '4'], 100: ['e', '5'], 72: '9', 81: '0', 87: '6', 84: ['e', '5'], 99: ['b', '2'], 98: ['c', '3'], 82: ['c', '3'], 83: ['b', '2']} ~~~ 接着调试,在strcmp调用前断点,会看到比较的两个字符,可以看输入发生什么变化。 输入2345(因为这四个不确定),如下图可以看到传入的两个字符地址 ![image-20240516100435885.png](http://xherlock.top/usr/uploads/2024/05/1682756680.png) 经查看一个0x67(42字节第一个)一个0x53(83),所以2对应83,即b对应99。以此类推得到flag ## baby_tree 2022 https://www.nssctf.cn/problem/2341 查询了资料可知是swift代码转换成的ast,但没找到什么方法可以还原源代码,只能硬分析代码了 代码太长,因此搜了很多关键词,最后搜比较==的时候搜到了下方一大串字符值 ![image-20240516110534271.png](http://xherlock.top/usr/uploads/2024/05/3991456609.png) 其中变量名为b(525行那里) 因此还是需要分析出函数的逻辑,首先ast由一个top\_level\_code\_decl(里面调用了check)和func_decl(check)组成,很明显check传入两个字符串参数 ![image-20240516144034922.png](http://xherlock.top/usr/uploads/2024/05/4093382119.png) 下面逐个分析:先分析主函数(调用了check),首先有一个大的if判断,具体翻译的代码是len(CommandLine.arguments)>=2,有点类似c主函数 ~~~swift (binary_expr type='Bool' location=re.swift:16:32 range=[re.swift:16:4 - line:16:35] nothrow (dot_syntax_call_expr implicit type='(Int, Int) -> Bool' location=re.swift:16:32 range=[re.swift:16:32 - line:16:32] nothrow (declref_expr type='(Int.Type) -> (Int, Int) -> Bool' location=re.swift:16:32 range=[re.swift:16:32 - line:16:32] decl=Swift.(file).Int extension.>= function_ref=single) // 这里声明了比较两个int,且大于等于 (argument_list implicit (argument (type_expr implicit type='Int.Type' location=re.swift:16:32 range=[re.swift:16:32 - line:16:32] typerepr='Int')) )) (argument_list implicit (argument (member_ref_expr type='Int' location=re.swift:16:26 range=[re.swift:16:4 - line:16:26] decl=Swift.(file).Array extension.count [with (substitution_map generic_signature=<Element> (substitution Element -> String))] (load_expr implicit type='[String]' location=re.swift:16:16 range=[re.swift:16:4 - line:16:16] (member_ref_expr type='@lvalue [String]' location=re.swift:16:16 range=[re.swift:16:4 - line:16:16] decl=Swift.(file).CommandLine.arguments (type_expr type='CommandLine.Type' location=re.swift:16:4 range=[re.swift:16:4 - line:16:4] typerepr='CommandLine'))))) (argument (integer_literal_expr type='Int' location=re.swift:16:35 range=[re.swift:16:35 - line:16:35] value=2 builtin_initializer=Swift.(file).Int.init(_builtinIntegerLiteral:) initializer=**NULL**)) )) ~~~ 满足条件进入下一个逻辑:data=CommandLine.arguments[1] ~~~swift (pattern_binding_decl range=[re.swift:17:5 - line:17:39] (pattern_named type='String' 'data') // 定义参数名data Original init: (subscript_expr type='<null>' (member_ref_expr type='@lvalue [String]' location=re.swift:17:28 range=[re.swift:17:16 - line:17:28] decl=Swift.(file).CommandLine.arguments (type_expr type='CommandLine.Type' location=re.swift:17:16 range=[re.swift:17:16 - line:17:16] typerepr='CommandLine')) (argument_list (argument (integer_literal_expr type='Int' location=re.swift:17:38 range=[re.swift:17:38 - line:17:38] value=1 builtin_initializer=Swift.(file).Int.init(_builtinIntegerLiteral:) initializer=**NULL**)) )) // 取CommandLine.arguments[1] Processed init: (load_expr implicit type='String' location=re.swift:17:37 range=[re.swift:17:16 - line:17:39] (subscript_expr type='@lvalue String' location=re.swift:17:37 range=[re.swift:17:16 - line:17:39] decl=Swift.(file).Array extension.subscript(_:) [with (substitution_map generic_signature=<Element> (substitution Element -> String))] (inout_expr implicit type='inout Array<String>' location=re.swift:17:16 range=[re.swift:17:16 - line:17:28] (member_ref_expr type='@lvalue [String]' location=re.swift:17:28 range=[re.swift:17:16 - line:17:28] decl=Swift.(file).CommandLine.arguments (type_expr type='CommandLine.Type' location=re.swift:17:16 range=[re.swift:17:16 - line:17:16] typerepr='CommandLine'))) (argument_list (argument (integer_literal_expr type='Int' location=re.swift:17:38 range=[re.swift:17:38 - line:17:38] value=1 builtin_initializer=Swift.(file).Int.init(_builtinIntegerLiteral:) initializer=**NULL**)) )))) (var_decl range=[re.swift:17:9 - line:17:9] "data" type='String' interface type='String' access=fileprivate let readImpl=stored immutable) ~~~ 然后又定义了String key='345y' ~~~swift (pattern_binding_decl range=[re.swift:18:5 - line:18:15] (pattern_named type='String' 'key') Original init: (string_literal_expr type='String' location=re.swift:18:15 range=[re.swift:18:15 - line:18:15] encoding=utf8 value="345y" builtin_initializer=Swift.(file).String extension.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) initializer=**NULL**) Processed init: (string_literal_expr type='String' location=re.swift:18:15 range=[re.swift:18:15 - line:18:15] encoding=utf8 value="345y" builtin_initializer=Swift.(file).String extension.init(_builtinStringLiteral:utf8CodeUnitCount:isASCII:) initializer=**NULL**)) (var_decl range=[re.swift:18:9 - line:18:9] "key" type='String' interface type='String' access=fileprivate let readImpl=stored immutable) ~~~ 接着定义了Bool result=check(data,key) ~~~swift (pattern_binding_decl range=[re.swift:19:5 - line:19:33] (pattern_named type='Bool' 'result') Original init: (call_expr type='Bool' location=re.swift:19:18 range=[re.swift:19:18 - line:19:33] nothrow (declref_expr type='(String, String) -> Bool' location=re.swift:19:18 range=[re.swift:19:18 - line:19:18] decl=re.(file).check@re.swift:1:6 function_ref=single) (argument_list (argument (declref_expr type='String' location=re.swift:19:24 range=[re.swift:19:24 - line:19:24] decl=re.(file).top-level code.data@re.swift:17:9 function_ref=unapplied)) (argument (declref_expr type='String' location=re.swift:19:30 range=[re.swift:19:30 - line:19:30] decl=re.(file).top-level code.key@re.swift:18:9 function_ref=unapplied)) )) Processed init: (call_expr type='Bool' location=re.swift:19:18 range=[re.swift:19:18 - line:19:33] nothrow (declref_expr type='(String, String) -> Bool' location=re.swift:19:18 range=[re.swift:19:18 - line:19:18] decl=re.(file).check@re.swift:1:6 function_ref=single) (argument_list (argument (declref_expr type='String' location=re.swift:19:24 range=[re.swift:19:24 - line:19:24] decl=re.(file).top-level code.data@re.swift:17:9 function_ref=unapplied)) (argument (declref_expr type='String' location=re.swift:19:30 range=[re.swift:19:30 - line:19:30] decl=re.(file).top-level code.key@re.swift:18:9 function_ref=unapplied)) ))) (var_decl range=[re.swift:19:9 - line:19:9] "result" type='Bool' interface type='Bool' access=fileprivate let readImpl=stored immutable) ~~~ 最后一段貌似是打印result ~~~swift (call_expr type='()' location=re.swift:20:5 range=[re.swift:20:5 - line:20:17] nothrow (declref_expr type='(Any..., String, String) -> ()' location=re.swift:20:5 range=[re.swift:20:5 - line:20:5] decl=Swift. (file).print(_:separator:terminator:) function_ref=single) (argument_list labels=_:separator:terminator: (argument (vararg_expansion_expr implicit type='Any...' location=re.swift:20:11 range=[re.swift:20:11 - line:20:11] (array_expr implicit type='Any...' location=re.swift:20:11 range=[re.swift:20:11 - line:20:11] initializer=**NULL** (erasure_expr implicit type='Any' location=re.swift:20:11 range=[re.swift:20:11 - line:20:11] (declref_expr type='Bool' location=re.swift:20:11 range=[re.swift:20:11 - line:20:11] decl=re.(file).top-level code.result@re.swift:19:9 function_ref=unapplied))))) (argument label=separator (default_argument_expr implicit type='String' location=re.swift:20:10 range=[re.swift:20:10 - line:20:10] default_args_owner=Swift.(file).print(_:separator:terminator:) param=1)) (argument label=terminator (default_argument_expr implicit type='String' location=re.swift:20:10 range=[re.swift:20:10 - line:20:10] default_args_owner=Swift.(file).print(_:separator:terminator:) param=2)) )) ~~~ 分析check,传入参数为字符串encoded和keyValue(发现个妙处,可以根据行号提示来划分代码逻辑),第二行定义了UInt8 b[UTF8View(encoded)] ~~~swift (pattern_binding_decl range=[re.swift:2:5 - line:2:33] (pattern_named type='[UInt8]' 'b') // 变量名b Original init: (call_expr type='[UInt8]' location=re.swift:2:19 range=[re.swift:2:13 - line:2:33] nothrow (constructor_ref_call_expr type='(String.UTF8View) -> [UInt8]' location=re.swift:2:19 range=[re.swift:2:13 - line:2:19] nothrow (declref_expr implicit type='(Array<UInt8>.Type) -> (String.UTF8View) -> Array<UInt8>' location=re.swift:2:19 range=[re.swift:2:19 - line:2:19] decl=Swift.(file).Array extension.init(_:) [with (substitution_map generic_signature=<Element, S where Element == S.Element, S : Sequence> (substitution Element -> UInt8) (substitution S -> String.UTF8View))] function_ref=single) // Array (argument_list implicit (argument (type_expr type='[UInt8].Type' location=re.swift:2:13 range=[re.swift:2:13 - line:2:19] typerepr='[UInt8]')) )) // UInt8类型 (argument_list (argument (member_ref_expr type='String.UTF8View' location=re.swift:2:29 range=[re.swift:2:21 - line:2:29] decl=Swift.(file).String extension.utf8 (declref_expr type='String' location=re.swift:2:21 range=[re.swift:2:21 - line:2:21] decl=re.(file).check(_:_:).encoded@re.swift:1:14 function_ref=unapplied))) )) Processed init: (call_expr type='[UInt8]' location=re.swift:2:19 range=[re.swift:2:13 - line:2:33] nothrow (constructor_ref_call_expr type='(String.UTF8View) -> [UInt8]' location=re.swift:2:19 range=[re.swift:2:13 - line:2:19] nothrow (declref_expr implicit type='(Array<UInt8>.Type) -> (String.UTF8View) -> Array<UInt8>' location=re.swift:2:19 range=[re.swift:2:19 - line:2:19] decl=Swift.(file).Array extension.init(_:) [with (substitution_map generic_signature=<Element, S where Element == S.Element, S : Sequence> (substitution Element -> UInt8) (substitution S -> String.UTF8View))] function_ref=single) (argument_list implicit (argument (type_expr type='[UInt8].Type' location=re.swift:2:13 range=[re.swift:2:13 - line:2:19] typerepr='[UInt8]')) )) (argument_list (argument (member_ref_expr type='String.UTF8View' location=re.swift:2:29 range=[re.swift:2:21 - line:2:29] decl=Swift.(file).String extension.utf8 (declref_expr type='String' location=re.swift:2:21 range=[re.swift:2:21 - line:2:21] decl=re.(file).check(_:_:).encoded@re.swift:1:14 function_ref=unapplied))) ))) (var_decl range=[re.swift:2:9 - line:2:9] "b" type='[UInt8]' interface type='[UInt8]' access=private readImpl=stored writeImpl=stored readWriteImpl=stored) ~~~ 接着第三行又定义了同样的UInt8 k=[UTF8View(keyValue)] ~~~swift (pattern_binding_decl range=[re.swift:3:5 - line:3:34] (pattern_named type='[UInt8]' 'k') Original init: (call_expr type='[UInt8]' location=re.swift:3:19 range=[re.swift:3:13 - line:3:34] nothrow (constructor_ref_call_expr type='(String.UTF8View) -> [UInt8]' location=re.swift:3:19 range=[re.swift:3:13 - line:3:19] nothrow (declref_expr implicit type='(Array<UInt8>.Type) -> (String.UTF8View) -> Array<UInt8>' location=re.swift:3:19 range=[re.swift:3:19 - line:3:19] decl=Swift.(file).Array extension.init(_:) [with (substitution_map generic_signature=<Element, S where Element == S.Element, S : Sequence> (substitution Element -> UInt8) (substitution S -> String.UTF8View))] function_ref=single) (argument_list implicit (argument (type_expr type='[UInt8].Type' location=re.swift:3:13 range=[re.swift:3:13 - line:3:19] typerepr='[UInt8]')) )) (argument_list (argument (member_ref_expr type='String.UTF8View' location=re.swift:3:30 range=[re.swift:3:21 - line:3:30] decl=Swift.(file).String extension.utf8 (declref_expr type='String' location=re.swift:3:21 range=[re.swift:3:21 - line:3:21] decl=re.(file).check(_:_:).keyValue@re.swift:1:33 function_ref=unapplied))) )) Processed init: (call_expr type='[UInt8]' location=re.swift:3:19 range=[re.swift:3:13 - line:3:34] nothrow (constructor_ref_call_expr type='(String.UTF8View) -> [UInt8]' location=re.swift:3:19 range=[re.swift:3:13 - line:3:19] nothrow (declref_expr implicit type='(Array<UInt8>.Type) -> (String.UTF8View) -> Array<UInt8>' location=re.swift:3:19 range=[re.swift:3:19 - line:3:19] decl=Swift.(file).Array extension.init(_:) [with (substitution_map generic_signature=<Element, S where Element == S.Element, S : Sequence> (substitution Element -> UInt8) (substitution S -> String.UTF8View))] function_ref=single) (argument_list implicit (argument (type_expr type='[UInt8].Type' location=re.swift:3:13 range=[re.swift:3:13 - line:3:19] typerepr='[UInt8]')) )) (argument_list (argument (member_ref_expr type='String.UTF8View' location=re.swift:3:30 range=[re.swift:3:21 - line:3:30] decl=Swift.(file).String extension.utf8 (declref_expr type='String' location=re.swift:3:21 range=[re.swift:3:21 - line:3:21] decl=re.(file).check(_:_:).keyValue@re.swift:1:33 function_ref=unapplied))) ))) (var_decl range=[re.swift:3:9 - line:3:9] "k" type='[UInt8]' interface type='[UInt8]' access=private readImpl=stored writeImpl=stored readWriteImpl=stored) ~~~ 第四行分别定义了UInt8类型的r0、r1、r2、r3 ~~~swift (pattern_binding_decl range=[re.swift:4:5 - line:4:25] (pattern_typed type='UInt8' (pattern_named type='UInt8' 'r0') (type_ident (component id='UInt8' bind=Swift.(file).UInt8))) (pattern_typed type='UInt8' (pattern_named type='UInt8' 'r1') (type_ident (component id='UInt8' bind=Swift.(file).UInt8))) (pattern_typed type='UInt8' (pattern_named type='UInt8' 'r2') (type_ident (component id='UInt8' bind=Swift.(file).UInt8))) (pattern_typed type='UInt8' (pattern_named type='UInt8' 'r3') (type_ident (component id='UInt8' bind=Swift.(file).UInt8)))) (var_decl range=[re.swift:4:9 - line:4:9] "r0" type='UInt8' interface type='UInt8' access=private readImpl=stored writeImpl=stored readWriteImpl=stored) (var_decl range=[re.swift:4:13 - line:4:13] "r1" type='UInt8' interface type='UInt8' access=private readImpl=stored writeImpl=stored readWriteImpl=stored) (var_decl range=[re.swift:4:17 - line:4:17] "r2" type='UInt8' interface type='UInt8' access=private readImpl=stored writeImpl=stored readWriteImpl=stored) (var_decl range=[re.swift:4:21 - line:4:21] "r3" type='UInt8' interface type='UInt8' access=private readImpl=stored writeImpl=stored readWriteImpl=stored) ~~~ 第五行是一个for循环,从0到b.count-4 ~~~swift (for_each_stmt range=[re.swift:5:5 - line:12:5] (pattern_named type='Int' 'i') (pattern_named type='Int' 'i') (binary_expr type='ClosedRange<Int>' location=re.swift:5:15 range=[re.swift:5:14 - line:5:26] nothrow (dot_syntax_call_expr implicit type='(Int, Int) -> ClosedRange<Int>' location=re.swift:5:15 range=[re.swift:5:15 - line:5:15] nothrow (declref_expr type='(Int.Type) -> (Int, Int) -> ClosedRange<Int>' location=re.swift:5:15 range=[re.swift:5:15 - line:5:15] decl=Swift.(file).Comparable extension.... [with (substitution_map generic_signature=<Self where Self : Comparable> (substitution Self -> Int))] function_ref=double) (argument_list implicit (argument (type_expr implicit type='Int.Type' location=re.swift:5:15 range=[re.swift:5:15 - line:5:15] typerepr='Int')) )) (argument_list implicit (argument (integer_literal_expr type='Int' location=re.swift:5:14 range=[re.swift:5:14 - line:5:14] value=0 builtin_initializer=Swift.(file).Int.init(_builtinIntegerLiteral:) initializer=**NULL**)) // 0开始 (argument (binary_expr type='Int' location=re.swift:5:25 range=[re.swift:5:18 - line:5:26] nothrow (dot_syntax_call_expr implicit type='(Int, Int) -> Int' location=re.swift:5:25 range=[re.swift:5:25 - line:5:25] nothrow (declref_expr type='(Int.Type) -> (Int, Int) -> Int' location=re.swift:5:25 range=[re.swift:5:25 - line:5:25] decl=Swift.(file).Int extension.- function_ref=double) // 类比上面的大于等于,可知这里做了减法,即b.count-4 (argument_list implicit (argument (type_expr implicit type='Int.Type' location=re.swift:5:25 range=[re.swift:5:25 - line:5:25] typerepr='Int')) )) (argument_list implicit (argument (member_ref_expr type='Int' location=re.swift:5:20 range=[re.swift:5:18 - line:5:20] decl=Swift.(file).Array extension.count [with (substitution_map generic_signature=<Element> (substitution Element -> UInt8))] (load_expr implicit type='[UInt8]' location=re.swift:5:18 range=[re.swift:5:18 - line:5:18] (declref_expr type='@lvalue [UInt8]' location=re.swift:5:18 range=[re.swift:5:18 - line:5:18] decl=re.(file).check(_:_:).b@re.swift:2:9 function_ref=unapplied)))) // 这里算了b.count (argument (integer_literal_expr type='Int' location=re.swift:5:26 range=[re.swift:5:26 - line:5:26] value=4 builtin_initializer=Swift.(file).Int.init(_builtinIntegerLiteral:) initializer=**NULL**)) //这里4 ))) )) (var_decl implicit range=[re.swift:5:11 - line:5:11] "$i$generator" type='ClosedRange<Int>.Iterator' interface type='ClosedRange<Int>.Iterator' access=private readImpl=stored writeImpl=stored readWriteImpl=stored) ~~~ 下一个块很长,但总体看来其实就是第6-11行代码,均为赋值表达式 ![image-20240516192424988.png](http://xherlock.top/usr/uploads/2024/05/4142639455.png) **第6行代码**,两个tuple,第一个r0、r1、r2、r3, ![image-20240516192725554.png](http://xherlock.top/usr/uploads/2024/05/213654601.png) 第二个发现根据下标取值,得到b[0](这里写的i,i正好0)、b[1]、b[2]、b[3] ![image-20240516192904793.png](http://xherlock.top/usr/uploads/2024/05/1499355587.png) **第7行代码**:首先取b[i+0] ![image-20240516193648621.png](http://xherlock.top/usr/uploads/2024/05/1596550008.png) 接着最外层做了r2和另一个数异或 ![image-20240516194227454.png](http://xherlock.top/usr/uploads/2024/05/1179252013.png) 再往里是0xff和另一个数与计算 ![image-20240516194424711.png](http://xherlock.top/usr/uploads/2024/05/2580176347.png) 再往里是k[0]和另一个数求和 ![image-20240516194557282.png](http://xherlock.top/usr/uploads/2024/05/1232397900.png) 最后一层r0右移4位 ![image-20240516194709062.png](http://xherlock.top/usr/uploads/2024/05/3426161345.png) 综上第7行代码表达式为 b[i+0]=r2^(0xff&(k[0]+(r0>>4))),真的挺复杂 以此类推得到所有代码 ~~~c bool check(string encoded, string keyValue) { UInt8 b=[UTF8View(encoded)]; UInt8 k=[UTF8View(keyValue)]; UInt8 r0, r1, r2, r3; for (int i = 0; i < len(b)-4; i++) { r0, r1, r2, r3 = b[i+0], b[i+1], b[i+2], b[i+3]; b[i+0]=r2^(0xff&(k[0]+(r0>>4))); b[i+1]=r3^(0xff&(k[1]+(r1>>2))); b[i+2]=r0^k[2]; b[i+3]=r1^k[3]; k[0], k[1], k[2], k[3] = k[1], k[2], k[3], k[0]; } return b == [88, 35, 88, 225, 7, 201, 57, 94, 77, 56, 75, 168, 72, 218, 64, 91, 16, 101, 32, 207, 73, 130, 74, 128, 76, 201, 16, 248, 41, 205, 103, 84, 91, 99, 79, 202, 22, 131, 63, 255, 20, 16] } ~~~ python正则提取比较的值: ~~~python import re with open('./baby_tree.ast') as f: content = f.read() results = re.findall(r"(?<=line:13:).*(?<=value=)([\d]{1,3})", content) results = list(map(lambda x: int(x), results)) # [88, 35, 88, 225, 7, 201, 57, 94, 77, 56, 75, 168, 72, 218, 64, 91, 16, 101, 32, 207, 73, 130, 74, 128, 76, 201, 16, 248, 41, 205, 103, 84, 91, 99, 79, 202, 22, 131, 63, 255, 20, 16] ~~~ 编写脚本逆向 ~~~python import re with open('./baby_tree.ast') as f: content = f.read() results = re.findall(r"(?<=line:13:).*(?<=value=)([\d]{1,3})", content) results = list(map(lambda x: int(x), results)) print(results, len(results)) k = [ord(i) for i in "5y34"] # 38次左移结果 print(k) b = results for i in range(len(b)-4, -1, -1): r1 = b[i+3] ^ k[3] r0 = b[i+2] ^ k[2] r3 = b[i+1] ^ (0xff & (k[1] + (r1 >> 2))) r2 = b[i] ^ (0xff & (k[0] + (r0 >> 4))) b[i], b[i+1], b[i+2], b[i+3] = r0, r1, r2, r3 k[0], k[1], k[2], k[3] = k[3], k[0], k[1], k[2] flag = '' for i in b: flag += chr(i) print(flag) ~~~ ## happymath 2022 https://www.nssctf.cn/problem/2405 ~~~c int __fastcall main(int argc, const char **argv, const char **envp) { __int64 v3; // rax __int64 v4; // rbx __int64 v5; // rdx char v6; // cl __int64 v7; // r10 int v8; // edi unsigned int v9; // eax __int64 i; // r9 char v11; // cl unsigned int v12; // edx unsigned int v13; // r9d unsigned int v14; // r8d unsigned int v15; // r8d unsigned int v16; // r8d int v17; // r8d int v18; // r8d int v19; // r9d int v20; // r9d scanf("%32s", input); v3 = -1i64; do ++v3; while ( input[v3] ); if ( (_DWORD)v3 == 32 ) // 长度32 { v4 = 0i64; v5 = 0i64; while ( 1 ) { v6 = input[v5]; if ( (unsigned __int8)(v6 - 97) > 5u && (unsigned __int8)(v6 - 48) > 9u )// 0-9a-f break; if ( ++v5 >= 32 ) { v7 = 0i64; v8 = 4; while ( 1 ) { v9 = -1; for ( i = v4; i < v8; v9 = dword_140024630[v7 + (~(v11 & v9) & (unsigned __int8)(v11 | v9))] ^ (v9 >> 8) ) v11 = input[i++]; v12 = (v9 | ~*(_DWORD *)((char *)&unk_140024610 + v4)) & ~(v9 & ~*(_DWORD *)((char *)&unk_140024610 + v4)); v13 = ((((((((v12 | (v12 >> 1)) >> 2) | v12 | (v12 >> 1)) >> 4) | ((v12 | (v12 >> 1)) >> 2) | v12 | (v12 >> 1)) >> 8) | ((((v12 | (v12 >> 1)) >> 2) | v12 | (v12 >> 1)) >> 4) | ((v12 | (v12 >> 1)) >> 2) | v12 | (v12 >> 1)) >> 16) | ((((((v12 | (v12 >> 1)) >> 2) | v12 | (v12 >> 1)) >> 4) | ((v12 | (v12 >> 1)) >> 2) | v12 | (v12 >> 1)) >> 8) | ((((v12 | (v12 >> 1)) >> 2) | v12 | (v12 >> 1)) >> 4) | ((v12 | (v12 >> 1)) >> 2) | v12 | (v12 >> 1); v14 = ((v12 & (v13 | (v13 >> 1)) & ~(v13 & (v13 >> 1))) >> 1) | v12 & (v13 | (v13 >> 1)) & ~(v13 & (v13 >> 1)); v15 = (((v14 >> 2) | v14) >> 4) | (v14 >> 2) | v14; v16 = (((v15 >> 8) | v15) >> 16) | (v15 >> 8) | v15; v17 = (4 * ((2 * v16) | v16)) | (2 * v16) | v16; v18 = (((16 * v17) | v17) << 8) | (16 * v17) | v17; v19 = (4 * ((2 * (v13 & 1)) | v13 & 1)) | (2 * (v13 & 1)) | v13 & 1; v20 = (((16 * v19) | v19) << 8) | (16 * v19) | v19; if ( ((v20 | (v20 << 16)) & (~((v18 << 16) | v18) | v18 & 1)) != 0 ) break; // 不能break v8 += 4; v7 += 256i64; v4 += 4i64; if ( v8 >= 36 ) { printf("Your flag is flag{%s}\n", input); return 0; } } break; } } } printf("Wrong!"); return 0; } ~~~ 直接爆破,注意unk_140024610也需要是4字节(卡了半天) ~~~c #include<iostream> using namespace std; unsigned int dword_140024630[2048] = {略}; unsigned int unk_140024610[8] = { 0x95F553DC, 0x1D3B4E23, 0x0B913414, 0x6B9E4582, 0x35E7621B, 0xFC86887F, 0x26DEA0FB, 0x87044CAA }; int calc(int input[], int v4, int v7, int v8) { int v11; unsigned int v9 = -1; for (int i = v4; i < v8; v9 = dword_140024630[v7 + (~(v11 & v9) & (v11 | v9) & 0xff)] ^ (v9 >> 8) ) v11 = input[i++]; unsigned int v12 = (v9 | ~unk_140024610[v4/4]) & ~(v9 & ~unk_140024610[v4/4]); unsigned int v13 = ((((((((v12 | (v12 >> 1)) >> 2) | v12 | (v12 >> 1)) >> 4) | ((v12 | (v12 >> 1)) >> 2) | v12 | (v12 >> 1)) >> 8) | ((((v12 | (v12 >> 1)) >> 2) | v12 | (v12 >> 1)) >> 4) | ((v12 | (v12 >> 1)) >> 2) | v12 | (v12 >> 1)) >> 16) | ((((((v12 | (v12 >> 1)) >> 2) | v12 | (v12 >> 1)) >> 4) | ((v12 | (v12 >> 1)) >> 2) | v12 | (v12 >> 1)) >> 8) | ((((v12 | (v12 >> 1)) >> 2) | v12 | (v12 >> 1)) >> 4) | ((v12 | (v12 >> 1)) >> 2) | v12 | (v12 >> 1); unsigned int v14 = ((v12 & (v13 | (v13 >> 1)) & ~(v13 & (v13 >> 1))) >> 1) | v12 & (v13 | (v13 >> 1)) & ~(v13 & (v13 >> 1)); unsigned int v15 = (((v14 >> 2) | v14) >> 4) | (v14 >> 2) | v14; unsigned int v16 = (((v15 >> 8) | v15) >> 16) | (v15 >> 8) | v15; int v17 = (4 * ((2 * v16) | v16)) | (2 * v16) | v16; int v18 = (((16 * v17) | v17) << 8) | (16 * v17) | v17; int v19 = (4 * ((2 * (v13 & 1)) | v13 & 1)) | (2 * (v13 & 1)) | v13 & 1; int v20 = (((16 * v19) | v19) << 8) | (16 * v19) | v19; if ( ((v20 | (v20 << 16)) & (~((v18 << 16) | v18) | v18 & 1)) != 0 ) return 0; else return 1; } void loop(int input[], int v4, int v7, int v8) { for (int k1 = 48; k1 < 103; k1++) { for (int k2 = 48; k2 < 103; k2++) { for (int k3 = 48; k3 < 103; k3++) { for (int k4 = 48; k4 < 103; k4++) { input[v4] = k1, input[v4+1] = k2, input[v4+2] =k3, input[v4+3] = k4; int result = calc(input, v4, v7, v8); if (result) { printf("%c%c%c%c", input[v4], input[v4+1], input[v4+2], input[v4+3]); return; } if (k4 == 57) k4 = 96; } if (k3 == 57) k3 = 96; } if (k2 == 57) k2 = 96; } if (k1 == 57) k1 = 96; } } int main() { int v4 = 0; int v5 = 0; int input[32]; while ( 1 ) { if ( ++v5 >= 32 ) { int v7 = 0; int v8 = 4; while (1) { loop(input, v4, v7, v8); // 每次4个字符 v8 += 4; v7 += 256; v4 += 4; if ( v8 >= 36 ) return 0; } printf("\n"); } } } ~~~ ## hana 易语言代码逆向,比较难懂,最好动态调试和静态分析相结合分析主函数 首先使用ida的易语言反编译器插件,将一些函数转为中文函数名,更方便理解,由于有输出,可以先定位**标准输出**,然后向上找 这是网上的代码: ![image-20240517172601741.png](http://xherlock.top/usr/uploads/2024/05/1713145057.png) 这是调试分析过程中的代码,基本对应 ![image-20240517180223840.png](http://xherlock.top/usr/uploads/2024/05/3523844365.png) 使用如下代码加密我们的输入 ~~~易语言 〈字节集〉 加密数据 (字节集 字节集数据,文本型 密码文本,[整数型 加密算法]) - 数据操作支持库一->数据加解密 ~~~ 因此上述代码是使用RC4算法加密,key为"Wrong!",加密的是输入的字符串转成的字节集(到字节集) ![image-20240517180345119.png](http://xherlock.top/usr/uploads/2024/05/1329427875.png) 字节集结构为 ~~~c typdef struct 字节集 { int unkown; int length; char* bytes; }; ~~~ 再往下分析可以看到字节集比较,传入的参数分别为:比较的两个字节集和文本长度 ![image-20240517181110104.png](http://xherlock.top/usr/uploads/2024/05/1257506514.png) 其中0x667BD8是先前RC4加密后的字节集地址+8(正好是结构体的bytes),因此查看0x4804CB是比较的对象,直接转为字符串,转为字节格式 使用Crypto包下的ARC4解密即可 ~~~python from Crypto.Cipher import ARC4 s = b"\x56\xEC\xA0\xDC\x57\x07\xF4\xA3\xE9\x77\xBF\x93\xBC\x86\x52\xA5\x14\x6A\xA5\xBD\xB5\xD2\x7F\x0B\x9B\x67\x1D\x08\xEF\xC9\x32\x5D\x43\xED\x1E\x01\x4B\x7B" key = bytes("Wrong!", encoding="utf-8") enc = ARC4.new(key) flag = enc.decrypt(s) print(flag) # b'flag{08360c3f9f994e199427d9c7ed14ef23}' ~~~ ## easyCpp c++ transfrom:https://blog.csdn.net/zhang___bo/article/details/119389457 **transform(first,last,result,op);**//first是容器的首迭代器,last为容器的末迭代器,result为存放结果的容器,op为要进行操作的一元函数对象或sturct、class。 本题中可以直接双击查看具体内容 ~~~c++ int __fastcall main(int argc, const char **argv, const char **envp) { __int64 v3; // rdx __int64 v4; // rdx __int64 v5; // rdx __int64 v6; // rdx __int64 v7; // r12 __int64 v8; // rbx __int64 v9; // rax __int64 v10; // rdx int v11; // eax int v12; // r8d int v13; // r9d int v14; // r12d int v15; // ebx int v16; // eax int v17; // ecx int v18; // r8d int v19; // r9d unsigned int *v20; // rax int i; // [rsp+1Ch] [rbp-174h] int j; // [rsp+20h] [rbp-170h] char v24[32]; // [rsp+30h] [rbp-160h] BYREF char v25[32]; // [rsp+50h] [rbp-140h] BYREF char v26[32]; // [rsp+70h] [rbp-120h] BYREF char v27[32]; // [rsp+90h] [rbp-100h] BYREF char v28[32]; // [rsp+B0h] [rbp-E0h] BYREF __int64 v29[4]; // [rsp+D0h] [rbp-C0h] BYREF __int64 v30[4]; // [rsp+F0h] [rbp-A0h] BYREF _DWORD v31[18]; // [rsp+110h] [rbp-80h] BYREF unsigned __int64 v32; // [rsp+158h] [rbp-38h] v32 = __readfsqword(0x28u); std::vector<int>::vector(v24, argv, envp); std::vector<int>::vector(v25, argv, v3); std::vector<int>::vector(v26, argv, v4); std::vector<int>::vector(v27, argv, v5); std::vector<int>::vector(v28, argv, v6); for ( i = 0; i <= 15; ++i ) { scanf("%d", &v31[i]); // 输入 std::vector<int>::push_back(v25, &v31[i]); } for ( j = 0; j <= 15; ++j ) { LODWORD(v30[0]) = fib(j);// 生成斐波那契数列16个 std::vector<int>::push_back(v24, v30); } std::vector<int>::push_back(v26, v31); v7 = std::back_inserter<std::vector<int>>(v26);// 存放结果 v8 = std::vector<int>::end(v25); v30[0] = std::vector<int>::begin(v25); v9 = __gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator+(v30, 1LL); std::transform<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,std::back_insert_iterator<std::vector<int>>,main::{lambda(int)#1}>( v9, // v9是存放输入数字容器的开始+1,即从第二个开始 v8, // v8是存放输入数字容器的结尾 v7, v31); std::vector<int>::vector(v29, v8, v10); LODWORD(v8) = std::vector<int>::end(v26); // 前面处理完新的容器 v11 = std::vector<int>::begin(v26); std::accumulate<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,std::vector<int>,main::{lambda(std::vector<int>,int)#2}>( (unsigned int)v30, // 存放输入的容器 v11, v8, (unsigned int)v29, v12, v13); // 深入分析发现是对上一个容器做了逆序操作 std::vector<int>::operator=(v27, v30); std::vector<int>::~vector(v30); std::vector<int>::~vector(v29); if ( (unsigned __int8)std::operator!=<int,std::allocator<int>>(v27, v24) )// 处理后的输入和斐波那契数列比较 { puts("You failed!"); exit(0); } v14 = std::back_inserter<std::vector<int>>(v28); v15 = std::vector<int>::end(v25); v16 = std::vector<int>::begin(v25); std::copy_if<__gnu_cxx::__normal_iterator<int *,std::vector<int>>,std::back_insert_iterator<std::vector<int>>,main::{lambda(int)#3}>( v16, v15, v14, v17, v18, v19); // 这里是输入为奇数的复制到v28 puts("You win!"); printf("Your flag is:flag{"); v29[0] = std::vector<int>::begin(v28); // 之后打印v28 v30[0] = std::vector<int>::end(v28); while ( (unsigned __int8)__gnu_cxx::operator!=<int *,std::vector<int>>(v29, v30) ) { v20 = (unsigned int *)__gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator*(v29); std::ostream::operator<<(&std::cout, *v20); __gnu_cxx::__normal_iterator<int *,std::vector<int>>::operator++(v29); } putchar(125); std::vector<int>::~vector(v28); std::vector<int>::~vector(v27); std::vector<int>::~vector(v26); std::vector<int>::~vector(v25); std::vector<int>::~vector(v24); return 0; } ~~~ 解密脚本 ~~~python fib = [1, 1] while len(fib) < 16: fib.append(fib[len(fib)-1]+fib[len(fib)-2]) print(fib, len(fib)) fib = fib[::-1] fib = [i-fib[0] if i != fib[0] else i for i in fib] print(fib) flag = 'flag{' flag += str(fib[0]) for i in range(1, len(fib)): if fib[i] % 2: flag += str(fib[i]) flag += '}' print(flag) ~~~ ## mfc逆向-200 题目涉及MFC(微软基础类)控件的逆向,由图可知需要获取控件里的信息 ![image-20240521155355401.png](http://xherlock.top/usr/uploads/2024/05/585198395.png) 由于Flag在控件里,需要我们获取句柄,发送消息 首先使用xspy(可以获取运行程序的窗口信息)找到控件值,如下图一串加密字符串 ![image-20240521160134148.png](http://xherlock.top/usr/uploads/2024/05/2402594930.png) 往下找可以看到内部函数onmsg ![image-20240521160326394.png](http://xherlock.top/usr/uploads/2024/05/3852534512.png) ![image-20240521160435987.png](http://xherlock.top/usr/uploads/2024/05/3609593832.png) ![image-20240521160645336.png](http://xherlock.top/usr/uploads/2024/05/143787373.png) 因此重点针对自定义消息,写个c脚本给窗口发送消息 ~~~c #include<stdio.h> #include<string.h> #include<windows.h> int main() { HWND h = FindWindowA("944c8d100f82f0c18b682f63e4dbaa207a2f1e72581c2f1b",NULL); //HWND h = FindWindowA(NULL, "Flag就在控件里"); //HWND h = FindWindowA("944c8d100f82f0c18b682f63e4dbaa207a2f1e72581c2f1b", "Flag就在控件里"); //这里用到两个关键函数,一个是获取窗口句柄函数,第二个就是根据句柄发送消息函数。获取句柄的FindWindowA中第一个可以传入类名,第二个可以传入标题,因为我们两个都有,所以任意一个都可以锁定程序窗口。 if (h) { SendMessage(h, 0x464, NULL, NULL); //发送函数中第二个是区别其他消息的常量值,这里题目用了自定义常量值,所以我们要对应一致。 } getchar(); return 0; } ~~~ 运行发现内容改变 ![image-20240521160831453.png](http://xherlock.top/usr/uploads/2024/05/3687690885.png) 为DES加密,DES解密即可,试了好多加解密工具,发现只有飘云阁一个工具可以解密超过8字节的DES ![image-20240521171153172.png](http://xherlock.top/usr/uploads/2024/05/3083206081.png) thIs_Is_real_kEy_hahaaa 最后修改:2024 年 05 月 21 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 0 如果觉得我的文章对你有用,请随意赞赏