Reverse(十三)
StupidOrangeCat2
直接ida32位(7.7或8.3版本,低的分析不出来反调试代码)分析代码,由关键函数可知其为迷宫问题
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的值是关键,必须要进去才是正确代码
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并赋值的地方
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可以得到真实代码
s=0x402780
for i in range(1989):
ida_bytes.patch_byte(s+i,idc.get_wide_byte(s+i)^32)
在0x402780处C转为code,再右键转为函数,得到真实代码
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
接着进入sub_402F60函数
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解密
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
#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代码
#!/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)
查询可知的用python2运行,分析代码:
- base64.b64decode:base64解密
- zlib.decompress:解压字符串
- marshal.loads:将字节对象转换为值,这里可以知道上一步得到的正是字节码
- exec:很明显是执行代码的函数
要想反编译字节码,先提取到pyc文件中:
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值可以发现没有文件头
随便写一个py,然后编译提取文件头,用16进制编辑器添加即可
import py_compile
py_compile.compile('gen_pyc27.py')
再用uncompyle6反编译,结果提示failed,但是生成了out.py(uncompyle6 -o out.py .out.pyc),特别长
# 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两个指令。其中带有字符常量,因此分析下这些字节码指令
由上分析可知,这是在做字符交换然后相加,最终得到的是字符串
写脚本模拟下栈:
#!/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
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
import opcode
# 查询指令集指令的值
for op in range(len(opcode.opname)):
print('ox%.2X(%.3d): %s' % (op, op, opcode.opname[op]))
运行后直接打印flag
babybc-2021
https://www.nssctf.cn/problem/841
给了个bc文件,没见过,搜了半天以为是bitcomet(种子下载器)打开的,最后发现不对,忙活半天
谷歌大法好,搜到了https://blog.csdn.net/pc153262603/article/details/89553688
博客里说bc后缀的文件是bitcode的二进制格式,可以使用clang编译为可执行文件
clang baby.bc -o baby
ida64分析
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范围
__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数组比较关键
__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
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包来算的
把红色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:重复前面
...重复
代码解密
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再分析,逻辑会稍微清晰些,一些函数能够识别
网上分析的很清楚,读代码太难懂,基本都根据一串特殊字符串猜测
猜测异或,发现有一定规律,低四位都会取反,同时之间重复出现的是-
题目没给完整,flag里面都是十六进制字符和-
首先计算出字符映射
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(因为这四个不确定),如下图可以看到传入的两个字符地址
经查看一个0x67(42字节第一个)一个0x53(83),所以2对应83,即b对应99。以此类推得到flag
baby_tree 2022
https://www.nssctf.cn/problem/2341
查询了资料可知是swift代码转换成的ast,但没找到什么方法可以还原源代码,只能硬分析代码了
代码太长,因此搜了很多关键词,最后搜比较==的时候搜到了下方一大串字符值
其中变量名为b(525行那里)
因此还是需要分析出函数的逻辑,首先ast由一个top_level_code_decl(里面调用了check)和func_decl(check)组成,很明显check传入两个字符串参数
下面逐个分析:先分析主函数(调用了check),首先有一个大的if判断,具体翻译的代码是len(CommandLine.arguments)>=2,有点类似c主函数
(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]
(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'
(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)
(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
(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)]
(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)]
(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
(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
(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行代码,均为赋值表达式
第6行代码,两个tuple,第一个r0、r1、r2、r3,
第二个发现根据下标取值,得到b[0](这里写的i,i正好0)、b[1]、b[2]、b[3]
第7行代码:首先取b[i+0]
接着最外层做了r2和另一个数异或
再往里是0xff和另一个数与计算
再往里是k[0]和另一个数求和
最后一层r0右移4位
综上第7行代码表达式为 b[i+0]=r2^(0xff&(k[0]+(r0>>4))),真的挺复杂
以此类推得到所有代码
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正则提取比较的值:
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]
编写脚本逆向
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
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字节(卡了半天)
#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的易语言反编译器插件,将一些函数转为中文函数名,更方便理解,由于有输出,可以先定位标准输出,然后向上找
这是网上的代码:
这是调试分析过程中的代码,基本对应
使用如下代码加密我们的输入
〈字节集〉 加密数据 (字节集 字节集数据,文本型 密码文本,[整数型 加密算法]) - 数据操作支持库一->数据加解密
因此上述代码是使用RC4算法加密,key为"Wrong!",加密的是输入的字符串转成的字节集(到字节集)
字节集结构为
typdef struct 字节集
{
int unkown;
int length;
char* bytes;
};
再往下分析可以看到字节集比较,传入的参数分别为:比较的两个字节集和文本长度
其中0x667BD8是先前RC4加密后的字节集地址+8(正好是结构体的bytes),因此查看0x4804CB是比较的对象,直接转为字符串,转为字节格式
使用Crypto包下的ARC4解密即可
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。
本题中可以直接双击查看具体内容
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;
}
解密脚本
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(微软基础类)控件的逆向,由图可知需要获取控件里的信息
由于Flag在控件里,需要我们获取句柄,发送消息
首先使用xspy(可以获取运行程序的窗口信息)找到控件值,如下图一串加密字符串
往下找可以看到内部函数onmsg
因此重点针对自定义消息,写个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;
}
运行发现内容改变
为DES加密,DES解密即可,试了好多加解密工具,发现只有飘云阁一个工具可以解密超过8字节的DES
thIs_Is_real_kEy_hahaaa