考点:
- TLS回调函数
- PEB反调试
- 主进程与子进程的异常处理
- 花指令与XXTEA
简述考点
TLS回调函数
这个逆向技巧其实只要记住一句话:执行于线程开始与结束
创建或终止线程时,TLS回调函数都会自动调用执行
稍加调试不难发现主逻辑在main函数,可以确定有TLS回调函数或去看看表
去IMAGE_TLS_DIRECTORY,找到Address of Callbacks(注意回调函数不止一个)
于是发现了401990即是我们的回调函数(其实IDA已经识别出来了,不过可以了解一下原理)
PEB反调试
这题只是调用了一下,并且!设置了回调函数,这个函数里实现了一个Hook(稍后再聊)
主进程与子进程的异常处理
经过一个函数会创建一个文件tmp,而在tmp里预留了一个异常,专门是用来让主进程接收然后修改某些值
而这个函数就是用来接收异常0xC0000005,并修改了指定值
花指令与XXTEA
花指令其实只要跟一遍就会很清楚,如果未解析在函数开头按P就好
这题的加密就是XXTEA了,看篇文章即可很快识别与理解
有趣的是执行流,TLS跳来跳去与主进程子进程修改了XXTEA的某些值
当然硬做也行,不过未免无趣了些(错过了好多有趣的逆向知识!
文章参考
逆向工程核心原理第45章
云之君的Write up
现在就正式以我的视角来认识一遍这道题!
0x00 日常查壳
无壳32位
0x01 开始上当
点进main函数,太天真了,解密就知道上当了
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
| int __cdecl main(int argc, const char **argv, const char **envp) { char v4[100]; char v5[33]; char v6[7]; int i;
v5[0] = 38; v5[1] = 17; v5[2] = 8; v5[3] = 35; v5[4] = 26; v5[5] = 8; v5[6] = 28; v5[7] = 39; v5[8] = 3; v5[9] = 25; v5[10] = 26; v5[11] = 43; v5[12] = 10; v5[13] = 29; v5[14] = 4; v5[15] = 30; v5[16] = 8; v5[17] = 49; v5[18] = 25; v5[19] = 4; v5[20] = 2; v5[21] = 25; v5[22] = 54; v5[23] = 1; v5[24] = 20; v5[25] = 57; v5[26] = 4; v5[27] = 59; v5[28] = 5; v5[29] = 3; v5[30] = 10; v5[31] = 5; v5[32] = 0; qmemcpy(v6, "81=<{xy", sizeof(v6)); memset(v4, 0, sizeof(v4)); sub_401050("Please input your flag: "); sub_4010F0("%s", v4); for ( i = 0; i < 40 && (i ^ v4[i] ^ 0x7F) == v5[i]; ++i ) ; if ( i == 40 ) sub_401050("correct\n"); else sub_401050("wrong\n"); return 0; }
|
0x02 TLS回调函数
于是稍加尝试与翻翻函数看看字符串什么,就会看到IDA标识出的一个TlsCallback_0,很明显主要逻辑是在那
首先就是要弄懂函数的执行流程
- 首先,注意TLS回调函数是线程运行前和线程退出后都要执行
- 而这个a1 + 12就是对应着运行前与退出,对应DLL_PROCESS_ATTACH和DLL_PROCESS_DETACH(Track师傅留
- 所以总结一下!a1 + 12 == 1是线程运行之前执行
- a1 + 12 == 0 是线程退出后运行
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 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
| void __usercall sub_4019BF(int a1@<ebp>) { char *v1;
if ( *(_DWORD *)(a1 + 12) == 1 ) { memset((void *)(a1 - 284), 0, 0x50u); puts((void *)(a1 - 284)); *(_BYTE *)(a1 - 1) = 0; *(_BYTE *)(a1 - 1) = NtCurrentPeb()->BeingDebugged; if ( !*(_BYTE *)(a1 - 1) ) return; *(_BYTE *)(a1 - 32) = 57; *(_BYTE *)(a1 - 31) = 51; *(_BYTE *)(a1 - 30) = 62; *(_BYTE *)(a1 - 29) = 56; *(_BYTE *)(a1 - 28) = 0; Xor_0x7F((const char *)(a1 - 32)); hObject = CreateFileMappingA(0, 0, 4u, 0, 0x1000u, (LPCSTR)(a1 - 32)); *(_DWORD *)input = MapViewOfFile(hObject, 0xF001Fu, 0, 0, 0x1000u); *(_BYTE *)(a1 - 116) = 47; *(_BYTE *)(a1 - 115) = 19; *(_BYTE *)(a1 - 114) = 26; *(_BYTE *)(a1 - 113) = 30; *(_BYTE *)(a1 - 112) = 12; *(_BYTE *)(a1 - 111) = 26; *(_BYTE *)(a1 - 110) = 95; *(_BYTE *)(a1 - 109) = 22; *(_BYTE *)(a1 - 108) = 17; *(_BYTE *)(a1 - 107) = 15; *(_BYTE *)(a1 - 106) = 10; *(_BYTE *)(a1 - 105) = 11; *(_BYTE *)(a1 - 104) = 95; *(_BYTE *)(a1 - 103) = 6; *(_BYTE *)(a1 - 102) = 16; *(_BYTE *)(a1 - 101) = 10; *(_BYTE *)(a1 - 100) = 13; *(_BYTE *)(a1 - 99) = 95; *(_BYTE *)(a1 - 98) = 25; *(_BYTE *)(a1 - 97) = 19; *(_BYTE *)(a1 - 96) = 30; *(_BYTE *)(a1 - 95) = 24; *(_BYTE *)(a1 - 94) = 69; *(_BYTE *)(a1 - 93) = 95; *(_BYTE *)(a1 - 92) = 0; v1 = (char *)Xor_0x7F((const char *)(a1 - 116)); puts(v1); *(_BYTE *)(a1 - 8) = 90; *(_BYTE *)(a1 - 7) = 12; *(_BYTE *)(a1 - 6) = 0; Xor_0x7F((const char *)(a1 - 8)); scanf((char *)(a1 - 8), *(_DWORD *)input, 41); } if ( !*(_DWORD *)(a1 + 12) ) { *(_BYTE *)(a1 - 24) = 81; *(_BYTE *)(a1 - 23) = 80; *(_BYTE *)(a1 - 22) = 11; *(_BYTE *)(a1 - 21) = 18; *(_BYTE *)(a1 - 20) = 15; *(_BYTE *)(a1 - 19) = 0; Xor_0x7F((const char *)(a1 - 24)); sub_401410(); memset((void *)(a1 - 204), 0, 0x44u); *(_DWORD *)(a1 - 204) = 68; CreateProcessA( (LPCSTR)(a1 - 24), 0, 0, 0, 0, 3u, 0, 0, (LPSTARTUPINFOA)(a1 - 204), (LPPROCESS_INFORMATION)(a1 - 136)); *(_BYTE *)(a1 - 44) = 28; *(_BYTE *)(a1 - 43) = 16; *(_BYTE *)(a1 - 42) = 13; *(_BYTE *)(a1 - 41) = 13; *(_BYTE *)(a1 - 40) = 26; *(_BYTE *)(a1 - 39) = 28; *(_BYTE *)(a1 - 38) = 11; *(_BYTE *)(a1 - 37) = 117; *(_BYTE *)(a1 - 36) = 0; *(_BYTE *)(a1 - 16) = 8; *(_BYTE *)(a1 - 15) = 13; *(_BYTE *)(a1 - 14) = 16; *(_BYTE *)(a1 - 13) = 17; *(_BYTE *)(a1 - 12) = 24; *(_BYTE *)(a1 - 11) = 117; *(_BYTE *)(a1 - 10) = 0; *(_BYTE *)(a1 - 88) = 47; *(_BYTE *)(a1 - 87) = 19; *(_BYTE *)(a1 - 86) = 26; *(_BYTE *)(a1 - 85) = 30; *(_BYTE *)(a1 - 84) = 12; *(_BYTE *)(a1 - 83) = 26; *(_BYTE *)(a1 - 82) = 95; *(_BYTE *)(a1 - 81) = 28; *(_BYTE *)(a1 - 80) = 19; *(_BYTE *)(a1 - 79) = 16; *(_BYTE *)(a1 - 78) = 12; *(_BYTE *)(a1 - 77) = 26; *(_BYTE *)(a1 - 76) = 95; *(_BYTE *)(a1 - 75) = 11; *(_BYTE *)(a1 - 74) = 23; *(_BYTE *)(a1 - 73) = 26; *(_BYTE *)(a1 - 72) = 95; *(_BYTE *)(a1 - 71) = 27; *(_BYTE *)(a1 - 70) = 26; *(_BYTE *)(a1 - 69) = 29; *(_BYTE *)(a1 - 68) = 10; *(_BYTE *)(a1 - 67) = 24; *(_BYTE *)(a1 - 66) = 24; *(_BYTE *)(a1 - 65) = 26; *(_BYTE *)(a1 - 64) = 13; *(_BYTE *)(a1 - 63) = 95; *(_BYTE *)(a1 - 62) = 30; *(_BYTE *)(a1 - 61) = 17; *(_BYTE *)(a1 - 60) = 27; *(_BYTE *)(a1 - 59) = 95; *(_BYTE *)(a1 - 58) = 11; *(_BYTE *)(a1 - 57) = 13; *(_BYTE *)(a1 - 56) = 6; *(_BYTE *)(a1 - 55) = 95; *(_BYTE *)(a1 - 54) = 30; *(_BYTE *)(a1 - 53) = 24; *(_BYTE *)(a1 - 52) = 30; *(_BYTE *)(a1 - 51) = 22; *(_BYTE *)(a1 - 50) = 17; *(_BYTE *)(a1 - 49) = 117; *(_BYTE *)(a1 - 48) = 0; sub_401510(a1 - 24, a1 - 136); if ( dword_404440 == 1 ) { sub_4012C0((_DWORD *)(*(_DWORD *)input + 20), 5, (int)&enc2); *(_DWORD *)(a1 - 120) = memcmp((const void *)(*(_DWORD *)input + 20), &unk_40402C, 0x14u); if ( !*(_DWORD *)(a1 - 120) ) { Xor_0x7F((const char *)(a1 - 44)); puts((void *)(a1 - 44)); LABEL_12: CloseHandle(hObject); return; } } else if ( dword_404440 == -2 ) { Xor_0x7F((const char *)(a1 - 88)); puts((void *)(a1 - 88)); goto LABEL_12; } Xor_0x7F((const char *)(a1 - 16)); puts((void *)(a1 - 16)); goto LABEL_12; } }
|
第一个TLS函数
执行几句会发现遇到一个PEB的反调试,ZF标志位直接改了过去即可,要不然就少了个TLS函数(注意这是第二个TLS函数,第一个TLS执行完后会执行这个)
随后继续跟随,就是经过混淆的字符串,然后经过异或0x7F再输出,于是一个scanf接收我们的input
第二个TLS函数
随后的a1 + 12并不会进入,于是就会执行完第一个TLS函数ret走,由于TLS的机制会执行到第二个TLS函数
所以我们可以在第二个TLS函数开头下个断点,一个F9就会到这
简述该函数
- 获取WriteFile API的地址
- 将WriteFile API地址改成自写函数的地址(也就是Hook函数)
- 也就是之后我们调用WriteFile函数地址就会调用Hook函数(Hook函数里执行完Hook代码就会脱钩)
随后在第二个TLS函数会调用ExitProcess(),也就是退出进程,那么退出进程又会调用TLS函数链
再返TLS链
于是又回到了第一个TLS函数,不过这次进的是退出线程的函数块
在这里就是创建子进程tmp文件,并且可以发现WriteFile API的地址修改成了Hook函数
这时候已经可以在同目录文件夹找到tmp文件,拉进IDA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| int __cdecl main(int argc, const char **argv, const char **envp) { void *v3; int result;
sub_401400(v3); if ( IsDebuggerPresent_0() ) { DELTA ^= 0x90909090; key[1] = 144; } DELTA = sub_401210(DELTA); sub_401390(&aMinilctfCbda59); sub_401240(&aMinilctfCbda59, 5, key); if ( !memcmp(&aMinilctfCbda59, &unk_404018, 0x14u) ) result = 1; else result = -1; return result; }
|
关键的一个点就是触发0xC0000005异常,和主函数对应,又异或了个值0x1B207
(想动调两个文件,前些日子成功过,今天一直不成功如果有师傅会调请务必告诉我!)
而tmp函数里的加密逻辑其实也就是XXTEA,关键的是从>> 5改成了>> 6(如果看了下XXTEA源码相信没什么问题)
0x03 GetFlag
于是再注意一下DELTA数的变换前一半flag就出来了
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
| #include <stdio.h> #include <stdint.h> #define DELTA 0x1c925d64 #define MX (((z>>6^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z))) void btea(uint32_t *v, int n, uint32_t const key[4]) { uint32_t y, z, sum; unsigned p, rounds, e; if (n > 1) { rounds = 6 + 52/n; sum = 0; z = v[n-1]; do { sum += DELTA; e = (sum >> 2) & 3; for (p=0; p<n-1; p++) { y = v[p+1]; z = v[p] += MX; } y = v[0]; z = v[n-1] += MX; } while (--rounds); } else if (n < -1) { n = -n; rounds = 6 + 52/n; sum = rounds*DELTA; y = v[0]; do { e = (sum >> 2) & 3; for (p=n-1; p>0; p--) { z = v[p-1]; y = v[p] -= MX; } z = v[n-1]; y = v[0] -= MX; sum -= DELTA; } while (--rounds); } } int main() { uint32_t v[]= {0x6B7CE328, 0x4841D5DD, 0x963784DC, 0xEF8A3226, 0x0776B226}; uint32_t const k[4]= {0x12,0x90,0x56,0x78}; int n = 5, i, j; printf("%x\n", ((0x9E3779B9 ^ 0x12345678 ^ 0x90909090 ^ 0x7B) + 12345) ^ 0x1B207); printf("%x\n", 0x1C93EF63 ^ 0x1B207u); btea(v, -n, k);
unsigned char * p = (unsigned char *)v; for ( i = 0; i < 5; i++ ) for ( j = 0; j < 4; j++ ) printf("%c", p[i * 4 + j]); return 0; }
|
而后面那半就比较好出了,没有改任何Delta数或XXTEA算法
GetFlag!