-
-
[原创]KCTF2023 第六题 至暗时刻
-
2023-9-14 11:23 3003
-
- 反调试1
1 2 3 | .text: 0000000140001324 FF 15 2E 4D 00 00 call cs:IsDebuggerPresent .text: 000000014000132A 85 C0 test eax, eax .text: 000000014000132C 74 05 jz short loc_140001333 |
- 反调试2
1 2 3 4 | .text: 0000000140001324 FF 15 2E 4D 00 00 call cs:IsDebuggerPresent .text: 000000014000132A | 31C0 | xor eax,eax | .text: 000000014000132C | EB 05 | jmp blackclient_. 140001333 | .text: 000000014000132E | E8 65FFFFFF | call blackclient_. 140001298 | |
- 反调试3
1 2 3 4 | .text: 0000000140001484 FF 15 DE 4B 00 00 call cs:CheckRemoteDebuggerPresent .text: 000000014000148A 83 7D 38 00 cmp [rbp + pbDebuggerPresent], 0 .text: 000000014000148E 0F 84 95 00 00 00 jz loc_140001529 .text: 0000000140001494 B9 08 00 00 00 mov ecx, 8 ; Size |
- 然后就是跟踪关键操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | fun_scanf((__int64) "%s" , input_1, v64); fun_decode(v15, v52, (__int64) "*s>0?" , 5 ); / / kctf sub_1400026AC(&Src, v16, v52, v17, qword_140009A80); / / kctf拼接到字符串前面 sub_140002BBA((__int64)v19, (__int64)&Source, 0i64 , (__int64)&v69, Size); / / NtAllocateVirtualMemory sub_140002DA9((__int64)v19); / / NtWriteVirtualMemory fun_decode(v20, v58, (__int64) "!?>d*" , 5 ); / / kernel32.dll fun_decode(v22, v55, (__int64) "?x)da" , v21); / / RtlFillMemory(地址,大小,值) ( (unsigned int )sub_140003529((__int64)&hThread) ) / / NtCreateThreadEx strncpy((char * )&Str1, Source + 67 , 3ui64 ); if ( !strcmp((const char * )&Str1, "110" ) ) / / 此时Str1 = = "110" { v43 = &unk_140006590; } else { if ( strcmp((const char * )&Str1, "120" ) ) / / 不等于 120 ,就退出 ExitProcess( 0 ); v43 = &unk_140006598; } fun_decode(&Dest, 3 , (__int64)v43, 3 ); fun_printf((__int64)&unk_14000659C, &Dest); / / 这里肯定就是打印成功了 |
代码中有很多syscall,win11调试进不去,需要布置win10虚拟机调试,才能看到函数名
创建的第一个线程,每次调用RtlFillMemory拷贝1字节,跟踪发现拷贝的就是 unk_140008050 中的数据,而且看着像jmp指令:
用IDA加载能识别为代码,但是有很多错误。需要x64dbg调试shellcode。
先内存断,再执行断。shellcode运行后会将所有17替换成00,重新拿到shellcode,IDA F5就能完美识别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | __int64 sub_20217() { __int64 (__fastcall * CreateToolhelp32Snapshot)(signed __int64, _QWORD); / / rbx __int64 (__fastcall * Process32First)(__int64, int * ); / / r13 void (__fastcall * CloseHandle)(__int64); / / r15 __int64 ( * GetCurrentProcessId)(void); / / r12 __int64 process; / / rdi bool v5; / / si __int64 handle; / / rax __int64 v7; / / r14 int v9; / / eax __int64 (__fastcall * Process32Next_1)(__int64, int * ); / / rbx __int64 (__fastcall * OpenProcess_1)(signed __int64, _QWORD); / / r13 __int64 address; / / rbx int v13; / / eax int * v14; / / rdx _BYTE * v15; / / rbx int * lpBuffer; / / [rsp + 48h ] [rbp - 2B0h ] int v17; / / [rsp + 58h ] [rbp - 2A0h ] __int64 v18; / / [rsp + 60h ] [rbp - 298h ] int v19; / / [rsp + 68h ] [rbp - 290h ] int v20; / / [rsp + 80h ] [rbp - 278h ] int v21; / / [rsp + 88h ] [rbp - 270h ] __int64 Process32Next; / / [rsp + 300h ] [rbp + 8h ] __int64 OpenProcess; / / [rsp + 308h ] [rbp + 10h ] __int64 (__fastcall * VirtualQueryEx)(__int64, __int64, int * * , signed __int64); / / [rsp + 310h ] [rbp + 18h ] CreateToolhelp32Snapshot = (__int64 (__fastcall * )(signed __int64, _QWORD))sub_20563( 0xF88DDF46 ); OpenProcess = sub_20563( 0xFD0B55A7 ); VirtualQueryEx = (__int64 (__fastcall * )(__int64, __int64, int * * , signed __int64))sub_20563( 0x242E6FF ); Process32First = (__int64 (__fastcall * )(__int64, int * ))sub_20563( 0x3F347695 ); Process32Next = sub_20563( - 1813961927 ); CloseHandle = (void (__fastcall * )(__int64))sub_20563( 0x1CA655F1 ); GetCurrentProcessId = (__int64 ( * )(void))sub_20563( 55981281 ); process = 0i64 ; v20 = 0x238 ; v5 = 0 ; handle = CreateToolhelp32Snapshot( 2i64 , 0i64 ); v7 = handle; if ( handle = = - 1 ) return 0xFFFFFFFFi64 ; v9 = Process32First(handle, &v20); Process32Next_1 = (__int64 (__fastcall * )(__int64, int * ))Process32Next; OpenProcess_1 = (__int64 (__fastcall * )(signed __int64, _QWORD))OpenProcess; while ( v9 ) { if ( v21 = = (unsigned int )GetCurrentProcessId() ) { process = OpenProcess_1( 0x2000000i64 , 0i64 ); if ( process ) { address = 0i64 ; while ( 1 ) { do { if ( !VirtualQueryEx(process, address, &lpBuffer, 48i64 ) ) { Process32Next_1 = (__int64 (__fastcall * )(__int64, int * ))Process32Next; OpenProcess_1 = (__int64 (__fastcall * )(signed __int64, _QWORD))OpenProcess; goto LABEL_20; } address = (__int64)lpBuffer + v18; } while ( v19 ! = 4096 || v17 ! = 64 ); v13 = GetCurrentProcessId(); v14 = lpBuffer; if ( v21 = = v13 ) v5 = sub_2062F( * lpBuffer); if ( v5 ) break ; * (_BYTE * )v14 = 'm' ; / / 第一层判断,kctf头 * ((_BYTE * )v14 + 1 ) = 'j' ; * ((_BYTE * )v14 + 2 ) = ')' ; * ((_BYTE * )v14 + 3 ) = 0 ; * ((_BYTE * )v14 + 67 ) = '1' ; * ((_BYTE * )v14 + 68 ) = '2' ; * ((_BYTE * )v14 + 69 ) = '0' ; } v15 = v14 + 1 ; if ( sub_20AA3((__int64)(v14 + 1 )) ) / / 所有判断,都在这里呗 传入kctf后面的字符串 { * (v15 - 4 ) = 'i' ; / / 正确答案!!!!!! * (v15 - 3 ) = 'o' ; * (v15 - 2 ) = ' ' ; * (v15 - 1 ) = 0 ; v15[ 63 ] = '1' ; v15[ 64 ] = '1' ; } else { * (v15 - 4 ) = 'm' ; * (v15 - 3 ) = 'j' ; * (v15 - 2 ) = ')' ; * (v15 - 1 ) = 0 ; v15[ 63 ] = '1' ; v15[ 64 ] = '2' ; } v15[ 65 ] = '0' ; break ; } } LABEL_20: v9 = Process32Next_1(v7, &v20); } CloseHandle(v7); return ((__int64 (__fastcall * )(__int64))CloseHandle)(process); } |
然后就是反复调试,发现字符串分为3块:
3201382652D139C0E22132DF1BC2212EA0991650A229B36436823D0B13D51E6 //前缀,3位一个16进制数
input //必须是3的倍数,后续分析长度必须是180
677116575313142309154604431859253431473963507533496829080645035455771774602058076430276921790210013736267644383505517280 //后缀 2位一个10进制数核心函数是2063B,要求input每3位,是一个十六进制
比如 input[0]="112",0x112=274,十位*9+个位=67,要跟后缀[0]相等
十位和个位就确定了。同时十位和个位要作为下标,充当数独的坐标。百位就是数独的内容。
数独一共是9*9个格子,其中前缀是21个数,input是60个数。所以先转换已有的21个数,更新到棋盘
再求解出答案:
编程反算数独下标,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | 74 ,[ 7 ][ 4 ]的数字 = 2 , 274 = 0x112 , input [ 0 ] = "112" 78 ,[ 7 ][ 8 ]的数字 = 7 , 778 = 0x30a , input [ 2 ] = "30A" ,有限制是大写字母 17 ,以此类推, 63 58 14 15 25 10 16 51 04 47 20 65 27 37 34 52 43 70 55 83 36 54 75 32 08 06 50 03 60 61 85 18 82 66 22 64 07 71 33 30 76 23 87 02 11 01 41 40 28 84 48 42 38 05 56 80 88 |
- 得到flag,好玩的题
11230A2CD3C31CA32E0D707D38E0743531F80F726C1D133B3A914E2F034B1D63BB17F34428E2A31B038C25E0FA2BF2301053752062AA16E20A2FC1971730E90823D01A724B0CA19B0652811541480B80943AE27E13122C30C120
赞赏
他的文章
[原创]看雪2023 第三题 秘密计划
3465