C/C++ DLL反射式注入内存手动映射

反射式注入 dll ,不会调用 LoadLibrary 这个 API,因此也无法使用 CreateToolhelp32Snapshot 遍历到这个模块。同时也不需要 DLL 留在磁盘上(可以通过网络下发,或加密后存放在磁盘),因此这种注入方式更加隐蔽。

原理

总的来说,就是我们要重新实现一遍 LoadLibrary 这个 API :

假设现在我们已经使用 ReadFile 拿到了 DLL 的所有内容
之后我们需要调用 VirtualAlloc 在目标进程中申请一块内存用来存放这个 DLL
使用 WriteProcessMemory 将 DLL 的内容写入刚申请的虚拟内存中
关键 这个 DLL 中需要有一个导出函数,我们暂且叫它 ReflectiveLoader,这个函数的功能就是装载自身。所以我们只需要等到 DLL 被载入内存后,使用 CreateRemoteThread 创建一个远程线程来调用这个导出函数。
最后总结一点,核心问题就是如何编写这个 ReflectiveLoader 函数 

另外就是,因为调用 ReflectiveLoader 时, DLL 还没有加载(毕竟人家的功能就是加载 DLL。。),在编写这个函数的时候就会有很多限制(比如无法使用全局变量)。

DLL 自装载

