# Mini vnctf Crackme Wirteup

反调试在 ctf 中是非常常见且基础的技术,作为招新赛的考点不会太难,单出又不是很有意思,所以把它和 hook 结合起来出题,不知道大家有没有玩的开心~

那让我们开始吧!

# 程序分析

shift + f12 查找字符串定位,根据函数功能重命名出 printfscanf 函数

发现逻辑非常简单,将输入的数据与 0x14 异或后再加 0x10,最后和密文比较

解密后发现这是一个 fake flag,也就是说真正的逻辑并不在这里,注意到程序在加密时是先把输入复制到一个另一个变量里在进行加密,也就是说,最开始的输入还是原始的,我们可以对它下硬件断点去跟踪谁对这个数据做了改变

# 调试

scanf 处下断,启动调试,发现程序退出,很明显有反调试。既然程序会退出,那么里面一定调用了 exit 这个函数,所以在 exit 函数下断,看谁调用了它(也可以交叉引用去看)

断下后查看栈,

发现 sub_7FF797291450 函数调用了 exit

是经典的 IsDebuggerPresent 反调试,由于整个函数只有反调试一个作用,我们可以交叉引用看谁调用了这个函数,直接把整个函数 nop 掉就好了

在这段代码中,第一个函数是刚刚的反调试,第二个函数 sub_7FF79729147B 是另一种反调试,利用 CheckRemoteDebuggerPresent 检测调试,如果被检测到就退出

在确定没有别的地方调用这两个函数以后,这边我们直接把两个 call 给 nop 就好了

修改完之后记得在 edit -> patch program -> Apply patchs to input file 保存修改,不然调试时还是原来的程序

继续看刚才调用反调试函数的地方,下面有一个 srand 函数,从汇编可以看出种子是 0x123456, 又用 CreateThread 创建新线程,StartAddress 就是这个线程要调用的函数

StartAddress 函数里的这个检测是什么呢,其实是计算了刚刚那个假函数的 crc 值,crc 是一种单向算法,fake_main 只要有一点不一样,算出来的 crc 值就千差万别,这个地方是在检测 fake_main 函数有没有被下断点,因为软件断点会暂时把下断处代码的第一个字节替换成 0xcc,这样 fake_main 的值就会和原始不同

那么想绕过这个检测,可以 nop 掉 exit 函数,可以修改 if 判断的跳转,也可以直接把创建新线程的函数 nop 掉,这就随大家的喜好了

现在,只剩一个 sub_7FF797292561 函数没有分析了,这个函数是在做什么呢,可以看一下源码

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
int hook_iat(void *TargetStub, void *MyFunc)
{
uint8_t *stub = (uint8_t *)TargetStub;

if (stub[0] != 0xFF || stub[1] != 0x25)
{
return -1;
}

int32_t rel_offset = *(int32_t *)(&stub[2]);
uint64_t rip_base = (uint64_t)stub + 6;
void **iat_entry_ptr = (void **)(rip_base + rel_offset);

DWORD OldProtect;
if (!VirtualProtect(iat_entry_ptr, sizeof(void *), PAGE_READWRITE, &OldProtect))
{
return -1;
}

originaliat = (iatType)(*iat_entry_ptr);
if (originaliat == NULL)
{
VirtualProtect(iat_entry_ptr, sizeof(void *), OldProtect, &OldProtect);
return -1;
}

*iat_entry_ptr = MyFunc;

VirtualProtect(iat_entry_ptr, sizeof(void *), OldProtect, &OldProtect);

return 0;
}

再结合程序

memcmp 函数其实是一个跳转 FF 25 是远跳的机器码,后面四字节是偏移

1
2
3
int32_t rel_offset = *(int32_t *)(&stub[2]);  // 偏移
uint64_t rip_base = (uint64_t)stub + 6; // 此时rip的值(这条jmp指令长度为6个字节)
void **iat_entry_ptr = (void **)(rip_base + rel_offset); // memcmp函数的地址

所以 sub_7FF797292561 函数其实是先保存原始 memcmp 函数的地址,然后

1
*iat_entry_ptr = MyFunc;

将原本要执行的函数,替换成自己的函数,实现了一个 iathook。

也就是在执行 fake_main 之前,我们的 memcmp 早已被替换成了 sub_7FF6E0B42384 函数。

