-
-
[原创] KCTF 2023 第六题 wp - 98k
-
发表于: 2023-9-14 00:35 9085
-
main 之前有两个 TlsCallback 用于反调试,直接 patch 掉;
进入 main ,新开一个线程,执行函数 sub_140001630
sub_140001630 就是主逻辑,字符串都加密了,异或解密之后就能看出来逻辑:
常规流程,提示后输入,之后和 "kctf" 以及两个 main 之前初始化的全局变量字符串拼接 "kctf" + str1 + input + str2
,之后写入到分配的一段内存上,再调用 write_memory 函数向 +500 的位置写入 shellcode ,再用 NtQueueApcThread 执行这段代码。 write_memory 中还有个反调试,去掉就行(上面是已经去掉的)。
write_memory 中可以看到, shellcode 的位置在 0x140008050 ,直接跳转过去,第一条 call 是花指令,跳转到当前地址 +4 的位置,处理后如下:
这一小段循环会将后面指令中的所有 0x17 全部改成 0 ,写个脚本去掉就行,在 0x140008073 处生成函数 F5 :
check_header 函数就检测头部的 "kctf" ,通过这种方式找到分配的这段内存,之后进入 flag 的判断(数独的判断)
进入 check_sudoku 就很明显的能看到,完全就是数独的判断逻辑。最后就只剩下一个函数 get_value ,这里会得到输入的格式的判断。 get_value 里的逻辑比较乱,简单整理一下函数行为:每次读取输入的 3 个字节,必须是数字或者大写的 A-F ,之后这三个字符计算得到 16 进制的数值,10进制表示的个位、十位、百位分别是数独中的 x, y, sudoku[y][x]
。当取到的 y 和 x 与传入的参数相符合时,就会结束循环。如果当前取到的输入的下标不超过 63 (第 21 组内,即原 str1 的范围内),则直接返回;否则会取后 120 字节进行判断,其中找到与 9 * y + x
相等的 2 字节对应的 10 进制数,并且这个下标和要和前一部分的每 3 字节的下标相差 21 (即跳过前面的 21 组),最后还会判断总长度是 243 + 120 保证没有多余字节。
所以前面的 63 字节是给出数独中已有的值,后 120 字节指定了输入的数独格子的顺序。直接套用 z3 例子求解:
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;
}
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;
}
__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;
}
__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;
}
.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
.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
void
__fastcall check()
{
HANDLE
(__fastcall *CreateToolhelp32Snapshot)(
DWORD
,
DWORD
);
// rbx
BOOL
(__stdcall *Process32First)(
HANDLE
, LPPROCESSENTRY32);
// r13
BOOL
(__stdcall *CloseHandle)(
HANDLE
);
// r15
DWORD
(__stdcall *GetCurrentProcessId)();
// r12
HANDLE
v4;
// rdi
bool
v5;
// si
HANDLE
v6;
// rax
void
*v7;
// r14
BOOL
i;
// eax
char
*v11;
// rbx
DWORD
v12;
// eax
_BYTE *v13;
// rdx
_BYTE *v14;
// rbx
struct
_MEMORY_BASIC_INFORMATION Buffer;
// [rsp+48h] [rbp-2B0h] BYREF
PROCESSENTRY32 v16[2];
// [rsp+80h] [rbp-278h] BYREF
BOOL
(__stdcall *Process32Next)(
HANDLE
, LPPROCESSENTRY32);
// [rsp+300h] [rbp+8h] MAPDST
HANDLE
(__stdcall *OpenProcess)(
DWORD
,
BOOL
,
DWORD
);
// [rsp+308h] [rbp+10h] MAPDST
SIZE_T
(__stdcall *VirtualQueryEx)(
HANDLE
,
LPCVOID
, PMEMORY_BASIC_INFORMATION,
SIZE_T
);
// [rsp+310h] [rbp+18h]
CreateToolhelp32Snapshot = (
HANDLE
(__fastcall *)(
DWORD
,
DWORD
))find_proc(0xF88DDF46);
OpenProcess = (
HANDLE
(__stdcall *)(
DWORD
,
BOOL
,
DWORD
))find_proc(0xFD0B55A7);
VirtualQueryEx = (
SIZE_T
(__stdcall *)(
HANDLE
,
LPCVOID
, PMEMORY_BASIC_INFORMATION,
SIZE_T
))find_proc(0x242E6FF);
Process32First = (
BOOL
(__stdcall *)(
HANDLE
, LPPROCESSENTRY32))find_proc(0x3F347695);
Process32Next = (
BOOL
(__stdcall *)(
HANDLE
, LPPROCESSENTRY32))find_proc(0x93E12339);
CloseHandle = (
BOOL
(__stdcall *)(
HANDLE
))find_proc(0x1CA655F1);
GetCurrentProcessId = (
DWORD
(__stdcall *)())find_proc(0x35634E1);
v4 = 0i64;
v16[0].dwSize = 568;
v5 = 0;
v6 = CreateToolhelp32Snapshot(2i64, 0i64);
v7 = v6;
if
( v6 != (
HANDLE
)-1i64 )
{
for
( i = Process32First(v6, v16); i; i = Process32Next(v7, v16) )
{
if
( v16[0].th32ProcessID == GetCurrentProcessId() )
{
v4 = OpenProcess(0x2000000u, 0, v16[0].th32ProcessID);
if
( v4 )
{
v11 = 0i64;
while
( VirtualQueryEx(v4, v11, &Buffer, 0x30ui64) )
{
v11 = (
char
*)Buffer.BaseAddress + Buffer.RegionSize;
if
( Buffer.State == 4096 && Buffer.AllocationProtect == 0x40 )
{
v12 = GetCurrentProcessId();
v13 = Buffer.BaseAddress;
if
( v16[0].th32ProcessID == v12 )
v5 = check_header(*(_DWORD *)Buffer.BaseAddress);
if
( v5 )
{
v14 = v13 + 4;
if
( check_sudoku(v13 + 4) )
{
*(v14 - 4) = 0x69;
*(v14 - 3) = 0x6F;
*(v14 - 2) = 0x20;
*(v14 - 1) = 0;
v14[63] = 0x31;
v14[64] = 0x31;
}
else
{
*(v14 - 4) = 0x6D;
*(v14 - 3) = 0x6A;
*(v14 - 2) = 0x29;
*(v14 - 1) = 0;
v14[63] = 49;
v14[64] = 50;
}
v14[65] = 48;
goto
LABEL_19;
}
*v13 = 109;
v13[1] = 106;
v13[2] = 41;
v13[3] = 0;
v13[67] = 49;
v13[68] = 50;
v13[69] = 48;
}
}
}
}
}
LABEL_19:
CloseHandle(v7);
CloseHandle(v4);
}
}
void
__fastcall check()
{
HANDLE
(__fastcall *CreateToolhelp32Snapshot)(
DWORD
,
DWORD
);
// rbx
BOOL
(__stdcall *Process32First)(
HANDLE
, LPPROCESSENTRY32);
// r13
BOOL
(__stdcall *CloseHandle)(
HANDLE
);
// r15
DWORD
(__stdcall *GetCurrentProcessId)();
// r12
HANDLE
v4;
// rdi
bool
v5;
// si
HANDLE
v6;
// rax
void
*v7;
// r14
BOOL
i;
// eax
char
*v11;
// rbx
DWORD
v12;
// eax
_BYTE *v13;
// rdx
_BYTE *v14;
// rbx
struct
_MEMORY_BASIC_INFORMATION Buffer;
// [rsp+48h] [rbp-2B0h] BYREF
PROCESSENTRY32 v16[2];
// [rsp+80h] [rbp-278h] BYREF
BOOL
(__stdcall *Process32Next)(
HANDLE
, LPPROCESSENTRY32);
// [rsp+300h] [rbp+8h] MAPDST
HANDLE
(__stdcall *OpenProcess)(
DWORD
,
BOOL
,
DWORD
);
// [rsp+308h] [rbp+10h] MAPDST
SIZE_T
(__stdcall *VirtualQueryEx)(
HANDLE
,
LPCVOID
, PMEMORY_BASIC_INFORMATION,
SIZE_T
);
// [rsp+310h] [rbp+18h]
CreateToolhelp32Snapshot = (
HANDLE
(__fastcall *)(
DWORD
,
DWORD
))find_proc(0xF88DDF46);
OpenProcess = (
HANDLE
(__stdcall *)(
DWORD
,
BOOL
,
DWORD
))find_proc(0xFD0B55A7);
VirtualQueryEx = (
SIZE_T
(__stdcall *)(
HANDLE
,
LPCVOID
, PMEMORY_BASIC_INFORMATION,
SIZE_T
))find_proc(0x242E6FF);
Process32First = (
BOOL
(__stdcall *)(
HANDLE
, LPPROCESSENTRY32))find_proc(0x3F347695);
Process32Next = (
BOOL
(__stdcall *)(
HANDLE
, LPPROCESSENTRY32))find_proc(0x93E12339);
CloseHandle = (
BOOL
(__stdcall *)(
HANDLE
))find_proc(0x1CA655F1);
GetCurrentProcessId = (
DWORD
(__stdcall *)())find_proc(0x35634E1);
v4 = 0i64;
v16[0].dwSize = 568;
v5 = 0;
v6 = CreateToolhelp32Snapshot(2i64, 0i64);
v7 = v6;
if
( v6 != (
HANDLE
)-1i64 )
{
for
( i = Process32First(v6, v16); i; i = Process32Next(v7, v16) )
{
if
( v16[0].th32ProcessID == GetCurrentProcessId() )
{
v4 = OpenProcess(0x2000000u, 0, v16[0].th32ProcessID);
if
( v4 )
{
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)