首页
社区
课程
招聘
[原创]Flare-On 8th Challenge 9复现
2022-1-21 11:05 9637

[原创]Flare-On 8th Challenge 9复现

erfze 活跃值
12
2022-1-21 11:05
9637

这道题目可以好好看下官方WriteUp,其前半部分是Core Architecture Concepts——将题目中所用到的各种核心技术都进行了讲解,后半部分Challenge Walkthrough则是以题目作为前半部分技术的例子,对细节进行了阐述。这篇文章将侧重技术而并非解题。

0x01 CRT Initialization Functions

笔者使用的VS版本如下:

 

 

进入main函数之前的函数调用栈如下:

 

 

上图所示的栈回溯并不完整,完整调用关系如下:

 

 

 

重点在于_initterm_e_initterm两个函数(摘自How to explicitly call CRT startup functions):

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
typedef void (__cdecl *_PVFV)();
 
void _initterm(const _PVFV *ppfn, const _PVFV *end)
{
    do
    {
        if (_PVFV pfn = *++ppfn)
        {
            pfn();
        }
    } while (ppfn < end);
}
 
typedef int  (__cdecl *_PIFV)();
 
int _initterm_e(const _PIFV *ppfn, const _PIFV *end)
{
    do
    {
        if (_PIFV pfn = *++ppfn)
        {
            if (int err = pfn()) return err;
        }
    } while (ppfn < end);
 
    return 0;
}

__xi_a__xi_z__xc_a__xc_z含义如下(摘自How can I schedule some code to run after all '_atexit()' functions are completed):

1
2
3
4
5
6
7
8
extern _CRTALLOC(".CRT$XIA") _PIFV __xi_a[];
extern _CRTALLOC(".CRT$XIZ") _PIFV __xi_z[];    /* C initializers */
extern _CRTALLOC(".CRT$XCA") _PVFV __xc_a[];
extern _CRTALLOC(".CRT$XCZ") _PVFV __xc_z[];    /* C++ initializers */
extern _CRTALLOC(".CRT$XPA") _PVFV __xp_a[];
extern _CRTALLOC(".CRT$XPZ") _PVFV __xp_z[];    /* C pre-terminators */
extern _CRTALLOC(".CRT$XTA") _PVFV __xt_a[];
extern _CRTALLOC(".CRT$XTZ") _PVFV __xt_z[];    /* C terminators */

微软在其文档中给出这样一段代码:

1
2
3
4
5
6
7
8
9
10
11
int func(void)
{
    return 3;
}
 
int gi = func();
 
int main()
{
    return gi;
}

笔者编译时配置属性如下:

 

 

禁用优化:

 

 

可以看到生成之后在__xc_a__xc_z中间有一_dynamic_initializer_for__gi__指针:

 

 

 

题目中使用该技术进行VEH Hook,具体细节笔者会放到VEH Hooking一节中描述。关于该技术的利用,趋势科技在其博客中列出一表格如下:

 

 

文末会给出具体链接,感兴趣的读者可以进一步阅读。

0x02 VEH Hooking

VEH——Vectored Exception Handling,向量化异常处理。关于VEH与SEH知识讲解可以看VEH和SEH,这里不再赘述。

 

Vectored Exception Handling, Hooking Via Forced Exception这篇文章通过页的PAGE_GUARD属性触发VEH:

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
bool LeoHook::Hook(uintptr_t original_fun, uintptr_t hooked_fun)
{
    LeoHook::og_fun = original_fun;
    LeoHook::hk_fun = hooked_fun;
 
    //We cannot hook two functions in the same page, because we will cause an infinite callback
    if (AreInSamePage((const uint8_t*)og_fun, (const uint8_t*)hk_fun))
        return false;
 
    //Register the Custom Exception Handler
    VEH_Handle = AddVectoredExceptionHandler(true, (PVECTORED_EXCEPTION_HANDLER)LeoHandler);
 
    //Toggle PAGE_GUARD flag on the page
    if(VEH_Handle && VirtualProtect((LPVOID)og_fun, 1, PAGE_EXECUTE_READ | PAGE_GUARD, &oldProtection))
        return true;
 
    return false;
}
 
