2021 07 14总结

本日复盘

T1: MRCTF2019 Re/Shit

PE —— 导入地址表(IAT)

逆向时能直接看到源代码的题目还算比较少见,程序用到了PE中的IAT,花指令,而且如果源代码里没有decode函数的话,解密算法肯定也是一道难关= =。

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

FARPROC proc = NULL;
int initHook()
{
HMODULE hModule = LoadLibraryA("Kernel32.dll");
if (hModule)
{
// proc 为 IsDebuggerPresent
proc = GetProcAddress(hModule, "IsDebuggerPresent");
if (proc == NULL)
return -1;
}
return 0;
}
// 获得proc在iat的地址
PDWORD update()
{
// 初始化proc
if (initHook() != 0)
exit(-1);
HANDLE hProcess = GetModuleHandle(NULL);
PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)hProcess;
PIMAGE_NT_HEADERS nt_header = (PIMAGE_NT_HEADERS)(dos_header->e_lfanew + (DWORD)hProcess);
IMAGE_OPTIONAL_HEADER* opt_header = &(nt_header->OptionalHeader);
PIMAGE_IMPORT_DESCRIPTOR iat = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hProcess + opt_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);

while (iat->FirstThunk)
{
PIMAGE_THUNK_DATA data = (PIMAGE_THUNK_DATA)(iat->FirstThunk + (DWORD)hProcess);
while (data->u1.Function)
{
if (IMAGE_SNAP_BY_ORDINAL(data->u1.AddressOfData))
{
data++;
continue;
}
if ((DWORD)proc == data->u1.Function)
return &data->u1.Function;
data++;
}
iat++;
}
return NULL;
}
PDWORD table_addr = NULL;

// 地址修改为addr
int writeAddr(DWORD addr)
{
if (table_addr == NULL)
table_addr = update();
DWORD dwOldProtect;
MEMORY_BASIC_INFORMATION mbi_thunk;
VirtualQuery(table_addr, &mbi_thunk, sizeof(MEMORY_BASIC_INFORMATION));
VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, PAGE_READWRITE, &mbi_thunk.Protect);
*table_addr = (DWORD)addr;
VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, mbi_thunk.Protect, &dwOldProtect);
return 0;
}

BOOL editedIsDebuggerPresent()
{
std::cout << "This is edited IsDebuggerPresent!!\n";
return TRUE;
}

int main()
{
writeAddr((DWORD)editedIsDebuggerPresent);
IsDebuggerPresent();
}

IAT就是导入地址表,包含可执行程序所需的导入函数的地址,在之前虽然学习过PE相关知识,但是实践太少,看了一会才明白代码的意思。

代码中initHook函数就是获得IsDebuggerPresent函数的地址,update函数用于获取IAT表中为IsDebuggerPresent函数的那一项的地址,最后writeAddr函数将表中那一项的值修改为参数addr的值。

在上述代码中,虽然调用的是IsDebuggerPresent函数,但实际上是editedIsDebuggerPresent被执行。

花指令

花指令不同于打乱符号等一些混淆技术,它通过在一段汇编指令中添加一些无关、无意义的汇编指令,打乱了反汇编器的解析。

emm感觉花指令硬编码、栈平衡这方面内容的熟悉程度要求比较高,以前做题是遇到花指令要么看别人怎么做,要么用一些套路,虽然最后做出来了也不知道原理。

代码中的伪指令_emit 0xE8就相当于IDA Pro中的DB 0xE8 。(写E8其实就是为了迷惑人,让人以为是一个call…= =)

但是本题直接给了源码,出题人的思路已经可以直接看出,硬编码的知识变得不太重要。这里我们就题论题,说一说函数和栈。

对于一个函数来说,能正常地调用并返回最重要的一点就是最后要栈平衡(我说最重要就是最重要= =,别喷我)。对栈进行修改的方法:1. push、pop 2. 对ebp、esp直接修改(mov、add、sub…) 3. call、ret 。
将这三种方法配合jmp、条件判断(test、cmp、jne…)、db dw dq等等一起使用,能更好的达到混淆的目的。

本题花指令部分模拟代码

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
#include <iostream>

int test()
{
_asm
{
call sub10
label09:
_emit 0xE8
jmp label10
sub10 :
add dword ptr[esp], 1
retn
label10 :
}
return 123;
}

int main()
{
std::cout << "Hello World!\n";
int a = test();
std::cout << a << "\n";
std::cout << "Hello World! END\n";
}

/*
程序输出:
Hello World!
123
Hello World! END
*/

其中的:

1
2
3
4
5
6
7
8
9
10
11
_asm
{
call sub10
label09:
_emit 0xE8
jmp label10
sub10 :
add dword ptr[esp], 1
retn
label10 :
}

即为花指令。

在IDA中查看test函数

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
.text:00412470 ; int __cdecl test()
.text:00412470 ?test@@YAHXZ proc near ; CODE XREF: test(void)↑j
.text:00412470
.text:00412470 var_CC = dword ptr -0CCh
.text:00412470
.text:00412470 push ebp
.text:00412471 mov ebp, esp
.text:00412473 sub esp, 0C0h
.text:00412479 push ebx
.text:0041247A push esi
.text:0041247B push edi
.text:0041247C
.text:0041247C __$EncStackInitStart_10:
.text:0041247C mov edi, ebp
.text:0041247E xor ecx, ecx
.text:00412480 mov eax, 0CCCCCCCCh
.text:00412485 rep stosd
.text:00412487
.text:00412487 __$EncStackInitEnd_10:
.text:00412487 call sub10
.text:00412487 ; ---------------------------------------------------------------------------
.text:0041248C db 0E8h, 0EBh, 5
.text:0041248F ; ---------------------------------------------------------------------------
.text:0041248F
.text:0041248F sub10: ; CODE XREF: test(void):__$EncStackInitEnd_10↑j
.text:0041248F add [esp+0CCh+var_CC], 1
.text:00412493 retn
.text:00412494 ; ---------------------------------------------------------------------------
.text:00412494
.text:00412494 label10:
.text:00412494 mov eax, 7Bh ; '{'
.text:00412499 pop edi
.text:0041249A pop esi
.text:0041249B pop ebx
.text:0041249C add esp, 0C0h
.text:004124A2 cmp ebp, esp
.text:004124A4 call j___RTC_CheckEsp
.text:004124A9 mov esp, ebp
.text:004124AB pop ebp
.text:004124AC retn
.text:004124AC ?test@@YAHXZ endp ; sp-analysis failed
.text:004124AC
.text:004124AC ; ---------------------------------------------------------------------------

下一步就是将 0EBh, 5 这段解析为汇编指令嗷。

变成了这样:(局部代码)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.text:00412487                 call    loc_41248F
.text:00412487 ; ---------------------------------------------------------------------------
.text:0041248C db 0E8h
.text:0041248D ; ---------------------------------------------------------------------------
.text:0041248D jmp short label10
.text:0041248F ; ---------------------------------------------------------------------------
.text:0041248F
.text:0041248F loc_41248F: ; CODE XREF: test(void):__$EncStackInitEnd_10↑j
.text:0041248F add dword ptr [esp+0], 1
.text:00412493 retn
.text:00412494 ; ---------------------------------------------------------------------------
.text:00412494
.text:00412494 label10: ; CODE XREF: test(void)+1D↑j
.text:00412494 mov eax, 7Bh ; '{'

这样只是找到了真正会执行的汇编指令,(当然你的IDA让程序走一遍最后也能得到上面的结果,我也这样做)。

下一步就是去除花指令,直接把上面出现的所有指令nop掉。

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
.text:00D42470     ; int __cdecl test()
.text:00D42470 ?test@@YAHXZ proc near ; CODE XREF: test(void)↑j
.text:00D42470 000 push ebp
.text:00D42471 004 mov ebp, esp
.text:00D42473 004 sub esp, 0C0h
.text:00D42479 0C4 push ebx
.text:00D4247A 0C8 push esi
.text:00D4247B 0CC push edi
.text:00D4247C
.text:00D4247C __$EncStackInitStart_10:
.text:00D4247C 0D0 mov edi, ebp
.text:00D4247E 0D0 xor ecx, ecx
.text:00D42480 0D0 mov eax, 0CCCCCCCCh
.text:00D42485 0D0 rep stosd
.text:00D42487
.text:00D42487 __$EncStackInitEnd_10:
.text:00D42487 0D0 nop
.text:00D42488 0D0 nop
.text:00D42489 0D0 nop
.text:00D4248A 0D0 nop
.text:00D4248B 0D0 nop
.text:00D4248C 0D0 nop
.text:00D4248D 0D0 nop
.text:00D4248E 0D0 nop
.text:00D4248F
.text:00D4248F sub10:
.text:00D4248F 0D0 nop
.text:00D42490 0D0 nop
.text:00D42491 0D0 nop
.text:00D42492 0D0 nop
.text:00D42493 0D0 nop
.text:00D42494
.text:00D42494 label10:
.text:00D42494 0D0 mov eax, 7Bh ; '{'
.text:00D42499 0D0 pop edi
.text:00D4249A 0CC pop esi
.text:00D4249B 0C8 pop ebx
.text:00D4249C 0C4 add esp, 0C0h
.text:00D424A2 004 cmp ebp, esp
.text:00D424A4 004 call j___RTC_CheckEsp
.text:00D424A9 004 mov esp, ebp
.text:00D424AB 004 pop ebp
.text:00D424AC 000 retn
.text:00D424AC ?test@@YAHXZ endp

IDA 终于能舒服地F5了。

WEB/枯燥的抽奖

考察了一个伪随机数,看了看php_mt_seed.c的源码,很多SIMD、AVX、MIC之类的知识,太菜了以后看= =。

Web Hook

本来以为这个暑假没啥开发的事了,现在有了个Web Hook加QQBot的需求,想边学Spring边开发。

Yapi RCE漏洞

人畜无害的RCE,主要是放Docker里面了,还不知道怎么扩大影响(除了sudo rm -rf /*)。要是在非Docker环境下估计就炸了,这就是某du的开源项目吗= =。

Windows 内核与驱动

没错,今天忘了学了,明天补上。(编译原理今天也没看,淦)


又水了个博客,美滋滋= =。

溜了溜了,身体要紧。

(2021-07-14)