sub_7FF6E0B42384 函数有一个复杂的自定义函数,一个标准 rc4 函数,最后的 originaliat 其实就是 memcmp 函数,到现在逻辑就很清晰了,真正的加密流程应该是:enc 函数加密 key,用加密后的 key 对 input 进行 rc4 加密,最后和密文比较。

所以,要解密的话,只需要调试拿到加密后的密钥,然后将密文进行 rc4 解密就可以了。

相信不少师傅都做到了这一步,那为什么没有成功解密呢?

反调试的精髓,就在于悄无声息的进行,直接 exit 大家都能发现,怎样才能不被发现又能阻碍你调试呢,那当然是偷偷的篡改一些数据,使得最后无法成功解密。

程序中关键的数据有:key,dword_7FF6E0B53054,密文

对这三个东西下硬件断点,调试

上面这个函数是检测进程,有没有运行 ida,那么下面这个是什么意思呢

查看汇编

减 0x1C 的是 eax,而 eax 又是从 [rax+rdx] 取的,rdx 是存放输入的地址,此时 rax 又等于 0x30,那么 eax 应该就是 input 往后偏移 0x30 的地方

也就是我们的后 10 个密文

其实这边用这种写法本来是想藏一下交叉引用,没想到 input 可以交叉到,失误了(

最后 dword_7FF6E0B53054 的修改也比较容易被发现,enc 函数单步一下就会发现不是原来的那个函数,这个其实也是一种 hook

至此所有的反调试就都找完了

# 解密

patch 完上面反调试以后,dump 出 key 和密文,就可以写解密脚本了

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
#include <stdio.h>

void rc4_init(unsigned char* s, unsigned char* key, unsigned long Len_k)
{
int i = 0, j = 0;
unsigned char k[256] = { 0 };
unsigned char tmp = 0;
for (i = 0; i < 256; i++) {
s[i] = i;
k[i] = key[i % Len_k];
}
for (i = 0; i < 256; i++) {
j = (j + s[i] + k[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
}
}

void rc4_crypt(unsigned char* Data, unsigned long Len_D, unsigned char* key, unsigned long Len_k)
{
unsigned char s[256];
rc4_init(s, key, Len_k);
int i = 0, j = 0, t = 0;
unsigned long k = 0;
unsigned char tmp;
for (k = 0; k < Len_D; k++) {
i = (i + 1) % 256;
j = (j + s[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
t = (s[i] + s[j]) % 256;
Data[k] = Data[k] ^ s[t];
}
}

int main()
{
unsigned char key[] = {0x50, 0xB5, 0xC8, 0x17, 0x4C, 0xA7, 0xD1, 0xD6, 0x44, 0xA1, 0xC5, 0x38, 0x82, 0xB0, 0x55, 0xF8};
unsigned long key_len = sizeof(key);
unsigned char data[] = { 0x8D, 0x9C, 0x0E, 0xFD, 0x86, 0x85, 0x08, 0x79, 0x1B, 0xBC, 0x8E, 0xC8, 0x00, 0x66, 0x07, 0xC5,
0xA8, 0x2E, 0xE6, 0x24, 0x40, 0xA0, 0xB2, 0xC1, 0xC4, 0x64};
rc4_crypt(data, sizeof(data), key, key_len);
for (int i = 0; i < sizeof(data); i++)
{
printf("%c", data[i]);
}
printf("\n");
return 0;
}
// VNCTF{0h_y0u3_4r3_Be5t^_^}

# 最后

一些常见的反调试是可以用插件 scyllahide 绕过的,反调试的种类多样,题目里也没有完全涵盖,大家在接下来的学习中还会遇到很多很多。

因为编译和环境的问题,可能有部分师傅无法正常运行题目,实在是非常抱歉╥﹏╥…,我打包了需要的依赖,需要复现的师傅可以下载

通过网盘分享的文件:crackme.zip
链接: https://pan.baidu.com/s/1Urha3z6JWXSmtWSVhOF5Tw?pwd=pam4 提取码: pam4
–来自百度网盘超级会员 v4 的分享

最后附上程序源代码

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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
#include <iostream>
#include <stdint.h>
#include <windows.h>
#include <winternl.h>
#include <tlhelp32.h>
#include <string>
#include <cwctype>
#include <algorithm>
#include "hde64/hde64.h"
#include <vector>
#include <chrono>

typedef NTSTATUS (WINAPI *NtQueryInformationProcessPtr)(
HANDLE ProcessHandle,
DWORD ProcessInformationClass,
PVOID ProcessInformation,
ULONG ProcessInformationLength,
PULONG ReturnLength
);
#define ROR32(x, n) (((x) >> (n)) | ((x) << (32 - (n))))
#define ROL32(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
typedef int (*iatType) (const void *_Buf1, const void *_Buf2, size_t _Size);
typedef int (*FuncType) (uint32_t num, uint8_t key[]);
FuncType originalfunc;
iatType originaliat;
uint32_t crc_original;
size_t FuncSize;
uint8_t input[] = "11111111111111111111111111";
uint8_t cmp_data[] = {0x8D,0x9C,0xE,0xFD,0x86,0x85,0x8,0x79,0x1B,0xBC,0x8E,0xC8,0x0,0x66,0x7,0xC5,0x88,0xE,0xC6,0x4,0x20,0x80,0x92,0xA1,0xA4,0x44};
uint8_t key[] = "WelcomeminivnCTF";
uint32_t num = 0xDEADBEEF;
int check4();
void fake_main();
static int _ = check4();
static const uint8_t s_box[256] =
{
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16};

void check1()
{
if (IsDebuggerPresent())
{
exit(0);
}
}

void check2()
{
BOOL res;
CheckRemoteDebuggerPresent(GetCurrentProcess(), &res);
if (res)
{
exit(0);
}
}


int check3(uint32_t num, uint8_t key[])
{
BOOL isdebug = FALSE;

__asm__ __volatile__ (
".intel_syntax noprefix\n\t"
"mov rax, gs:[0x60]\n\t"
"movzx eax, byte ptr [rax+0x02]\n\t"
"mov %0, eax\n\t"
".att_syntax\n"

: "=r" (isdebug)
:
: "rax", "eax"
);

if (isdebug)
{
num &= rand();
}
else
{
num |= rand();
}

return originalfunc(num, key);
}

std::wstring ToLower(std::wstring str)
{
std::transform(str.begin(), str.end(), str.begin(), ::towlower);
return str;
}

bool CheckForDebuggerProcesses()
{
std::vector<std::wstring> targetProcesses = {
L"ollydbg.exe",
L"x64dbg.exe",
L"ida.exe",
L"windbg.exe"};

std::vector<std::wstring> targetProcessesLower;
for (const auto &procName : targetProcesses)
{
targetProcessesLower.push_back(ToLower(procName));
}

targetProcessesLower.push_back(L"ida64.exe");

HANDLE hProcessSnap;
PROCESSENTRY32W pe32;
pe32.dwSize = sizeof(PROCESSENTRY32W);

hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

if (!Process32FirstW(hProcessSnap, &pe32))
{
CloseHandle(hProcessSnap);
return false;
}

do
{
std::wstring currentProcessName = ToLower(pe32.szExeFile);

for (const auto &targetName : targetProcessesLower)
{
if (currentProcessName == targetName)
{
CloseHandle(hProcessSnap);
return true;
}
}
} while (Process32NextW(hProcessSnap, &pe32));

CloseHandle(hProcessSnap);
return false;
}

int check4()
{
if (CheckForDebuggerProcesses())
{
for (int i = 0; i < 10; i++)
{
input[48 + i] -= 0x1C;
}
}
else
{
for (int i = 0; i < 10; i++)
{
input[48 + i] += 0x20;
}
}
return 0;
}

uint32_t CRC32(const void *data, size_t len)
{
uint32_t crc = 0xFFFFFFFFu;
const uint8_t *p = (const uint8_t *)data;

for (size_t i = 0; i < len; i++)
{
crc ^= p[i];
for (int j = 0; j < 8; j++)
{
uint32_t mask = 0 - (crc & 1);
crc = (crc >> 1) ^ (0xEDB88320u & mask);
}
}
return ~crc;
}

void GetTextSection(uintptr_t *base, size_t *size)
{
HMODULE hMod = GetModuleHandle(NULL);
uint8_t *baseAddr = (uint8_t *)hMod;

IMAGE_DOS_HEADER *dos = (IMAGE_DOS_HEADER *)baseAddr;
IMAGE_NT_HEADERS *nt = (IMAGE_NT_HEADERS *)(baseAddr + dos->e_lfanew);

IMAGE_SECTION_HEADER *sec = IMAGE_FIRST_SECTION(nt);

for (int i = 0; i < nt->FileHeader.NumberOfSections; i++)
{
if (memcmp(sec[i].Name, ".text", 5) == 0)
{
*base = (uintptr_t)(baseAddr + sec[i].VirtualAddress);
*size = sec[i].Misc.VirtualSize;
return;
}
}
}

size_t GetFunctionSize(void *func)
{
uintptr_t textBase;
size_t textSize;
GetTextSection(&textBase, &textSize);

uintptr_t addr = (uintptr_t)func;

if (addr < textBase || addr >= textBase + textSize)
return 0;

return (textBase + textSize) - addr;
}

int check5()
{
uintptr_t textBase;
size_t textSize;
GetTextSection(&textBase, &textSize);
FuncSize = 0x1AE;
crc_original = CRC32((void*)fake_main, FuncSize);
if (crc_original != 0x98B396A1)
{
exit(0);
}
//printf("%X\n", crc_original);
return 0;
}
/*

DWORD WINAPI check6(LPVOID lParam)
{
uint32_t crc_new = CRC32((void *)fake_main, FuncSize);
if (crc_new != crc_original)
{
exit(0);
}
return 0;
}*/

void fake_main()
{
printf("Ciallo~\n");
printf("Welcome to mini vnctf!\n");
printf("Please input your flag:\n");
scanf("%s", input);
uint8_t data[] = {0x52, 0x6A, 0x67, 0x50, 0x62, 0x7F, 0x70, 0x8C, 0x8D, 0x77, 0x5B, 0x8D, 0x77, 0x5B, 0x85, 0x5B, 0x82, 0x85, 0x8F, 0x81, 0x5B, 0x82, 0x88, 0x85, 0x83, 0x79};
uint8_t your_input[100]{};

memcpy(your_input, input, strlen((const char *)input));

for (int i = 0; i < 26; i++)
{
your_input[i] = (your_input[i] ^ 0x114514) & 0xFF;
your_input[i] = (your_input[i] + 0x1919810) & 0xFF;
}

if (memcmp(your_input, data, 26))
{
printf("Wrong,Try again.\n");
}
else
{
printf("Great!You are best~\n");
}
}

int enc_key(uint32_t num, uint8_t key[])
{
uint32_t state[4];
uint32_t initial_state[4];
initial_state[0] = 0xDEADBEEF ^ num;
initial_state[1] = 0xCAFEBABE ^ ROR32(num, 13);
initial_state[2] = 0x1337C0DE ^ (~num);
initial_state[3] = 0xABADBABE ^ (num * 0x41C64E6D);

memcpy(state, initial_state, sizeof(state));

for (int i = 0; i < 32; i++)
{
uint8_t *b = (uint8_t *)&state[1];
uint32_t s_out = 0;
s_out |= (uint32_t)s_box[b[0]] << 24;
s_out |= (uint32_t)s_box[b[1]] << 16;
s_out |= (uint32_t)s_box[b[2]] << 8;
s_out |= (uint32_t)s_box[b[3]];

uint32_t t0 = ROL32(state[0], 7) + state[3];
uint32_t t1 = ROR32(s_out, 11) ^ state[2];
uint32_t t2 = (state[2] & 0x55555555) | (state[3] & 0xAAAAAAAA);
uint32_t t3 = state[0] ^ ROL32(state[1], 5);

state[0] = t0 ^ 0x9E3779B9;
state[1] = t1 + (i * 0x1B);
state[2] = t2 ^ 0xFFFFFFFF;
state[3] = t3;
}

for (int i = 0; i < 4; i++)
{
state[i] ^= initial_state[(i + 2) % 4];
}

memcpy(key, state, 16);

return 0;
}

void rc4_init(unsigned char *s, unsigned char *key, unsigned long Len_k)
{
int i = 0, j = 0;
unsigned char k[256] = {0};
unsigned char tmp = 0;
for (i = 0; i < 256; i++)
{
s[i] = i;
k[i] = key[i % Len_k];
}
for (i = 0; i < 256; i++)
{
j = (j + s[i] + k[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
}
}

void rc4_crypt(unsigned char *Data, unsigned long Len_D, unsigned char *key, unsigned long Len_k)
{
unsigned char s[256];
rc4_init(s, key, Len_k);
int i = 0, j = 0, t = 0;
unsigned long k = 0;
unsigned char tmp;
for (k = 0; k < Len_D; k++)
{
i = (i + 1) % 256;
j = (j + s[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
t = (s[i] + s[j]) % 256;
Data[k] = Data[k] ^ s[t];
}
}

int true_main(const void *_Buf1, const void *_Buf2, size_t _Size)
{
//HANDLE hThread = NULL;
//hThread = CreateThread(NULL, 0, check6, NULL, 0, NULL);
//WaitForSingleObject(hThread, 60 * 1000);
//CloseHandle(hThread);
enc_key(num, key);
rc4_crypt(input, 26, key, 16);
// for(int i = 0; i < 16; i++)
// {
// printf("0x%X,",key[i]);
// }
// printf("\n");
// for(int i = 0; i < 26; i++)
// {
// printf("0x%X,",input[i]);
// }
// printf("\n");
// for(int i = 0; i < 26; i++)
// {
// printf("0x%X,",cmp_data[i]);
// }
// printf("\n");
return originaliat(input, cmp_data, 26);
}

int hook_function(void *Target, void *MyFunc)
{
int NeedLen = 0;
hde64s s;
while (hde64_disasm((char *)Target + NeedLen, &s))
{
if (NeedLen >= 14)
break;
NeedLen += s.len;
}
DWORD OldProtect;
VirtualProtect((LPVOID)Target, NeedLen, PAGE_EXECUTE_READWRITE, &OldProtect);

void *Trampoline = malloc(0x100);

DWORD OldProtect2;
VirtualProtect((LPVOID)Trampoline, 0x100, PAGE_EXECUTE_READWRITE, &OldProtect2);

memcpy(Trampoline, Target, NeedLen);
unsigned char FarJump1[14] = {0xff, 0x25, 0x00, 0x00, 0x00, 0x00};
*(DWORD64 *)(FarJump1 + 6) = (DWORD64)Target + NeedLen;
memcpy((char *)Trampoline + NeedLen, FarJump1, 14);

originalfunc = (FuncType)Trampoline;

unsigned char FarJump2[14] = {0xFF, 0x25, 0x00, 0x00, 0x00, 0x00};
*(DWORD64 *)(FarJump2 + 6) = (DWORD64)MyFunc;
memcpy(Target, FarJump2, 14);

VirtualProtect((LPVOID)Target, NeedLen, OldProtect, &OldProtect);

return 0;
}

int hook_iat(void *TargetStub, void *MyFunc)
{
uint8_t *stub = (uint8_t *)TargetStub;

if (stub[0] != 0xFF || stub[1] != 0x25)
{
return -1;
}

int32_t rel_offset = *(int32_t *)(&stub[2]);
uint64_t rip_base = (uint64_t)stub + 6;
void **iat_entry_ptr = (void **)(rip_base + rel_offset);

DWORD OldProtect;
if (!VirtualProtect(iat_entry_ptr, sizeof(void *), PAGE_READWRITE, &OldProtect))
{
return -1;
}

originaliat = (iatType)(*iat_entry_ptr);
if (originaliat == NULL)
{
VirtualProtect(iat_entry_ptr, sizeof(void *), OldProtect, &OldProtect);
return -1;
}

*iat_entry_ptr = MyFunc;

VirtualProtect(iat_entry_ptr, sizeof(void *), OldProtect, &OldProtect);

return 0;
}

DWORD WINAPI ThreadProc(LPVOID lParam)
{
check5();
return 0;
}

int fake_main2()
{
check1();
check2();
srand(0x123456);
HANDLE hThread = NULL;
hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
WaitForSingleObject(hThread, 60 * 1000);
CloseHandle(hThread);
hook_iat((void *)memcmp, (void *)true_main);
fake_main();
return 0;
}

int main()
{
hook_function((void *)enc_key, (void *)check3);
fake_main2();
}

//"VNCTF{0h_y0u3_4r3_Be5t^_^}"