LONG WINAPI LeoHook::LeoHandler(EXCEPTION_POINTERS *pExceptionInfo)
{
    if (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_GUARD_PAGE_VIOLATION) //We will catch PAGE_GUARD Violation
    {
        if (pExceptionInfo->ContextRecord->XIP == (uintptr_t)og_fun) //Make sure we are at the address we want within the page
        {
            pExceptionInfo->ContextRecord->XIP = (uintptr_t)hk_fun; //Modify EIP/RIP to where we want to jump to instead of the original function
        }
 
        pExceptionInfo->ContextRecord->EFlags |= 0x100; //Will trigger an STATUS_SINGLE_STEP exception right after the next instruction get executed. In short, we come right back into this exception handler 1 instruction later
        return EXCEPTION_CONTINUE_EXECUTION; //Continue to next instruction
    }
 
    if (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_SINGLE_STEP) //We will also catch STATUS_SINGLE_STEP, meaning we just had a PAGE_GUARD violation
    {
        DWORD dwOld;
        VirtualProtect((LPVOID)og_fun, 1, PAGE_EXECUTE_READ | PAGE_GUARD, &dwOld); //Reapply the PAGE_GUARD flag because everytime it is triggered, it get removes
 
        return EXCEPTION_CONTINUE_EXECUTION; //Continue the next instruction
    }
 
    return EXCEPTION_CONTINUE_SEARCH; //Keep going down the exception handling list to find the right handler IF it is not PAGE_GUARD nor SINGLE_STEP
}

由于设置了pExceptionInfo->ContextRecord->EFlags |= 0x100,每执行一条指令就会重新回到LeoHandler。题目中__scrt_common_main_seh函数在调用_initterm时,会依次执行__xc_a__xc_z之间每个函数:

 

 

我们先来看sub_402130sub_402150两个函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
sub_402130 proc near
mov     edx, 542F881Eh
mov     ecx, 246132h
call    GetProcAddr
mov     dword_6D16E4, eax
retn
sub_402130 endp
 
sub_402150 proc near
push    offset sub_406AD0
push    1
call    dword_6D16E4
retn
sub_402150 endp

sub_4054B0根据传递参数获取指定API地址,其具体过程暂不作展开。sub_402130通过给该函数传递值获取AddVectoredExceptionHandler地址,之后由sub_402150进行调用。跟进sub_406AD0查看其功能:

 

 

进入异常处理的一种情况:

 

 

sub_406AD0先是传递ContextRecord中ECX与EDX值给sub_4054B0获取API地址,之后将该地址写入ContextRecord—>EAX中,最后更改ContextRecord->Eip + 3处指令以及EIP值:

 

 

另一种触发异常类似于下面形式:

 

0x03 API Hashes

上文提到sub_4054B0函数会根据传递参数获取指定API地址,其接受两个参数——参数1用来指定DLL,参数2用来指定API。其获取DLL名称是通过搜索由sub_401100函数建立起来的映射,该函数首先会解密DLL名称:

 

 

字符串第一个字符解密算法不同于其他字符,之后会为每个DLL名称赋予一个Key值。最终建立起来的映射如下:

 

1
2
3
4
5
6
7
8
0x176684        ntdll.dll
0x246132        kernel32.dll
0x052325        ws2_32.dll
0x234324        user32.dll
0x523422        advapi32.dll
0x43493856    gdi32.dll
0x4258672    ole32.dll
0x7468951    oleaut32.dll

其计算导出函数名称Hash值算法如下:

 

 

官方WriteUp在这里给了一个计算Hash值的脚本:

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
import pefile
import sys
import os
M32 = 0xffffffff
def main():
     if len(sys.argv) != 3:
        print("usage: generate_hashes <in.dll> <out.hashes>")
        sys.exit(0)
 
     d = [pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_EXPORT"]]
     pe = pefile.PE(sys.argv[1], fast_load=True)
     pe.parse_data_directories(directories=d)
 
     names = [ e.name for e in pe.DIRECTORY_ENTRY_EXPORT.symbols]
     names = [i for i in names if i]
 
     with open(sys.argv[2], 'wb') as f:
        f.write(b"enum %s_hash\n"% os.path.split(sys.argv[1])[-1].split('.')[0].encode())
        f.write(b"{\n")
        for name in sorted(names):
            h = 0x40
            for x in name:
                h = x - 0x45523f21 * h & M32
            f.write((b" %s = 0x%x,\n"%(name, h)))
        f.write(b"};")
if __name__ == '__main__':
    main()

通过题目中算法计算输入DLL中每个导出函数名称的Hash值并保存至C Header文件,之后于File—>Load File—>Parse C Header File选择C Header文件,便可在Local Types窗口看到对应DLL的枚举类型:

 

 

分析时遇到Hash值直接选择对应enum即可:

 

 

wmsuper师傅在Flare-ON 8th 之第九题evil一文中采用的方法是获取需要修改的地址,对应DLL的Key及Hash值,之后再去对应DLL导出表中寻找匹配函数进行输出,最后通过得到的输出结果修复原题目对应地址处指令。

 

笔者在处理时采用了一个折衷的方案,之前在复现Challenge 7时使用了一个IDA Plugin——Shellcode Hashes,虽然其覆盖算法很多,但对于题目中这种特定算法没有在脚本当中实现,笔者于make_sc_hash_db.py中添加了该算法:

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
def imul45523f21hSub(inString,fName):
    if inString is None:
        return 0
    val = 0x40
    for i in inString:
        val = val * 0x45523f21
        val = i -val
        val = val & 0xFFFFFFFF
    val = val & 0xFFFFFFFF
    return val
 
pseudocode_imul45523f21hSub = '''acc := 0x40;
for c in input_string {
   acc := acc * 45523f21h:
   acc := c -acc;
}
'''
......
# The list of tuples of (supported hash name, hash size, pseudo_code)
HASH_TYPES = [
    ('ror7AddHash32',       32, pseudocode_ror7AddHash32),
    ('ror9AddHash32',       32, pseudocode_ror9AddHash32),
    ('ror11AddHash32',      32, pseudocode_ror11AddHash32),
    ('ror13AddHash32',      32, pseudocode_ror13AddHash32),
    ('ror13AddWithNullHash32'32, pseudocode_ror13AddWithNullHash32),
    ('ror13AddHash32AddDll',   32, pseudocode_ror13AddHash32AddDll),
    ('ror13AddHash32DllSimple',   32, pseudocode_ror13AddHash32DllSimple),
    ('ror13AddHash32Sub20h', 32, pseudocode_ror13AddHash32Sub20h),
    ('ror13AddHash32Sub1'32, pseudocode_ror13AddHash32),
    ('addRor4WithNullHash32',   32, pseudocode_addRor4WithNullHash32),
    ('addRor13Hash32',      32, pseudocode_addRor13Hash32),
    ('addRor13HashOncemore32',      32, pseudocode_addRor13HashOncemore32),
    ('rol3XorEax',          32, pseudocode_rol3XorEax),
    ('rol3XorHash32',       32, pseudocode_rol3XorHash32),
    ('rol5AddHash32',       32, pseudocode_rol5AddHash32),
    ('addRol5HashOncemore32',      32, pseudocode_addRol5HashOncemore32),
    ('rol7AddHash32',       32, pseudocode_rol7AddHash32),
    ('rol7AddXor2Hash32',       32, pseudocode_rol7AddXor2Hash32),
    ('rol7XorHash32',       32, pseudocode_rol7XorHash32),
    ('rol5XorHash32',       32, pseudocode_rol5XorHash32),
    ('rol8Xor0xB0D4D06Hash32',       32, pseudocode_rol8Xor0xB0D4D06Hash32),
    ('chAddRol8Hash32',     32, pseudocode_chAddRol8Hash32),
    ('rol9AddHash32',       32, pseudocode_rol9AddHash32),
    ('rol9XorHash32',       32, pseudocode_rol9XorHash32),
    ('xorRol9Hash32',       32, pseudocode_xorRol9Hash32),
    ('shl7Shr19XorHash32',     32, pseudocode_shl7Shr19XorHash32),
    ('shl7Shr19AddHash32',     32, pseudocode_shl7Shr19AddHash32),
    ('shl7SubHash32DoublePulser',     32, pseudocode_shl7SubHash32DoublePulser),
    ('sll1AddHash32',       32, pseudocode_sll1AddHash32),
    ('shr2Shl5XorHash32',   32, pseudocode_shr2Shl5XorHash32),
    ('xorShr8Hash32',       32, pseudocode_xorShr8Hash32),
    ('imul83hAdd',          32, pseudocode_imul83hAdd),
    ('imul21hAddHash32',          32, pseudocode_imul21hAddHash32),
    ('or21hXorRor11Hash32',          32, pseudocode_or21hXorRor11Hash32),
    ('or23hXorRor17Hash32',          32, pseudocode_or23hXorRor17Hash32),
    ('playWith0xe8677835Hash', 32, pseudocode_playWith0xe8677835Hash),
    ('poisonIvyHash',       32, pseudocode_poisonIvyHash),
    ('crc32',               32, 'Standard crc32'),
    ('crc32Xor0xca9d4d4e'32, 'crc32 ^ 0xCA9D4D4E'),
    ('crc32bzip2lower',           32, 'crc32 bzip2 and str lower'),
    ('mult21AddHash32',         32, pseudocode_hashMult21),
    ('add1505Shl5Hash32',       32, pseudocode_add1505Shl5Hash32),
    ('dualaccModFFF1Hash',      32, pseudocode_dualaccModFFF1Hash),
    ('hash_Carbanak',           32, pseudocode_hash_Carbanak),
    ('hash_ror13AddUpperDllnameHash32',32, pseudocode_hash_ror13AddUpperDllnameHash32),
    ('fnv1Xor67f', 32, pseudocode_fnv1Xor67f),
    ('adler32_666', 32, 'Adler32 with starting value 666'),
    ('shift0x82F63B78',           32, 'like crc32c'),
    ('contiApiHashing',       32, pseudocode_contiApiHashing),
    ('fnv1', 32, pseudocode_fnv1),
    ('imul45523f21hSub',32 , pseudocode_imul45523f21hSub)
]

之后生成数据库文件,使用插件时选择新生成数据库再应用对应算法即可:

 

 

关于该工具更多信息可阅读Using Precalculated String Hashes when Reverse Engineering Shellcodemake_sc_hash_db.py脚本对DLL目录中每个DLL的所有导出函数应用脚本内所有Hash算法计算Hash值写入到数据库文件中:

 

 

官方WriteUp上给出了一个Nop题目中Anti-Disassembly指令的脚本:

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
import sys
 
callfuncs =  [
b'\x33\xC0\xF7\xF0\xEB\x00\xEb',
b'\x33\xC0\x8B\x00\xEB\xFF\xE8',
b'\x33\xF6\xF7\xF6\xE8\xFF\xD2',
b'\x33\xC0\x8B\x00\x74\x03\x75',
b'\x33\xFF\xF4\xF4\x33\xC0\x74',
b'\x33\xFF\xF7\xF7\x33\xC0\x74',
b'\x33\xC0\xF7\xF0\x33\xC0\x74',
b'\x33\xC0\xF7\xF0\xE8\xFF\xD2',
b'\x33\xC0\xF7\xF0\x5B\x5D\xC3']
 
nops = b'\x90\x90\x90\x90\x90\x90\x90'
def main():
    if len(sys.argv) != 3:
        print("usage: nop_anti_dis.py <in out>\n")
        sys.exit(0)
with open(sys.argv[1], 'rb') as f:
    d = f.read()
for cf in callfuncs:
    d = d.replace(cf, nops)
with open(sys.argv[2], 'wb') as f:
    f.write(d)
if __name__ == "__main__":
    main()

再配合上nop-hidder脚本:

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
from idautils import *
from idc import *
 
mnemonics = dict()
hides = []
in_nop_sled = 0
curr_pos = 0
sled_len = 0
 
for seg_ea in Segments():
    for head in Heads(seg_ea, SegEnd(seg_ea)):
        if isCode(GetFlags(head)):       
            mnem = GetMnem(head)
            if mnem == 'nop':
                sled_len += 1
                if in_nop_sled == 0:
                    curr_pos = head
                    in_nop_sled = 1
            else :
                if in_nop_sled == 1 :
                    in_nop_sled = 0
                    hides.append([curr_pos,sled_len])
                    curr_pos = 0
                    sled_len = 0
 
for h in hides:
    if h[1] > 1:
        HideArea(h[0],h[0]+h[1],'','','',0)
 
print 'Done hidding...'

如此一来,使用IDA进行静态分析便会轻松很多。

0x04 Anti-Debug & Anti-Virtualization

关于Windows调试原理可以参阅:

Anti-Debug:

sub_4023D0函数首先会PatchDbgBreakPointDbgUiRemoteBreakin两个函数:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void Patch_DbgBreakPoint()
{
    HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
    if (!hNtdll)
        return;
 
    FARPROC pDbgBreakPoint = GetProcAddress(hNtdll, "DbgBreakPoint");
    if (!pDbgBreakPoint)
        return;
 
    DWORD dwOldProtect;
    if (!VirtualProtect(pDbgBreakPoint, 1, PAGE_EXECUTE_READWRITE, &dwOldProtect))
        return;
 
    *(PBYTE)pDbgBreakPoint = (BYTE)0xC3; // ret
 
    if (!VirtualProtect(pDbgBreakPoint, 1, PAGE_EXECUTE_READ, &dwOldProtect))
        return;
}

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
#pragma pack(push, 1)
    struct DbgUiRemoteBreakinPatch
    {
        WORD  push_0;
        BYTE  push;
        DWORD CurrentPorcessHandle;
        BYTE  mov_eax;
        DWORD TerminateProcess;
        WORD  call_eax;
    };
#pragma pack(pop)
 
void Patch_DbgUiRemoteBreakin()
{
    HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
    if (!hNtdll)
        return;
 
    FARPROC pDbgUiRemoteBreakin = GetProcAddress(hNtdll, "DbgUiRemoteBreakin");
    if (!pDbgUiRemoteBreakin)
        return;
 
    HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");
    if (!hKernel32)
        return;
 
    FARPROC pTerminateProcess = GetProcAddress(hKernel32, "TerminateProcess");
    if (!pTerminateProcess)
        return;
 
    DbgUiRemoteBreakinPatch patch = { 0 };
    patch.push_0 = '\x6A\x00';
    patch.push = '\x68';
    patch.CurrentPorcessHandle = 0xFFFFFFFF;
    patch.mov_eax = '\xB8';
    patch.TerminateProcess = (DWORD)pTerminateProcess;
    patch.call_eax = '\xFF\xD0';
 
    DWORD dwOldProtect;
    if (!VirtualProtect(pDbgUiRemoteBreakin, sizeof(DbgUiRemoteBreakinPatch), PAGE_READWRITE, &dwOldProtect))
        return;
 
    ::memcpy_s(pDbgUiRemoteBreakin, sizeof(DbgUiRemoteBreakinPatch),
        &patch, sizeof(DbgUiRemoteBreakinPatch));
    VirtualProtect(pDbgUiRemoteBreakin, sizeof(DbgUiRemoteBreakinPatch), dwOldProtect, &dwOldProtect);
}

通过Vmware使用的I/O端口0x5658进行检测:

 

 

如果是运行在Vmware中则直接退出:

 

 

之后的检测方法是来自Another VMWare Detection一文:

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
// vmgpftest.cpp
// Derek Soeder - eEye Digital Security - 09/05/2006
 
#include <windows.h>
#include <stdio.h>
 
typedef LONG (NTAPI *NTSETLDTENTRIES)(DWORD,DWORD,DWORD,DWORD,DWORD,DWORD);
#define EndUserModeAddress (*(UINT_PTR*)0x7FFE02B4)
 
int detect(LPEXCEPTION_POINTERS lpep)
{
    if ((UINT_PTR)(lpep->ExceptionRecord->ExceptionAddress) > EndUserModeAddress)
        printf("VMware emulation mode detected!\n");
    return EXCEPTION_EXECUTE_HANDLER;
}
 
void __declspec(naked) switchcs()
{
__asm
{
    pop eax
    push 0x000F
    push eax
    retf
}
}
 
int main(int argc, char* argv[])
{
    NTSETLDTENTRIES ZwSetLdtEntries;
    LDT_ENTRY csdesc;
 
    ZwSetLdtEntries = (NTSETLDTENTRIES)GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwSetLdtEntries");
 
    memset(&csdesc, 0, sizeof(csdesc));
    csdesc.LimitLow = (WORD)(EndUserModeAddress >> 12);
    csdesc.HighWord.Bytes.Flags1 = 0xFA; // P=1, DPL=3, Code32, RX
    csdesc.HighWord.Bytes.Flags2 = 0xC0 | ((EndUserModeAddress >> 28) & 0x0F); // G=1, B=1, Limit[19..16]
    ZwSetLdtEntries(0x000F, ((DWORD*)&csdesc)[0], ((DWORD*)&csdesc)[1], 0, 0, 0);
 
    __try
    {
        switchcs();
        __asm
        {
            or eax, -1
            jmp eax
        }
    }
    __except(detect(GetExceptionInformation())) { }
 
    return 0;
}

执行SELECT * FROM Win32_PnPEntity以检测是否位于VirtualBox中(下面代码出自al-khaser):

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
/*
Check vbox devices using WMI
*/
BOOL vbox_pnpentity_pcideviceid_wmi()
{
    IWbemServices *pSvc = NULL;
    IWbemLocator *pLoc = NULL;
    IEnumWbemClassObject* pEnumerator = NULL;
    BOOL bStatus = FALSE;
    HRESULT hRes;
    BOOL bFound = FALSE;
 
    // Init WMI
    bStatus = InitWMI(&pSvc, &pLoc, _T("ROOT\\CIMV2"));
    if (bStatus)
    {
        // If success, execute the desired query
        bStatus = ExecWMIQuery(&pSvc, &pLoc, &pEnumerator, _T("SELECT * FROM Win32_PnPEntity"));
        if (bStatus)
        {
            // Get the data from the query
            IWbemClassObject *pclsObj = NULL;
            ULONG uReturn = 0;
            VARIANT vtProp;
 
            while (pEnumerator)
            {
                hRes = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);
                if (0 == uReturn)
                    break;
 
                // Get the value of the Name property
                hRes = pclsObj->Get(_T("DeviceId"), 0, &vtProp, 0, 0);
 
                if (SUCCEEDED(hRes)) {
                    if (vtProp.vt == VT_BSTR) {
 
                        // Do our comparaison
                        if (_tcsstr(vtProp.bstrVal, _T("PCI\\VEN_80EE&DEV_CAFE")) != 0)
                        {
                            bFound = TRUE;
                        }
                    }
                    VariantClear(&vtProp);
                }
 
                // release the current result object               
                pclsObj->Release();
 
                if (bFound)
                    break;
            }
 
            // Cleanup
            pSvc->Release();
            pLoc->Release();
            pEnumerator->Release();
            CoUninitialize();
        }
    }
 
    return bFound;
}

