-
-
[原创] KCTF 2023 第六题 wp - 98k
-
发表于: 2023-9-14 00:35 9228
-
main 之前有两个 TlsCallback 用于反调试,直接 patch 掉;
进入 main ,新开一个线程,执行函数 sub_140001630
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
int __cdecl main( int argc, const char **argv, const char **envp)
{ _QWORD *v3; // rax
_Thrd_t ThrdAddr; // [rsp+30h] [rbp-28h] BYREF
_Thrd_t v6; // [rsp+40h] [rbp-18h] BYREF
v3 = operator new (8ui64);
*v3 = sub_140001630;
ThrdAddr._Hnd = ( void *)beginthreadex(0i64, 0, (_beginthreadex_proc_type)StartAddress, v3, 0, &ThrdAddr._Id);
if ( !ThrdAddr._Hnd )
{
ThrdAddr._Id = 0;
std::_Throw_Cpp_error(6);
}
if ( !ThrdAddr._Id )
std::_Throw_Cpp_error(1);
if ( ThrdAddr._Id == Thrd_id() )
std::_Throw_Cpp_error(5);
v6 = ThrdAddr;
if ( Thrd_join(&v6, 0i64) )
std::_Throw_Cpp_error(2);
return 0;
} |
sub_140001630 就是主逻辑,字符串都加密了,异或解密之后就能看出来逻辑:
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
|
__int64 sub_140001630()
{ // [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
std::string::ctor(&input_string, empty);
std::string::ctor(&Format, s); // 'Please enter your key:'
std::string::ctor(&str_kernel32_dll, aJzlOmJnms); // kernel32.dll
std::string::ctor(&str_RtlFillMemory, aM);
std::string::ctor(&str_kctf, aAJv); // kctf
std::string::append(&str_RtlFillMemory, aD_0); // RtlFillMemory
std::string::ctor(&Block, "7PK:" );
v0 = ( char *)&Format;
if ( Format.cap >= 0x10 )
v0 = Format.data.lstr;
vigenere_decrypt(v0, Format.size, "&x+^x" , 5);
v1 = ( char *)&Format;
if ( Format.cap >= 0x10 )
v1 = Format.data.lstr;
printf (v1);
v2 = input_string.size;
// set input length to 500
if ( input_string.size < 500 )
{
// ...
}
else
{
// ...
}
v7 = ( char *)&input_string;
if ( input_string.cap >= 0x10 )
v7 = input_string.data.lstr;
scanf ( "%s" , v7);
v8 = ( char *)&input_string;
if ( input_string.cap >= 0x10 )
v8 = input_string.data.lstr;
v9 = ( char *)&str1;
std::string::append(&str1, v8);
v10 = ( char *)&str2;
if ( str2.cap >= 0x10 )
v10 = str2.data.lstr;
v11 = str2.size;
v12 = str1.size;
str1 += str2; // ...
v15 = ( char *)&str_kctf;
if ( str_kctf.cap >= 0x10 )
v15 = str_kctf.data.lstr;
vigenere_decrypt(v15, str_kctf.size, "*s>0?" , 5);
str1 = str_kctf + str1; // ...
ProcessHandle = GetCurrentProcess();
RegionSize = 5500i64;
NtAllocateVirtualMemory(
ProcessHandle,
( PVOID *)&alloced,
0i64,
&RegionSize,
MEM_RESERVE|MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
if ( str1.cap >= 0x10 )
v9 = str1.data.lstr;
NtWriteVirtualMemory(ProcessHandle, alloced, v9, str1.size, NumberOfBytesWritten);
v20 = ( char *)&str_kernel32_dll;
if ( str_kernel32_dll.cap >= 0x10 )
v20 = str_kernel32_dll.data.lstr;
vigenere_decrypt(v20, str_kernel32_dll.size, "!?>d*" , 5);
v22 = ( char *)&str_RtlFillMemory;
if ( str_RtlFillMemory.cap >= 0x10 )
v22 = str_RtlFillMemory.data.lstr;
vigenere_decrypt(v22, str_RtlFillMemory.size, "?x)da" , v21);
v23 = ( char *)&str_RtlFillMemory;
if ( str_RtlFillMemory.cap >= 0x10 )
v23 = str_RtlFillMemory.data.lstr;
v24 = ( char *)&str_kernel32_dll;
if ( str_kernel32_dll.cap >= 0x10 )
v24 = str_kernel32_dll.data.lstr;
kernel32_dll = GetModuleHandleA(v24);
RtlFillMemory = ( void (__fastcall *)( void *, size_t , int ))GetProcAddress(kernel32_dll, v23);
if ( !RtlFillMemory || write_memory(ProcessHandle, alloced + 500) )
{
// string dtor
return 1i64;
}
if ( NtCreateThreadEx(
&hThread,
0x1FFFFFu,
0i64,
ProcessHandle,
(LPTHREAD_START_ROUTINE)ExitThread,
0i64,
1u,
0i64,
0i64,
0i64,
0i64) )
{
// string dtor
return 1;
}
NtQueueApcThread(hThread, (PIO_APC_ROUTINE)(alloced + 500), 0i64, 0i64, 0i64);
ResumeThread(hThread);
NtWaitForSingleObject(hThread, 0, 0i64);
CloseHandle(hThread);
*(_DWORD *)Str1 = 0;
*(_DWORD *)Destination = 0;
strncpy (Destination, ( const char *)alloced, 3ui64);
strncpy (Str1, ( const char *)alloced + 67, 3ui64);
if ( ! strcmp (Str1, "110" ) )
{
v40 = ( char *)&unk_140006590;
}
else
{
if ( strcmp (Str1, "120" ) )
ExitProcess(0);
v40 = ( char *)&byte_140006598;
}
vigenere_decrypt(Destination, 3, v40, 3);
printf ( "\n%s" , Destination);
// string dtor
return 0i64;
} int __fastcall write_memory( HANDLE ProcessHandle, unsigned __int8 *ptr)
{ int v4; // edi
unsigned __int8 *i; // rsi
GetCurrentProcess();
if ( !NtCreateThreadEx(
&Handle,
0x1FFFFFu,
0i64,
ProcessHandle,
(LPTHREAD_START_ROUTINE)ExitThread,
0i64,
1u,
0i64,
0i64,
0i64,
0i64) )
{
v4 = 0;
for ( i = unk_140008050; !NtQueueApcThread(Handle, (PIO_APC_ROUTINE)RtlFillMemory, &ptr[v4], 1i64, *i); ++i )
{
if ( (unsigned int )++v4 >= 0x92B )
{
ResumeThread(Handle);
NtWaitForSingleObject(Handle, 0, 0i64);
CloseHandle(Handle);
return 0;
}
}
TerminateThread(Handle, 0);
CloseHandle(Handle);
}
return 1;
} |
常规流程,提示后输入,之后和 "kctf" 以及两个 main 之前初始化的全局变量字符串拼接 "kctf" + str1 + input + str2
,之后写入到分配的一段内存上,再调用 write_memory 函数向 +500 的位置写入 shellcode ,再用 NtQueueApcThread 执行这段代码。 write_memory 中还有个反调试,去掉就行(上面是已经去掉的)。
write_memory 中可以看到, shellcode 的位置在 0x140008050 ,直接跳转过去,第一条 call 是花指令,跳转到当前地址 +4 的位置,处理后如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
.data: 0000000140008054 inc eax
.data: 0000000140008056 pop rdi
.data: 0000000140008057 mov ecx, 1010806h
.data: 000000014000805C xor ecx, 1010101h ; 0x907
.data: 0000000140008062 add rdi, 1Eh ; 0x140008073
.data: 0000000140008066 xor esi, esi
.data: 0000000140008068 cld
.data: 0000000140008069
.data: 0000000140008069 loc_140008069: ; CODE XREF: .data: 0000000140008071 ↓j
.data: 0000000140008069 mov al, [rdi]
.data: 000000014000806B cmp al, 17h
.data: 000000014000806D cmovz eax, esi
.data: 0000000140008070 stosb
.data: 0000000140008071 loop loc_140008069
|
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
他的文章
赞赏
雪币:
留言: