- 2020.05.05 嗯, 立下flag, 有空一定补其他题
magic
题目:magic
本题大概有三个任务: 1.找到真正的main函数
2.算出运行时间
3.RC4+VM逆向还原
寻找真正的main函数
拿到题, 运行一下, 输出如下:
D:\Downloads>magic.exe flag only appears at a specific time, range [2018-05-19 09:00, 2018-05-21 09:00) Better luck next time :)
|
用IDA打开, 根据交叉引用即可找到真正的main函数。
int sub_4011B0() { char *v0; signed __int64 v2; signed __int64 v3; void *v4; signed int v5; __int64 v6; char v7; char *v8; char v9; signed int v10; int v11; signed __int64 v12; _QWORD *v13; char *v14; size_t v15; __int64 v16; __int64 v17; signed __int64 v18; __int64 v19; size_t v20; int result; char v24; char v25; unsigned __int16 v26;
v0 = &v24; memset(&v24, 0, 0x68uLL); if ( unk_409670 ) GetStartupInfoA((LPSTARTUPINFOA)&unk_409670); _RBX = &unk_409AF8; v2 = *(_QWORD *)(__readgsqword(0x30u) + 8); while ( 1 ) { v3 = _InterlockedCompareExchange((volatile signed __int64 *)&unk_409AF8, v2, 0LL); if ( !v3 ) { v4 = &unk_409AF0; v5 = 0; if ( unk_409AF0 == 1 ) goto LABEL_42; goto LABEL_8; } if ( v2 == v3 ) break; Sleep((unsigned __int64)&unk_409670); } v4 = &unk_409AF0; v5 = 1; if ( unk_409AF0 == 1 ) { LABEL_42: amsg_exit(&unk_409670, &unk_409AF0, v0, 31LL); if ( unk_409AF0 == 1 ) goto LABEL_43; LABEL_11: if ( v5 ) goto LABEL_12; goto LABEL_44; } LABEL_8: if ( unk_409AF0 ) { dword_409004 = 1; } else { unk_409AF0 = 1; initterm(&unk_409670, &unk_409AF0, &unk_40B030, &unk_40B018); } if ( unk_409AF0 != 1 ) goto LABEL_11; LABEL_43: initterm(&unk_409670, &unk_409AF0, &unk_40B010, &unk_40B000); unk_409AF0 = 2; if ( v5 ) goto LABEL_12; LABEL_44: _RAX = 0LL; __asm { xchg rax, [rbx] } LABEL_12: if ( TlsCallback_0 ) TlsCallback_0(&unk_409670, &unk_409AF0, 2LL, 0LL, 0LL); sub_403940(&unk_409670, &unk_409AF0, v0); qword_4096A0 = (__int64)SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)&unk_409670); sub_403DA0(); sub_404850(&unk_409670, &unk_409AF0, v6, nullsub_1); sub_403740(); v7 = 0; qword_4099C8 = 0x400000LL; v8 = (char *)acmdln; if ( acmdln ) { while ( 1 ) { v9 = *v8; if ( *v8 <= 32 ) { if ( !v9 || !(v7 & 1) ) { if ( v9 ) { do ++v8; while ( *v8 && *v8 <= 32 ); } qword_4099C0 = (__int64)v8; break; } v7 = 1; } else if ( v9 == 34 ) { v7 ^= 1u; } ++v8; } } if ( unk_409670 ) { v10 = 10; if ( v25 & 1 ) v10 = v26; dword_405000 = v10; } v11 = dword_409020; v12 = 8LL * (dword_409020 + 1); v13 = malloc((size_t)&unk_409670); v14 = (char *)Code; v15 = (size_t)v13; if ( v11 > 0 ) { v16 = (unsigned int)(v11 - 1); v17 = 0LL; v18 = 8 * v16 + 8; do { v19 = *(_QWORD *)&v14[v17]; v4 = (void *)(strlen(v14) + 1); *(_QWORD *)(v15 + v17) = malloc((size_t)v14); v20 = *(_QWORD *)&v14[v17]; v17 += 8LL; memcpy(v14, v4, v20); } while ( v18 != v17 ); v13 = (_QWORD *)(v15 + v12 - 8); } *v13 = 0LL; Code = v15; sub_403310((__int64)v14, (__int64 *)v4); _initenv = qword_409010; result = main((int)v14, (const char **)v4, (const char **)Code); dword_40900C = result; if ( !dword_409008 ) exit((int)v14); if ( !dword_409004 ) { cexit(); result = dword_40900C; } return result; }
|
进去sub_403310函数:
__int64 __fastcall sub_403310(__int64 a1, __int64 *a2) { __int64 result;
result = (unsigned int)dword_4090A0; if ( !dword_4090A0 ) { dword_4090A0 = 1; result = sub_4032A0(a1, a2); } return result; }
|
__int64 __fastcall sub_4032A0(__int64 a1, __int64 *a2) { __int64 v2; unsigned int i; __int64 *v4;
v2 = qword_4049A0[0]; i = qword_4049A0[0]; if ( LODWORD(qword_4049A0[0]) == -1 ) { for ( i = 0; ; i = v2 ) { v2 = i + 1; if ( !qword_4049A0[v2] ) break; } } if ( i ) { v4 = &qword_4049A0[i]; a2 = &qword_4049A0[i - (unsigned __int64)(i - 1) - 1]; do { ((void (__fastcall *)(__int64, __int64 *))*v4)(a1, a2); --v4; } while ( v4 != a2 ); } return sub_403240(a1, a2, v2, sub_403260); }
|
qword_4049A0存放了两个函数的地址:
qword_4049A0 dq 0FFFFFFFFFFFFFFFFh, 402357h, 404990h, 0
|
进去sub_402357
__int64 __fastcall sub_402357(const char *a1, char **a2) { __int64 result; unsigned int v3;
Time((__int64)a1, (__int64)a2); result = dword_4099D0[0]; if ( !dword_4099D0[0] ) { v3 = strtol(a1, a2, 0) ^ 0xBADD1917; result = sub_402218((__int64)a1, (_DWORD)a2, 105, (unsigned __int64)&unk_405220); } return result; }
|
Time正是这个程序的第一步验证:时间验证
而sub_402218则是输出信息的位置。
以上由调试可知, 就不赘述了。
时间验证
__int64 __fastcall Time(__time64_t *a1, __int64 a2) { __int64 result; unsigned int v3; int v4; unsigned int v5; int i;
v5 = time64(a1); if ( v5 <= 0x5AFFE78F || v5 > 0x5B028A8F ) return 0LL; srand((unsigned int)a1); for ( i = 0; i <= 255; ++i ) byte_405020[i] ^= rand(); v4 = 0; v3 = 0; sub_4027ED((__int64)a1, a2, &v4, (__int64)byte_405020, &v3); if ( v4 == 0x700 ) { dword_4099D0[0] = v3; result = v3; } else { dword_4099D0[0] = 0; result = 0LL; } return result; }
|
time64函数返回当前时间戳, 根据这段代码, 返回的时间戳应该在(0x5AFFE78F, 0x5B028A8F]这个范围内程序才会继续执行。
其后以时间戳做为随机数种子, 取随机数对byte405020数组异或运算, 再通过sub4027ED这个函数对byte_405020数组进行一些运算, 最终得到v4的值, 并判断v4的值是否等于0x700.
在后面的代码中可知, 如果result ==0, 那么程序将打印出
flag only appears at a specific time, range [2018-05-19 09:00, 2018-05-21 09:00) Better luck next time :)
|
并退出。观察到0x5B028A8F - 0x5AFFE78F = 0x2A300, 所以time的范围算是比较小的了, 我们可以修改程序代码, 让程序自己跑出time。
修改如下:(IDA修改指令快捷键为Ctrl+Alt+K, 修改后patch上去即可。剩下的就看各位的汇编功底了!)
__int64 __fastcall sub_402268(__int64 a1, __int64 a2) { unsigned int v3; int v4; int v5; int i;
v5 = 0x5AFFE790; do { if ( (unsigned int)++v5 > 0x5B028A8F ) return 0LL; srand(a1); for ( i = 0; i <= 255; ++i ) byte_405120[i] = rand() ^ byte_405020[i]; v4 = 0; sub_4027ED(a1, a2, (__int64)&v4); } while ( v4 != 1792 ); dword_4099D0[0] = v3; return 0LL; }
|
在dword_4099D0[0] = v3;处下断, 即可跑出时间:0x5b00e398
为了接下来方便调试, 直接将v5设置为0x5b00e398, 即可进入第二步验证:输入验证
输入验证
经过调试, 找到输入验证的函数
__
int64 __fastcall sub_4023B1(const char *a1, __int64 a2) { __int64 v3; __int64 v4; __int64 v5; __int64 v6; unsigned int v7; char v8; __int64 *Count;
if ( !dword_4099D0[0] ) exit((int)a1); v8 = 0; v7 = dword_4099D0[0]; sub_402218(a1, a2, 49, (__int64)&unk_4052A0, dword_4099D0[0]); v3 = 0LL; v4 = 0LL; v5 = 0LL; v6 = 0LL; Count = (__int64 *)((char *)&v3 + 4); scanf(a1, a2, (char *)&v3 + 4, "%26s"); RC4((__int64)a1, a2, 26LL, (__int64)Count, (unsigned __int64)&v7); if ( !(unsigned int)check((size_t)a1) ) return sub_402218(a1, a2, 6, (__int64)aC, dword_4099D0[0]); RC4((__int64)a1, a2, 26LL, (__int64)Count, (unsigned __int64)&v7); sub_401FFB(a1); sub_402218(a1, a2, 35, (__int64)&unk_4052E0, dword_4099D0[0]); puts(a1); return sub_402218(a1, a2, 35, (__int64)&unk_4052E0, dword_4099D0[0]); }
|
RC4
RC4的辨识度较高:
__int64 __fastcall RC4(__int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5, unsigned int a6) { _QWORD *v6; __int64 v8; char v9; __int64 v10;
v10 = a4; memset(&v8 + 4, 0, 0x100uLL); v6 = &v8 + 36; sub_401C67((__int64)v6, a2, a5, (__int64)&v9, a6); return sub_401E02((__int64)v6, a2, v10); }
|
在sub_401C67中密钥扩展:
for ( i = 0; (signed int)i <= 255; ++i ) { *(_BYTE *)((signed int)i + a4) = i; result = (signed int)i; v7[i] = *(_BYTE *)(i % a5 + a3); }
|
在sub_401E02中的加密运算:
__int64 __fastcall sub_401E02(__int64 a1, __int64 a2, __int64 a3, __int64 a4, unsigned int a5) { unsigned int v5; char v6; __int64 result; unsigned int i; int v9; signed int v10;
v10 = 0; v9 = 0; for ( i = 0; ; ++i ) { result = i; if ( i >= a5 ) break; v10 = (unsigned __int8)(((unsigned int)((v10 + 1) >> 31) >> 24) + v10 + 1) - ((unsigned int)((v10 + 1) >> 31) >> 24); v5 = (unsigned int)((v9 + *(unsigned __int8 *)(v10 + a4)) >> 31) >> 24; v9 = (unsigned __int8)(v5 + v9 + *(_BYTE *)(v10 + a4)) - v5; v6 = *(_BYTE *)(v10 + a4); *(_BYTE *)(v10 + a4) = *(_BYTE *)(v9 + a4); *(_BYTE *)(a4 + v9) = v6; *(_BYTE *)(i + a3) ^= *(_BYTE *)((unsigned __int8)(*(_BYTE *)(v10 + a4) + *(_BYTE *)(v9 + a4)) + a4); } return result; }
|
check
__int64 __fastcall check(size_t Count, char *a2, __int64 a3, size_t input) { int v4; __int64 v5; int v6; unsigned int v8; signed int v9; int v10;
strncpy((char *)Count, a2, input); signal(Count, (void (__cdecl *)(int))a2); v10 = 0; v9 = 1; v8 = 0; r1 = (unsigned __int64)&byte_405320; LODWORD(r2) = (unsigned __int64)&input_RC4; while ( v9 ) { v4 = setjmp((_JBTYPE *)Count); if ( v4 == 0xA8 ) { *(&r0 + (opcode[v10] >> 4)) -= *(&r0 + (opcode[v10] & 0xF)); ++v10; } else if ( v4 > 0xA8 ) { if ( v4 == 0xAC ) { *(&r0 + (opcode[v10] >> 4)) &= *(&r0 + (opcode[v10] & 0xF)); ++v10; } else if ( v4 > 0xAC ) { if ( v4 == 0xAE ) { *(&r0 + (opcode[v10] >> 4)) ^= *(&r0 + (opcode[v10] & 0xF)); ++v10; } else if ( v4 < 0xAE ) { *(&r0 + opcode[v10]) = (unsigned __int8)~*((_BYTE *)&r0 + 4 * opcode[v10]); ++v10; } else { if ( v4 != 0xAF ) goto LABEL_43; cmp_0 = opcode[v10] >> 4; cmp_1 = opcode[v10] & 0xF; if ( !setjmp((_JBTYPE *)Count) ) opcode[v10] = cmp_0 / opcode[v10 + 1]; v10 += 2; } } else if ( v4 == 0xAA ) { *(&r0 + opcode[v10]) = *(&r0 + opcode[v10 + 1]); v10 += 2; } else if ( v4 > 0xAA ) { *(&r0 + opcode[v10]) = opcode[v10 + 1]; v10 += 2; } else { *(&r0 + (opcode[v10] >> 4)) += *(&r0 + (opcode[v10] & 0xF)); ++v10; } } else if ( v4 == 0xA3 ) { *(&r0 + (opcode[v10] >> 4)) |= *(&r0 + (opcode[v10] & 0xF)); ++v10; } else if ( v4 > 0xA3 ) { if ( v4 == 0xA6 ) { if ( !r5 ) v10 += (char)opcode[v10]; ++v10; } else if ( v4 > 0xA6 ) { if ( r5 ) v10 += (char)opcode[v10]; ++v10; } else { if ( v4 != 0xA5 ) goto LABEL_43; v10 += opcode[v10] + 1; } } else if ( v4 == 0xA0 ) { *(&r0 + opcode[v10]) = *(unsigned __int8 *)(signed int)*(&r0 + opcode[v10]); ++v10; } else if ( v4 == 0xA2 ) { v6 = v10 + 1; *(&r0 + opcode[v6]) >>= *(&r0 + opcode[v6]); v10 = v6 + 1; } else { if ( !v4 ) { v5 = opcode[v10]; longjmp_0((_JBTYPE *)Count, (int)a2); } LABEL_43: v9 = 0; v8 = r5; } } return v8; }
|
则调试分析, 如下:
.bss:0000000000409040 r0 dd ? ; DATA XREF: sub_402930+32↑o .bss:0000000000409040 ; sub_402930+4C↑o ... .bss:0000000000409044 r1 dd ? ; DATA XREF: check+52↑w .bss:0000000000409048 ; _DWORD *r2 .bss:0000000000409048 r2 dd ? ; DATA XREF: check+5F↑w .bss:000000000040904C r3 dd ? .bss:0000000000409050 r4 dd ? .bss:0000000000409054 r5 dd ? ; DATA XREF: check:loc_403073↑r .bss:0000000000409054 ; check:loc_40309A↑r ... .bss:0000000000409058 r6 dd ? .bss:000000000040905C dd ? .bss:0000000000409060 cmp_0 dd ? ; DATA XREF: sub_402930+22↑r .bss:0000000000409060 ; sub_402930+5B↑r ... .bss:0000000000409064 cmp_1 dd ? ; DATA XREF: sub_402930+3C↑r .bss:0000000000409064 ; check+35A↑w
|
AF的操作并不在0xAF中, 因为这里的除数永远等于0, 所以AF的操作在前面的除0异常处理signal中, 这里有点坑, IDA分析出来的和实际汇编代码看着不太一样。
else { if ( v4 != 0xAF ) goto LABEL_43; cmp_0 = opcode[v10] >> 4; cmp_1 = opcode[v10] & 0xF; if ( !setjmp((_JBTYPE *)Count) ) opcode[v10] = cmp_0 / opcode[v10 + 1]; v10 += 2; }
|
.text:00000000004029EC lea rdx, sub_402930 .text:00000000004029F3 mov ecx, 8 .text:00000000004029F8 call signal
|
在sub_402930中:
void __fastcall __noreturn sub_402930(_JBTYPE *a1, void (__cdecl *a2)(int), __int64 a3, int a4) { if ( a4 == 8 ) { signal((int)a1, a2); *(&r0 + cmp_0) = *(&r0 + cmp_0) == *(&r0 + cmp_1); longjmp_0(a1, (int)a2); } exit((int)a1); }
|
最终, opcode为:
3 AB 03 00 : mov r3, 0x00 3 AB 04 1A : mov r4, 0x1A 3 AB 00 66 : mov r0, 0x66 s1: 3 AA 05 02 : mov r5, r2 2 A9 53 : add r5, r3 2 A0 05 : mov r5, [r5] 3 AB 06 CC : mov r6, 0xCC 2 A9 56 : add r5, r6 3 AB 06 FF : mov r6, 0xFF 2 AC 56 : and r5, r6 2 AE 50 : xor r5, r0 2 AD 00 : r0 = ~r0 3 AA 06 05 : mov r6, r5 3 AA 05 01 : mov r5, r1 2 A9 53 : add r5, r3 2 A0 05 : mov r5, [r5] 3 AF 56 00 : cmp r5, r6 3 A7 01 : jz s2 1 CC s2 : 2 A9 35 : add r3, r5 3 AA 05 03 : mov r5, r3 3 AF 54 00 : cmp r5, r4 3 A6 D1 : jnz s1 1 CC
|
写成python大概是这样:
for r3 in range(26): r5 += 0xCC r5 &= 0xFF r5 ^= r0 r0 = (~r0) & 0xFF r6 = r5 r5 = const[r3] if r5 != r6 : break r3 += 1 r5 = input[r3]
|
则求出RC4之后的输入为:
const = [0x89, 0xC1, 0xEC, 0x50, 0x97, 0x3A, 0x57, 0x59, 0xE4, 0xE6, 0xE4, 0x42, 0xCB, 0xD9, 0x08, 0x22, 0xAE, 0x9D, 0x7C, 0x07, 0x80, 0x8F, 0x1B, 0x45, 0x04, 0xE8]
r0 = 0x66 for i in range(0, 26): r5 = const[i] r5 ^= r0 r5 = (r5 - 0xCC) & 0xFF r0 = (~r0) & 0xFF print('\\x' + hex(r5)[2:], end = '')
|
RC4解密:
from Crypto.Cipher import ARC4 key = b'\xA4\xE7\x2C\x32' * 4
obj = ARC4.new(key)
input = b"\x23\x8c\xbe\xfd\x25\xd7\x65\xf4\xb6\xb3\xb6\x0f\xe1\x74\xa2\xef\xfc\x38\x4e\xd2\x1a\x4a\xb1\x10\x96\xa5"
print(obj.decrypt(input))
|
得到输入:@ck_For_fun_02508iO2_2iOR}, 输入后输出以下信息, 摘掉眼镜打开高清视界0.0
the part of flag was protected by a magic spell! @ck_For_fun_02508iO2_2iOR} .843fFDCb52bc573DA7e336b4BCC97C6E. .1adC4b19FEBA1Bf9D182FAe8Eac1AeBF. .CB7EEFeD2B2D6dd76f bE D0 ec92. .DD1C36EDBaf56 63b6 ad83 f5D a60D. .28CCE56eaBbcF 0Bb9 ed7F 669 aff7. . dC 83 4 bf a01 . . DAB 2a0 CBD eB74 9eF6 0De 1Bf . . E15 d55A276 7A4c fA7 eE72 dc7 . . afB bE0fa2e 7Bf9 Eb14 6A5 891 . . DCf c907BF9 aFBB 28eA 4dE aB1 . . B25 c5B 16d d90f 0cb0 D78 Edd . . aEA7 eDaD 07 743A 935 27d . .D38f5b1FacEaBDeFBEEcbA4 0b9D0A0f. .ce1A5DFCe012a0a62A5e2D8 8e38C9A. .CC1b26fF12fC01f8aeB7cAC06c65FCbe. .e663471A878EcE289bee7c11d7f8CF7b. .--------------------------------. @ck_For_fun_02508iO2_2iOR} .--------------------------------.
|
最终flag为:rctf{h@ck_For_fun_02508iO2_2iOR}