IsDebuggerPresent

 

1
2
3
4
5
0:000< u kernelbase!IsDebuggerPresent L3
KERNELBASE!IsDebuggerPresent:
751ca8d0 64a130000000    mov     eax,dword ptr fs:[00000030h]
751ca8d6 0fb64002        movzx   eax,byte ptr [eax+2]
751ca8da c3              ret

之后依次是CheckRemoteDebuggerPresent,NtGlobalFlag,SeDebugPrivilege,HardwareBreakpoints及GetTickCount相关Anti-Debug,具体实现可以参阅al-khaserAnti-Debug:Timing,这里不再一一赘述。可以通过ScyllaHide进行Anti-Anti-Debug。

0x05 Flag

题目会校验传递参数个数:

 

 

之后sub_403A70会调用sub_410EE0函数(根据官方WriteUp,这里应该是使用了PolyHook2)HookCryptImportKey

 

 

 

解密字符串:

 

 

一共解密出4个字符串:"L0ve", "s3cret", "5Ex", "g0d"。笔者在这里没能动调,而是根据IDA静态分析结果计算而来,由于卡在此处故查到Flare-On 8 – Task 9一文,阅读过后发现其使用方法和工具都极高效,便不再重写这些步骤,感兴趣的读者可以跟着这篇文章学习一下。

0x06 参阅链接


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2022-1-21 12:32 被erfze编辑 ,原因:
收藏
点赞3
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回