因为 PE 文件包含了很多区段(节),为了节省空间,这些区段在磁盘上存储时是很紧凑的,如果把它们原模原样的放入内存中运行一定是会出问题的。所以 DLL 子装载函数的任务就是按照规则把这些区段按照规则映射到对应的虚拟地址中去。另外我们写的 DLL 会用到其他的 DLL (相对于被注入进程来说),这时我们还需要把我们 DLL 所依赖的 DLL 也装入内存,并修复导入表。

  • 首先使用 _ReturnAddress() 获取当前函数的返回地址,因为调用这个函数是在 ReflectiveLoader 的内部,因此从这个地址向上遍历,找 0x4d ,0x5a 就可以定位到 DLL 的 PE 文件头所在的虚拟地址 (o゚v゚)ノ

  • 使用内联汇编 mov EAX, FS:[0x30] 拿到 PEB ,用 PEB 遍历出目标进程所有模块的基地址,之后通过解析 PE 文件的导出表获取导出函数的偏移地址,基地址+偏移计算出我们需要的函数( LoadLibrary, GetProcAddress, VirtualAlloc,NtFlushInstructionCache)的虚拟地址

  • 虽然在调用 ReflectiveLoader 前,我们写的注入器程序已经在目标进程申请了一块空间,但是那是存放的是 DLL 在磁盘上的结构,要将 DLL 映射到内存需要重新分配内存。在 IMAGE_OPTIONAL_HEADER -> SizeOfImage 记录了这个 DLL 装入内存时占用的大小,用这个值作为 VirtualAlloc 的参数。

  • 将 DLL 的 PE文件头和各个节复制到对应的位置。

  • 被注入的 DLL可能还依赖于其他的 DLL,因此我们还需要使用 LoadLibrary 加载这些 DLL(LoadLibrary 的地址在上面已经拿到)

  • 被注入的 DLL 只有其 ReflectiveLoader 中的代码是故意写成地址无关、不需要重定位的,其他部分的代码则需要经过重定位才能正确运行。对于重定位问题,PE 的可选头中 DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC] 指向了重定位表:

  • IMAGE_BASE_RELOCATION结构和后面紧跟的若干个Typeoffset组成了一个块,其大小为结构体中的SizeOfBlock。因此,Typeoffset的数量可以根据SizeofBlock算出。当一个块结束时,后面紧跟的就是下一个块。若SizeofBlock为0则标志着重定位表结束了。Typeoffset的高4位代表重定位类型,一般为3,低12位则表示重定位地址。这个地址和IMAGE_BASE_RELOCATION中的VirtualAddress加起来则指向一个需要重定位的指令。

  • DLL重定位。首先计算得到基地址的偏移量,也就是实际的 DLL 加载地址减去 DLL 的推荐加载地址(保存在 NT可选头的 ImageBase 中,实际 DLL 加载地址是我们调用 VirtualAlloc()的返回值。然后将 VirtualAddress 和 Typeoffset 合力组成的地址所指向的双字加上这个偏移量,重定位就完成了。即:(DWORD)(VirtualAddress + Typeoffset的低12位) += (实际DLL加载地址 – 推荐DLL加载地址)

代码参考

https://github.com/SudoZhange/ProcessInjectionhttps://github.com/DarthTon/Xenos

以下代码来自 blackbone 框架 :

r3:

#define IOCTL_BLACKBONE_INJECT_DLL  (ULONG)CTL_CODE(FILE_DEVICE_BLACKBONE, 0x80B, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)

typedef enum _MmapFlags
{
    KNoFlags         = 0x00,    // No flags
    KManualImports   = 0x01,    // Manually map import libraries
    KWipeHeader      = 0x04,    // Wipe image PE headers
    KHideVAD         = 0x10,    // Make image appear as PAGE_NOACESS region
    KRebaseProcess   = 0x40,    // If target image is an .exe file, process base address will be replaced with mapped module value
    KNoThreads       = 0x80,    // Don't create new threads, use hijacking

    KNoExceptions    = 0x01000, // Do not create custom exception handler
    KNoSxS           = 0x08000, // Do not apply SxS activation context
    KNoTLS           = 0x10000, // Skip TLS initialization and don't execute TLS callbacks
} KMmapFlags;

typedef enum _InjectType
{
    IT_Thread,      // CreateThread into LdrLoadDll
    IT_Apc,         // Force user APC into LdrLoadDll
    IT_MMap,        // Manual map
} InjectType;

typedef struct _INJECT_DLL
{
    InjectType type;                // Type of injection
    wchar_t    FullDllPath[512];    // Fully-qualified path to the target dll
    wchar_t    initArg[512];        // Init routine argument
    ULONG      initRVA;             // Init routine RVA, if 0 - no init routine
    ULONG      pid;                 // Target process ID
    BOOLEAN    wait;                // Wait on injection thread
    BOOLEAN    unlink;              // Unlink module after injection
    BOOLEAN    erasePE;             // Erase PE headers after injection   
    KMmapFlags flags;               // Manual map flags
    ULONGLONG  imageBase;           // Image address in memory to manually map
    ULONG      imageSize;           // Size of memory image
    BOOLEAN    asImage;             // Memory chunk has image layout
} INJECT_DLL, *PINJECT_DLL;

/* -------------------------------------------------------- */

BOOL DriverControl::MmapDll( 
    DWORD pid, 
    void* address, 
    uint32_t size, 
    bool asImage,
    KMmapFlags flags,
    uint32_t initRVA /*= 0*/, 
    const std::wstring& initArg /*= L"" */ 
    )
{
	DWORD bytes = 0;
    INJECT_DLL data = { IT_MMap };

	memset( data.FullDllPath, 0, sizeof( data.FullDllPath ) );
    wcscpy_s( data.initArg, initArg.c_str() );

	data.pid = pid;
    data.initRVA = initRVA;
    data.wait = true;
    data.unlink = false;
    data.erasePE = false;
    data.flags = flags;
    data.imageBase = (ULONGLONG)address;
    data.imageSize = size;
    data.asImage = asImage;

	if (!DeviceIoControl(驱动设备句柄, IOCTL_BLACKBONE_INJECT_DLL, &data, sizeof( data ), nullptr, 0, &bytes, NULL ))
        return FALSE();

    return TRUE;
}

r0:

typedef enum _InjectType
{
    IT_Thread,      // CreateThread into LdrLoadDll
    IT_Apc,         // Force user APC into LdrLoadDll
    IT_MMap,        // Manual map
} InjectType;

typedef enum _PolicyOpt
{
    Policy_Disable,
    Policy_Enable,
    Policy_Keep,        // Don't change current value
} PolicyOpt;

typedef struct _SET_PROC_PROTECTION
{
    ULONG pid;              // Process ID
    PolicyOpt protection;   // Process protection
    PolicyOpt dynamicCode;  // DynamiCode policy
    PolicyOpt signature;    // BinarySignature policy
} SET_PROC_PROTECTION, *PSET_PROC_PROTECTION;

/*------------------- 篇幅原因,只贴上核心代码 -------------------*/



/// <summary>
/// Inject dll into process
/// </summary>
/// <param name="pid">Target PID</param>
/// <param name="pPath">TFull-qualified dll path</param>
/// <returns>Status code</returns>
NTSTATUS BBInjectDll( IN PINJECT_DLL pData )
{
    NTSTATUS status = STATUS_SUCCESS;
    NTSTATUS threadStatus = STATUS_SUCCESS;
    PEPROCESS pProcess = NULL;

    status = PsLookupProcessByProcessId( (HANDLE)pData->pid, &pProcess );
    if (NT_SUCCESS( status ))
    {
        KAPC_STATE apc;
        UNICODE_STRING ustrPath, ustrNtdll;
        SET_PROC_PROTECTION prot = { 0 };
        PVOID pNtdll = NULL;
        PVOID LdrLoadDll = NULL;
        PVOID systemBuffer = NULL;
        BOOLEAN isWow64 = (PsGetProcessWow64Process( pProcess ) != NULL) ? TRUE : FALSE;

        // Process in signaled state, abort any operations
        if (BBCheckProcessTermination( PsGetCurrentProcess() ))
        {
            DPRINT( "BlackBone: %s: Process %u is terminating. Abort\n", __FUNCTION__, pData->pid );
            if (pProcess)
                ObDereferenceObject( pProcess );

            return STATUS_PROCESS_IS_TERMINATING;
        }

        // Copy mmap image buffer to system space.
        // Buffer will be released in mapping routine automatically
        if (pData->type == IT_MMap && pData->imageBase)
        {
            __try
            {
                ProbeForRead( (PVOID)pData->imageBase, pData->imageSize, 1 );
                systemBuffer = ExAllocatePoolWithTag( PagedPool, pData->imageSize, BB_POOL_TAG );
                RtlCopyMemory( systemBuffer, (PVOID)pData->imageBase, pData->imageSize );
            }
            __except (EXCEPTION_EXECUTE_HANDLER)
            {
                DPRINT( "BlackBone: %s: AV in user buffer: 0x%p - 0x%p\n", __FUNCTION__, 
                        pData->imageBase, pData->imageBase + pData->imageSize );

                if (pProcess)
                    ObDereferenceObject( pProcess );

                return STATUS_INVALID_USER_BUFFER;
            }
        }

        KeStackAttachProcess( pProcess, &apc );

        RtlInitUnicodeString( &ustrPath, pData->FullDllPath );
        RtlInitUnicodeString( &ustrNtdll, L"Ntdll.dll" );

        // Handle manual map separately
        if (pData->type == IT_MMap)
        {
            MODULE_DATA mod = { 0 };

            __try {
                status = BBMapUserImage(
                    pProcess, &ustrPath, systemBuffer,
                    pData->imageSize, pData->asImage, pData->flags,
                    pData->initRVA, pData->initArg, &mod
                    );
            }
            __except (EXCEPTION_EXECUTE_HANDLER){
                DPRINT( "BlackBone: %s: Fatal exception in BBMapUserImage. Exception code 0x%x\n", __FUNCTION__, GetExceptionCode() );
            }

            KeUnstackDetachProcess( &apc );

            if (pProcess)
                ObDereferenceObject( pProcess );

            return status;
        }

        // Get ntdll base
        pNtdll = BBGetUserModule( pProcess, &ustrNtdll, isWow64 );

        if (!pNtdll)
        {
            DPRINT( "BlackBone: %s: Failed to get Ntdll base\n", __FUNCTION__ );
            status = STATUS_NOT_FOUND;
        }

        // Get LdrLoadDll address
        if (NT_SUCCESS( status ))
        {
            LdrLoadDll = BBGetModuleExport( pNtdll, "LdrLoadDll", pProcess, NULL );
            if (!LdrLoadDll)
            {
                DPRINT( "BlackBone: %s: Failed to get LdrLoadDll address\n", __FUNCTION__ );
                status = STATUS_NOT_FOUND;
            }
        }

        // If process is protected - temporarily disable protection
        if (PsIsProtectedProcess( pProcess ))
        {
            prot.pid         = pData->pid;
            prot.protection  = Policy_Disable;
            prot.dynamicCode = Policy_Disable;
            prot.signature   = Policy_Disable;
            BBSetProtection( &prot );
        }

        // Call LdrLoadDll
        if (NT_SUCCESS( status ))
        {
            SIZE_T size = 0;
            PINJECT_BUFFER pUserBuf = isWow64 ? BBGetWow64Code( LdrLoadDll, &ustrPath ) : BBGetNativeCode( LdrLoadDll, &ustrPath );

            if (pData->type == IT_Thread)
            {
                status = BBExecuteInNewThread( pUserBuf, NULL, THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER, pData->wait, &threadStatus );

                // Injection failed
                if (!NT_SUCCESS( threadStatus ))
                {
                    status = threadStatus;
                    DPRINT( "BlackBone: %s: User thread failed with status - 0x%X\n", __FUNCTION__, status );
                }
                // Call Init routine
                else
                {
                    if (pUserBuf->module != 0 && pData->initRVA != 0)
                    {
                        RtlCopyMemory( pUserBuf->buffer, pData->initArg, sizeof( pUserBuf->buffer ) );
                        BBExecuteInNewThread(
                            (PUCHAR)pUserBuf->module + pData->initRVA, 
                            pUserBuf->buffer,
                            THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER, 
                            TRUE, 
                            &threadStatus
                            );
                    }
                    else if (pUserBuf->module == 0)
                        DPRINT( "BlackBone: %s: Module base = 0. Aborting\n", __FUNCTION__ );
                }
            }
            else if (pData->type == IT_Apc)
            {
                status = BBApcInject( pUserBuf, pProcess, pData->initRVA, pData->initArg );
            }
            else
            {
                DPRINT( "BlackBone: %s: Invalid injection type specified - %d\n", __FUNCTION__, pData->type );
                status = STATUS_INVALID_PARAMETER;
            }

            // Post-inject stuff
            if (NT_SUCCESS( status ))
            {
                // Unlink module
                if (pData->unlink)
                    BBUnlinkFromLoader( pProcess, pUserBuf->module, isWow64 );

                // Erase header
                if (pData->erasePE)
                {
                    __try
                    {
                        PIMAGE_NT_HEADERS64 pHdr = RtlImageNtHeader( pUserBuf->module );
                        if (pHdr)
                        {
                            ULONG oldProt = 0;
                            size = (pHdr->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) ?
                                ((PIMAGE_NT_HEADERS32)pHdr)->OptionalHeader.SizeOfHeaders :
                                pHdr->OptionalHeader.SizeOfHeaders;

                            if (NT_SUCCESS( ZwProtectVirtualMemory( ZwCurrentProcess(), &pUserBuf->module, &size, PAGE_EXECUTE_READWRITE, &oldProt ) ))
                            {
                                RtlZeroMemory( pUserBuf->module, size );
                                ZwProtectVirtualMemory( ZwCurrentProcess(), &pUserBuf->module, &size, oldProt, &oldProt );

                                DPRINT( "BlackBone: %s: PE headers erased. \n", __FUNCTION__ );
                            }
                        }
                        else
                            DPRINT( "BlackBone: %s: Failed to retrieve PE headers for image\n", __FUNCTION__ );
                    }
                    __except (EXCEPTION_EXECUTE_HANDLER)
                    {
                        DPRINT( "BlackBone: %s: Exception during PE header erease: 0x%X\n", __FUNCTION__, GetExceptionCode() );
                    }
                }
            }

            ZwFreeVirtualMemory( ZwCurrentProcess(), &pUserBuf, &size, MEM_RELEASE );
        }

        // Restore protection
        if (prot.pid != 0)
        {
            prot.protection  = Policy_Enable;
            prot.dynamicCode = Policy_Enable;
            prot.signature   = Policy_Enable;
            BBSetProtection( &prot );
        }

        KeUnstackDetachProcess( &apc );
    }
    else
        DPRINT( "BlackBone: %s: PsLookupProcessByProcessId failed with status 0x%X\n", __FUNCTION__, status );

    if (pProcess)
        ObDereferenceObject( pProcess );

    return status;
}
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: c/c++和易语言都是常用的编程语言,可以用来编写驱动内存无痕读写的源码。 1. C/C++的驱动内存无痕读写源码: C/C++可以使用Windows提供的驱动开发工具包(Windows Driver Kit,简称WDK)编写驱动程序。在编写驱动程序时,可以使用设备驱动程序接口(Device Driver Interface,简称DDI)函数来读写内存。 首先,需要创建一个设备驱动程序,定义驱动程序的入口点,在其初始化驱动程序,并注册读写内存的回调函数。在回调函数,可以使用DDI函数来访问和修改内存。 2. 易语言的驱动内存无痕读写源码: 在易语言,可以使用EasyAnti提供的驱动内存读写接口来实现驱动内存无痕读写的功能。EasyAnti是一个易语言开发的驱动内核模块,提供了一系列的读写内存函数。 首先,需要加载EasyAnti驱动模块,并进行初始化。然后,使用EasyAnti提供的读写内存函数来访问和修改内存。 无论是使用C/C++还是易语言,驱动内存无痕读写都需要特殊的权限和操作系统支持。此外,在进行驱动内存无痕读写时,需要确保程序安全可靠,避免对系统造成损害。 ### 回答2: C/C++和易语言都是编程语言,可以用来编写驱动内存无痕读写的源码。 驱动内存无痕读写是指在操作系统内核层面进行内存读写操作,不留下任何痕迹,不受应用程序或者防护软件的检测和干扰。 在C/C++,可以使用Windows内核编程技术来实现驱动内存无痕读写。具体步骤如下: 1. 创建一个内核模式的驱动程序,可以使用Visual Studio等开发工具。 2. 在驱动程序,使用操作系统提供的API函数来打开、读取和写入进程内存。 3. 在驱动程序,通过提权来获得对内核空间的访问权限。 4. 使用内核模式的I/O函数来读取和写入指定进程内存。 在易语言,可以使用Win32扩展库来实现驱动内存无痕读写。具体步骤如下: 1. 创建一个易语言项目,导入Win32扩展库。 2. 使用Win32扩展库提供的函数和接口,来打开、读取和写入进程内存。 3. 通过调用Windows API函数来获得对内核空间的访问权限。 4. 使用Win32扩展库提供的函数来读取和写入指定进程内存。 无论是使用C/C++还是易语言,实现驱动内存无痕读写都需要对操作系统内核有一定的了解,并且需要具备驱动开发和内核编程的相关知识和技能。此外,如此高级的技术也可能涉及到系统安全和法律合规性的问题,需慎重使用。 ### 回答3: C/C++和易语言都是常用的编程语言,可以用于编写驱动程序和进行内存的读写操作。下面分别讨论这两种语言的驱动内存无痕读写源码实现。 对于C/C++语言来说,可以通过直接调用操作系统提供的API函数来实现驱动内存的无痕读写。例如,在Windows平台上,可以使用Windows内核编程接口(Kernel-Mode Driver Framework,简称KMDF)和Windows驱动开发包(Windows Driver Kit,简称WDK)来编写驱动程序。具体实现的步骤包括以下几个方面: 1. 首先,需要通过注册驱动程序等步骤将编写的驱动程序加载到操作系统。 2. 然后,驱动程序使用C/C++语言编写,并通过调用API函数来获取目标进程的句柄,进而访问进程内存。 3. 在获取目标进程内存句柄之后,可以通过调用API函数读取或写入目标进程内存数据。 相比之下,易语言编写驱动程序则较为简单。由于易语言的语法和开发方式相对于C/C++来说更加简洁,易于上手和理解,因此许多初学者也会选择使用易语言编写驱动程序。易语言的编程环境自带了驱动开发模块,可以直接在集成开发环境编写驱动程序,无需独立的驱动开发包。然而,易语言编写驱动程序的功能和灵活性相对较弱,对于一些高级操作可能存在限制。 综上所述,C/C++和易语言都可以用于编写驱动程序和进行内存的无痕读写操作。相比而言,C/C++提供了更为底层、灵活和功能丰富的编程能力,适合于对性能和功能有较高要求的应用场景;而易语言则更适合初学者或对功能需求较简单的场景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值