zoukankan      html  css  js  c++  java
  • 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/ProcessInjection
    https://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;
    }
    

    相关文章

    https://bbs.pediy.com/thread-266929.htm

  • 相关阅读:
    连载《一个程序猿的生命周期》-《发展篇》- 34.如果自己有想法去“创业”,怎么平衡与工作之间的关系
    连载《一个程序猿的生命周期》-《发展篇》- 33.是不是又走在“创业”的路上?!
    连载《一个程序猿的生命周期》-《发展篇》- 32.疫情中复工,跌宕起伏的2019,发展元年的2020
    连载《一个程序猿的生命周期》-《发展篇》- 28.恰逢五四,我们又走在奋斗的路上吗?
    连载《一个程序猿的生命周期》-《发展篇》- 27.从来没有996过,仍然需要人生的选择权
    连载《一个程序猿的生命周期》-《发展篇》- 26.且听风吟,静待花开,慢慢走向人生赢家
    连载《一个程序猿的生命周期》-《发展篇》- 25.论一个非正式项目经理的自我修养
    连载《一个程序猿的生命周期》-《发展篇》- 24.你所掌握的技术,创造的价值会越来越低
    连载《一个程序猿的生命周期》-《发展篇》- 23.两年多的时间,从孤家寡人到10多人的团体,经历了什么
    6年前端开发被实习生替代,所谓“经验”一文不值!
  • 原文地址:https://www.cnblogs.com/csnd/p/15613426.html
Copyright © 2011-2022 走看看