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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM