Kernel: Загрузка DLL из памяти (x86)
От: x64 Россия http://x64blog.name
Дата: 06.12.08 15:16
Оценка: 21 (6)
Предлагаю вашему вниманию код, загружающий DLL в а.п. произвольного процесса из памяти. Данный код настраивает секции, таблицу импортов и релоки. Замечания к этому коду следующие:

  • Код предназначен для выполнения из ядерного системного потока, созданного функцией PsCreateSystemThread().
  • В данном коде отсутствует настройка таблицы экспорта, в случае если DLL экспортирует какие-либо функции. Для интересующихся здесь
    Автор(ы): Максим М. Гумеров
    Дата: 20.03.2003
    Не вдаваясь в подробности, скажу лишь, что исследование было начато ради сокрытия использования программой на Delphi некоей DLL (написанной на VC++). То есть оператор видит один только Exe-файл, запускает его, а тот каким-то образом подключает функции, содержащиеся изначально (при компиляции проекта) в некоторой DLL.
    .
  • В коде отсутствуют исходники функций с префиксом Kmlib*. Это приватные функции, используемые в коммерческих проектах и я не могу выкладывать их код, однако интересующимся могу объяснить как они реализованы.
  • Обратите внимание, что данный код не добавляет загруженную DLL в список модулей процесса. По моему, совершенно не скромному мнению, это нафиг не нужно, однако для интересующихся опять же могу рассказать как это делается.
  • Код оттестирован на 32-битных версиях Windows XP/2003/Vista/2008, работает как часы.
  • Кстати, ребят, а вы знаете, что этот и ему подобный код вообще-то стоят немалых денег? ))))))))) И вы знаете, мне не жалко, моя личная прибыль от этого не пострадает, да.

    Ну поехали, пример использования внизу.

    BOOLEAN
    LdrIsImportByOrdinal (
    
        ULONG                        uImportDescriptor)
    
    {
        return ((uImportDescriptor & IMAGE_ORDINAL_FLAG32) != 0);
    }
    
    PIMAGE_NT_HEADERS
    LdrGetNtHeaders (
    
        PVOID                pImageBase)
    
    {
        PIMAGE_NT_HEADERS pNtHeaders = NULL;
        PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER) pImageBase;
    
        if (! pImageBase) return NULL;
    
        if (pDosHeader -> e_magic != IMAGE_DOS_SIGNATURE)
        {
            return NULL;
        }
    
        pNtHeaders = (PIMAGE_NT_HEADERS) ((ULONG_PTR) pImageBase + pDosHeader -> e_lfanew);
        if (pNtHeaders -> Signature != IMAGE_NT_SIGNATURE) return NULL;
    
        return pNtHeaders;
    }
    
    NTSTATUS
    LdrProcessImports (
    
        PEPROCESS                    pProcessObject,
        PBYTE                        pImageBase,
        PIMAGE_NT_HEADERS            pNtHeaders,
        PIMAGE_IMPORT_DESCRIPTOR    pImports)
    
    {
        PRTLX_MODULE_INFORMATION pModuleInfo = NULL;
        PIMAGE_IMPORT_DESCRIPTOR pImport = NULL;
        NTSTATUS status = STATUS_UNSUCCESSFUL;
        PUNICODE_STRING pusModuleName = NULL;
        PUNICODE_STRING pusModuleMask = NULL;
        UNICODE_STRING usAsterisk = {0};
        char* pImportedName = NULL;
        PULONG pRvaImport = NULL;
        char* pModuleName = NULL;
        PVOID pProcAddress = NULL;
        ULONG i = 0;
    
        //
        // Check input parameters
        //
    
        if (! pImageBase) return STATUS_INVALID_PARAMETER;
        if (! pImports) return STATUS_INVALID_PARAMETER;
    
        //
        // Initialize locals
        //
    
        RtlInitUnicodeString (&usAsterisk, L"*");
    
        //
        // Get pointer
        //
    
        pImport = pImports;
    
        //
        // Process import table entries
        //
    
        while (pImport -> Name != 0)
        {
            //
            // Get module name
            //
    
            pModuleName = (char*) (pImageBase + pImport -> Name);
    
            //
            // Convert module name to Unicode
            // and create search pattern
            //
    
            status = KmlibCreateUnicodeStringFromAnsiString (
                &pusModuleName,
                pModuleName);
    
            if (! NT_SUCCESS (status)) goto FINISHED;
    
            status = KmlibPathCombine (
                &usAsterisk,
                pusModuleName,
                &pusModuleMask);
    
            if (! NT_SUCCESS (status)) goto FINISHED;
    
            //
            // Find module base address
            //
    
            status = KmlibProcessFindModule (
                pProcessObject,
                NULL,
                pusModuleMask,
                &pModuleInfo);
    
            if (! NT_SUCCESS (status)) goto FINISHED;
    
            //
            // Free local-used objects
            //
    
            KmlibDeleteUnicodeString (&pusModuleMask);
            KmlibDeleteUnicodeString (&pusModuleName);
    
            //
            // Style detection
            //
    
            if (pImport -> TimeDateStamp == 0)
            {
                pRvaImport = (PULONG) (pImageBase + pImport -> FirstThunk);
            }
            else
            {
                pRvaImport = (PULONG) (pImageBase + pImport -> OriginalFirstThunk);
            }
    
            //
            // Process module entries
            //
    
            while (*pRvaImport != 0)
            {
                //
                // Get entry address
                //
    
                if (LdrIsImportByOrdinal (*pRvaImport))
                {
                    pProcAddress = KmlibGetProcedureAddress (pModuleInfo -> ImageBase, (char*) (*pRvaImport & 0xFFFF));
                }
                else
                {
                    pImportedName = (char*) (pImageBase + *pRvaImport + IMPORTED_NAME_OFFSET);
                    pProcAddress = KmlibGetProcedureAddress (pModuleInfo -> ImageBase, pImportedName);
                }
    
                //
                // Check address
                //
    
    
    
                if (! pProcAddress)
                {
                    status = STATUS_PROCEDURE_NOT_FOUND;
                    goto FINISHED;
                }
    
                //
                // Update imported entry
                //
    
                *((PVOID*) pRvaImport) = pProcAddress;
    
                //
                // Next entry within module
                //
    
                pRvaImport ++;
            }
    
            //
            // Local cleanup
            //
    
            KmlibFree (&pModuleInfo);
    
            //
            // Go to next imported module
            //
    
            pImport ++;
        }
    
        //
        // Here we succeeded ;)
        //
    
        status = STATUS_SUCCESS;
    
    FINISHED:
    
        //
        // Cleanup
        //
    
        if (pusModuleName) KmlibDeleteUnicodeString (&pusModuleName);
        if (pusModuleMask) KmlibDeleteUnicodeString (&pusModuleMask);
        if (pModuleInfo) ExFreePool (pModuleInfo);
    
        //
        // Return result
        //
    
        return status;
    }
    
    PIMAGE_BASE_RELOCATION
    LdrProcessRelocationBlock (
    
        ULONG_PTR        uVA,
        ULONG            uSizeOfBlock,
        PUSHORT            puNextOffset,
        LONG            lDelta)
    
    {
        PBYTE pFixupVA = NULL;
        USHORT uOffset = 0;
        LONG lTemp = 0;
        LONG lTempOrig = 0;
        LONGLONG l64Temp64 = 0;
        LONG_PTR lActualDiff = 0;
    
        while (uSizeOfBlock --)
        {
            uOffset = *puNextOffset & ((USHORT) 0xFFF);
            pFixupVA = (PBYTE) (uVA + uOffset);
    
            //
            // Apply the fixups.
            //
    
            switch ((*puNextOffset) >> 12)
            {
                case IMAGE_REL_BASED_HIGHLOW:
    
                    //
                    // HighLow - (32-bits) relocate the high and low half
                    // of an address.
                    //
    
                    *(LONG UNALIGNED*) pFixupVA += (ULONG) lDelta;
    
                    break;
    
                case IMAGE_REL_BASED_HIGH:
    
                    //
                    // High - (16-bits) relocate the high half of an address.
                    //
    
                    lTemp = *(PUSHORT) pFixupVA << 16;
                    lTemp += (ULONG) lDelta;
                    *(PUSHORT) pFixupVA = (USHORT) (lTemp >> 16);
    
                    break;
    
                case IMAGE_REL_BASED_HIGHADJ:
    
                    //
                    // Adjust high - (16-bits) relocate the high half of an
                    // address and adjust for sign extension of low half.
                    //
                    // If the address has already been relocated then don't
                    // process it again now or information will be lost.
                    //
    
                    if (uOffset & LDRP_RELOCATION_FINAL)
                    {
                        puNextOffset ++;
                        uSizeOfBlock --;
    
                        break;
                    }
    
                    lTemp = *(PUSHORT) pFixupVA << 16;
                    lTempOrig = lTemp;
    
                    puNextOffset ++;
                    uSizeOfBlock --;
    
                    lTemp += (LONG) (*(PSHORT) puNextOffset);
                    lTemp += (ULONG) lDelta;
                    lTemp += 0x8000;
    
                    *(PUSHORT) pFixupVA = (USHORT) (lTemp >> 16);
    
                    lActualDiff = ((((ULONG_PTR) (lTemp - lTempOrig)) >> 16) -
                        (((ULONG_PTR) lDelta) >> 16));
    
                    if (lActualDiff == 1)
                    {
                        //
                        // Mark the relocation as needing an increment if it is
                        // relocated again.
                        //
    
                        *(puNextOffset - 1) |= LDRP_RELOCATION_INCREMENT;
                    }
                    else if (lActualDiff != 0)
                    {
                        //
                        // Mark the relocation as cannot be reprocessed.
                        //
    
                        *(puNextOffset - 1) |= LDRP_RELOCATION_FINAL;
                    }
    
                    break;
    
                case IMAGE_REL_BASED_LOW:
    
                    //
                    // Low - (16-bit) relocate the low half of an address.
                    //
    
                    lTemp = *((PSHORT) pFixupVA);
                    lTemp += (ULONG) lDelta;
                    *((PUSHORT) pFixupVA) = (USHORT) lTemp;
    
                    break;
    
                case IMAGE_REL_BASED_ABSOLUTE:
    
                    //
                    // Absolute - no fixup required.
                    //
    
                    break;
    
                case IMAGE_REL_BASED_SECTION:
    
                    //
                    // Section Relative reloc.  Ignore for now.
                    //
    
                    break;
    
                case IMAGE_REL_BASED_REL32:
    
                    //
                    // Relative intrasection. Ignore for now.
                    //
    
                    break;
    
                case IMAGE_REL_BASED_HIGH3ADJ:
    
                    //
                    // Similar to HIGHADJ except this is the third word.
                    // Adjust low half of high ULONG of an address and adjust for
                    // sign extension of the low ULONG.
                    //
    
                    puNextOffset ++;
                    uSizeOfBlock --;
                    
                    l64Temp64 = *(PUSHORT) pFixupVA << 16;
                    l64Temp64 += (LONG) ((SHORT) puNextOffset [1]);
                    l64Temp64 <<= 16;
                    l64Temp64 += (LONG) ((USHORT) puNextOffset [0]);
                    l64Temp64 += lDelta;
                    l64Temp64 += 0x8000;
                    l64Temp64 >>= 16;
                    l64Temp64 += 0x8000;
    
                    *(PUSHORT) pFixupVA = (USHORT) (l64Temp64 >> 16);
                    
                    puNextOffset ++;
                    uSizeOfBlock --;
                    
                    break;
    
                default:
    
                    //
                    // Illegal - illegal relocation type.
                    //
    
                    return (PIMAGE_BASE_RELOCATION) NULL;
            }
    
            puNextOffset ++;
        }
    
        //
        // Return next relocation block
        //
    
        return (PIMAGE_BASE_RELOCATION) puNextOffset;
    }
    
    NTSTATUS
    LdrProcessRelocations (
    
        PBYTE                    pImageBase,
        LONG                    lImageBaseDelta,
        PIMAGE_NT_HEADERS        pNtHeaders,
        PIMAGE_BASE_RELOCATION    pRelocBlocks)
    
    {
        PIMAGE_BASE_RELOCATION pRelocBlock = NULL;
        ULONG uBlockSize = 0;
        PUSHORT pNextOffset = 0;
        ULONG uVA = 0;
        ULONG uRelocsSize = 0;
    
        //
        // Check input parameters
        //
    
        if (! pImageBase) return STATUS_INVALID_PARAMETER;
        if (lImageBaseDelta == 0) return STATUS_INVALID_PARAMETER;
        if (! pNtHeaders) return STATUS_INVALID_PARAMETER;
        if (! pRelocBlocks) return STATUS_INVALID_PARAMETER;
    
        //
        // Get relocation parameters
        //
    
        pRelocBlock = pRelocBlocks;
        uRelocsSize = pNtHeaders -> OptionalHeader.DataDirectory [IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
    
        //
        // Process relocation blocks
        //
    
        while (uRelocsSize > 0)
        {
            //
            // Calculate size of current
            // relocation block
            //
    
            uBlockSize = pRelocBlock -> SizeOfBlock;
            uRelocsSize -= uBlockSize;
    
            uBlockSize -= sizeof (IMAGE_BASE_RELOCATION);
            uBlockSize /= sizeof (USHORT);
    
            //
            // Calculate other parameters
            // needed to process current
            // relocation block
            //
    
            pNextOffset = (PUSHORT) (((PBYTE) pRelocBlock) + sizeof (IMAGE_BASE_RELOCATION));
            uVA = (ULONG) (pImageBase + pRelocBlock -> VirtualAddress);
    
            //
            // Process current relocation block
            //
    
            pRelocBlock = LdrProcessRelocationBlock (uVA, uBlockSize, pNextOffset, lImageBaseDelta);
            if (! pRelocBlock) return STATUS_ILLEGAL_DLL_RELOCATION;
        }
    
        //
        // Success, yep!
        //
    
        return STATUS_SUCCESS;
    }
    
    ULONG
    LdrGetSectionProtection (
    
        ULONG dwCharacteristics)
    
    {
        ULONG dwProtection = 0;
    
        if ((dwCharacteristics & IMAGE_SCN_MEM_NOT_CACHED) != 0)
        {
            dwProtection |= PAGE_NOCACHE;
        }
    
        if ((dwCharacteristics & IMAGE_SCN_MEM_EXECUTE) != 0 &&
            (dwCharacteristics & IMAGE_SCN_MEM_READ) != 0 &&
            (dwCharacteristics & IMAGE_SCN_MEM_WRITE) != 0)
        {
            dwProtection |= PAGE_EXECUTE_READWRITE;
        }
        else if (
            (dwCharacteristics & IMAGE_SCN_MEM_EXECUTE) != 0 &&
            (dwCharacteristics & IMAGE_SCN_MEM_READ) != 0)
        {
            dwProtection |= PAGE_EXECUTE_READ;
        }
        else if (
            (dwCharacteristics & IMAGE_SCN_MEM_READ) != 0 &&
            (dwCharacteristics & IMAGE_SCN_MEM_WRITE) != 0)
        {
            dwProtection |= PAGE_READWRITE;
        }
        else if (
            (dwCharacteristics & IMAGE_SCN_MEM_WRITE) != 0)
        {
            dwProtection |= PAGE_WRITECOPY;
        }
        else if (
            (dwCharacteristics & IMAGE_SCN_MEM_READ) != 0)
        {
            dwProtection |= PAGE_READONLY;
        }
        else
        {
            dwProtection |= PAGE_EXECUTE_READWRITE;
        }
    
        return dwProtection;
    }         
    
    NTSTATUS
    LdrLoadModule (
    
        HANDLE        hProcess,
        PEPROCESS    pProcess,
        PBYTE        pImage,
        PVOID*        ppImageBase,
        PULONG        puSizeOfImage,
        PVOID*        ppEntryPoint,
        HANDLE*        phModuleHandle)
    
    {
        PBYTE pImageBase = NULL;
        LONG lImageBaseDelta = 0;
        PIMAGE_NT_HEADERS pNtHeaders = NULL;
        PIMAGE_SECTION_HEADER pSections = NULL;
        ULONG uSection = 0;
        PBYTE pSource = NULL;
        PBYTE pSectionBase = NULL;
        ULONG uVirtualSectionSize = 0;
        ULONG uRawSectionSize = 0;
        HANDLE hModule = NULL;
        ULONG dwOldProtect = 0;
        NTSTATUS status = STATUS_UNSUCCESSFUL;
        KAPC_STATE kApcState = {0};
        BOOLEAN bAttached = FALSE;
    
        //
        // Check input parameters
        //
    
        if (! hProcess) return STATUS_INVALID_PARAMETER;
        if (! pProcess) return STATUS_INVALID_PARAMETER;
        if (! pImage) return STATUS_INVALID_PARAMETER;
        if (! phModuleHandle) return STATUS_INVALID_PARAMETER;
    
        //
        // Get required pointers
        //
    
        pSource = pImage;
    
        pNtHeaders = (PIMAGE_NT_HEADERS) (pSource + ((PIMAGE_DOS_HEADER) pSource) -> e_lfanew);
        if (! pNtHeaders) return STATUS_INVALID_IMAGE_FORMAT;
    
        //
        // Check image
        //
    
        if (pNtHeaders -> OptionalHeader.SizeOfImage == 0) return STATUS_INVALID_IMAGE_FORMAT;
        if (pNtHeaders -> Signature != IMAGE_NT_SIGNATURE) return STATUS_INVALID_IMAGE_FORMAT;
        if (pNtHeaders -> FileHeader.Machine != IMAGE_FILE_MACHINE_I386) return STATUS_INVALID_IMAGE_FORMAT;
        if (pNtHeaders -> OptionalHeader.SizeOfHeaders >= pNtHeaders -> OptionalHeader.SizeOfImage) return STATUS_INVALID_IMAGE_FORMAT;
    
        //
        // Allocate image memory block
        //
    
        pImageBase = (PBYTE) KmlibVirtualAllocInProcess (
            hProcess,
            pNtHeaders -> OptionalHeader.SizeOfImage);
    
        if (! pImageBase)
        {
            status = STATUS_INSUFFICIENT_RESOURCES;
            goto FINISHED;
        }
    
        //
        // Here we know real image base
        //
    
        hModule = (HANDLE) pImageBase;
    
        //
        // Calculate delta, if it is more than 0,
        // we should process relocation section;
        // it will be done later
        //
    
        lImageBaseDelta = (LONG) (pImageBase - pNtHeaders -> OptionalHeader.ImageBase);
    
        //
        // Attach to the target
        // process's address space
        //
    
        KeStackAttachProcess (pProcess, &kApcState);
        bAttached = TRUE;
    
        //
        // Get section base
        //
    
        pSectionBase = pImageBase + pNtHeaders -> OptionalHeader.SizeOfHeaders;
    
        //
        // Copy headers and make
        // them read-only
        //
    
        RtlCopyMemory (
            pSectionBase,
            pSource,
            pNtHeaders -> OptionalHeader.SizeOfHeaders);
    
        status = KmlibVirtualProtectInProcess (
            hProcess,
            pSectionBase,
            pNtHeaders -> OptionalHeader.SizeOfHeaders,
            PAGE_READONLY);
    
        if (! NT_SUCCESS (status)) goto FINISHED;
    
        //
        // Get sections start address
        //
    
        pSections = (PIMAGE_SECTION_HEADER)
            (((PBYTE) &pNtHeaders -> OptionalHeader) +
            pNtHeaders -> FileHeader.SizeOfOptionalHeader);
    
        //
        // Process sections
        //
    
        for (uSection = 0; uSection < pNtHeaders -> FileHeader.NumberOfSections; uSection ++)
        {
            //
            // Calculate section sizes
            //
    
            uVirtualSectionSize = pSections [uSection].Misc.VirtualSize;
            uRawSectionSize = pSections [uSection].SizeOfRawData;
    
            if (uVirtualSectionSize < uRawSectionSize)
            {
                uVirtualSectionSize = uVirtualSectionSize ^ uRawSectionSize;
                uRawSectionSize = uVirtualSectionSize ^ uRawSectionSize;
                uVirtualSectionSize = uVirtualSectionSize ^ uRawSectionSize;
            }
    
            //
            // Get section base
            //
    
            pSectionBase = pImageBase + pSections [uSection].VirtualAddress;
    
            //
            // Copy section data to the new address
            //
    
            RtlZeroMemory (pSectionBase, uVirtualSectionSize);
            RtlCopyMemory (pSectionBase, pSource + pSections [uSection].PointerToRawData, uRawSectionSize);
        }
    
        //
        // Check relocation section
        //
    
        if (pNtHeaders -> OptionalHeader.DataDirectory [IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress != 0)
        {
            //
            // Process relocations
            //
    
            status = LdrProcessRelocations (
                pImageBase,
                lImageBaseDelta,
                pNtHeaders,
                (PIMAGE_BASE_RELOCATION) (pImageBase + pNtHeaders -> OptionalHeader.DataDirectory [IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress));
    
            if (! NT_SUCCESS (status)) goto FINISHED;
        }
    
        //
        // Check import section
        //
    
        if (pNtHeaders -> OptionalHeader.DataDirectory [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress != 0)
        {
            //
            // Process imports
            //
    
            status = LdrProcessImports (
                pProcess,
                pImageBase,
                pNtHeaders,
                (PIMAGE_IMPORT_DESCRIPTOR) (pImageBase + pNtHeaders -> OptionalHeader.DataDirectory [IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress));
    
            if (! NT_SUCCESS (status)) goto FINISHED;
        }
    
        //
        // Set protection for sections' pages
        //
    
        for (uSection = 0; uSection < pNtHeaders -> FileHeader.NumberOfSections; uSection ++)
        {
            //
            // Set section protection
            //
    
            status = KmlibVirtualProtectInProcess (
                hProcess,
                pImageBase + pSections [uSection].VirtualAddress,
                pSections [uSection].Misc.VirtualSize,
                LdrGetSectionProtection (pSections [uSection].Characteristics));
    
            if (! NT_SUCCESS (status)) goto FINISHED;
        }
    
        //
        // Return requested data to the caller
        //
    
        *phModuleHandle = hModule;
    
        if (ppImageBase) *ppImageBase = pImageBase;
        if (puSizeOfImage) *puSizeOfImage = pNtHeaders -> OptionalHeader.SizeOfImage;
        if (ppEntryPoint) *ppEntryPoint = (PVOID) (pImageBase + pNtHeaders -> OptionalHeader.AddressOfEntryPoint);
    
        //
        // Here we succeeded!
        //
    
        status = STATUS_SUCCESS;
    
    FINISHED:
    
        //
        // Cleanup
        //
    
        if (bAttached)
        {
            KeUnstackDetachProcess (&kApcState);
        }
    
        if (! NT_SUCCESS (status))
        {
            if (pImageBase)
            {
                //
                // Free all the memory
                // we allocated in this routine
                //
    
                KmlibVirtualFreeInProcess (
                    hProcess,
                    &pImageBase);
            }
        }
    
        //
        // Return result
        //
    
        return status;
    }
    
    NTSTATUS
    LdrLoadImage (
    
        PEPROCESS        pTargetProcess,
        PBYTE            pRawImage,
        PVOID*            ppImageBase,
        PVOID*            ppEntryPoint)
    
    {
    
        NTSTATUS status = STATUS_UNSUCCESSFUL;
        HANDLE hTargetProcess = NULL;
        HANDLE hModule = NULL;
    
        //
        // Open process by pointer
        //
    
        status = KmlibObjectOpen (
            pTargetProcess,
            PROCESS_ALL_ACCESS,
            &hTargetProcess);
    
        if (! NT_SUCCESS (status)) goto FINISHED;
    
        //
        // Load image into target process's
        // address space
        //
    
        status = LdrLoadModule (
            hTargetProcess,
            pTargetProcess,
            pRawImage,
            ppImageBase,
            NULL,
            ppEntryPoint,
            &hModule);
    
        if (! NT_SUCCESS (status)) goto FINISHED;
    
        //
        // Success ;)
        //
    
        status = STATUS_SUCCESS;
    
    FINISHED:
    
        //
        // Cleanup
        //
    
        if (hTargetProcess) KmlibClose (&hTargetProcess);
    
        //
        // Return result
        //
    
        return status;
    }


    Пример использования, ну тут ничего хитрого нет:

    PVOID pRawImage = ... ;
    PEPROCESS pProcessObject = ... ;
    PVOID pImageBase = NULL;
    PVOID pEntryPoint = NULL;
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    
    status = LdrLoadImage (
      pProcessObject,
      pRawImage,
      &pImageBase,
      &pEntryPoint);
    
    if (! NT_SUCCESS (status))
    {
      // ошибка
    }


    Ну а как после завершения работы DLL выгрузить её, думаю, проблем не должно быть? Достаточно освободить (MEM_RELEASE) блок виртуальной памяти по адресу pImageBase, при этом размер образа знать не нужно, можно просто в третьем параметре в ZwFreeVirtualMemory() передать указатель на целое, содержащее 0. Разумеется, перед этим сама DLL должна корректно и полностью завершить работу.

    Ну, для чего это может понадобится, я объяснять не буду, — кому это надо и кто в теме, тот знает. Что касается схемы работы, тут могу предложить такой алгоритм:

    1. Делаем ObReferenceObject() на объект целевого процесса.
    2. Внедряем DLL описанным выше способом в целевой процесс и запихиваем любую полезную информацию о процессе и DLL в список.
    3. Создаём системный поток, в качестве контекста передаёт информацию о загруженном модуле (как минимум — адрес базы), а также указатель на EPROCESS целевого процесса.
    4. В системном потоке делаем non-alertable ожидание в KeWaitForSingleObject() на EPROCESS целевого процесса.
    5. После успешного ожидания подчищаем за процессом, т.е. удаляем из ассоциативного списка информацию, которую запихивали на шаге 2.
    6. Делаем ObDereferenceObject() на EPROCESS целевого процесса.

    Информацию, полученную на шаге 2, мы можем использовать, например, для передачи коду загруженной DLL. Кроме того, можно каким-либо образом предусмотреть преждевременную выгрузку DLL из процесса (по инициативе самой DLL), для этого можно использовать именованные события и проч. и проч. Но это уже совсем другая история... Возможно, я расскажу её позже.

    P.S.
    Попутно не могу не съязвить, что хоть автор вот этой вот статейки, Загрузчик PE-файлов
    Автор(ы): Максим М. Гумеров
    Дата: 20.03.2003
    Не вдаваясь в подробности, скажу лишь, что исследование было начато ради сокрытия использования программой на Delphi некоей DLL (написанной на VC++). То есть оператор видит один только Exe-файл, запускает его, а тот каким-то образом подключает функции, содержащиеся изначально (при компиляции проекта) в некоторой DLL.
    , и пишет, что он-де разбирался в вопросе, но его код, отвечающий за настройку релоков — полное говно, китайчеги переплюнули его на раз-два. Выше приводится правильный код.
  • JID: x64j@jabber.ru
    Re: Kernel: Загрузка DLL из памяти (x86)
    От: Аноним  
    Дата: 10.04.09 06:27
    Оценка:
    Здравствуйте, x64.
    в исходнике фигурировали некоторые константы, которые нигде не определены: IMPORTED_NAME_OFFSET
    LDRP_RELOCATION_FINAL
    LDRP_RELOCATION_INCREMENT
    IMAGE_REL_BASED_SECTION
    IMAGE_REL_BASED_REL32
    IMAGE_REL_BASED_HIGH3ADJ
    .
    Где бы можно было узнать их значения?
    Re[2]: Kernel: Загрузка DLL из памяти (x86)
    От: x64 Россия http://x64blog.name
    Дата: 10.04.09 06:46
    Оценка:
    А>Где бы можно было узнать их значения?

    В поиске это есть (у китайцев).

    #define IMPORTED_NAME_OFFSET            0x00000002
    #define IMAGE_ORDINAL_FLAG32            0x80000000
    #define IMAGE_ORDINAL_MASK32            0x0000FFFF
    
    #define LDRP_RELOCATION_INCREMENT        0x1
    #define LDRP_RELOCATION_FINAL            0x2
    
    #define IMAGE_REL_BASED_ABSOLUTE        0
    #define IMAGE_REL_BASED_HIGH            1
    #define IMAGE_REL_BASED_LOW            2
    #define IMAGE_REL_BASED_HIGHLOW        3
    #define IMAGE_REL_BASED_HIGHADJ        4
    #define IMAGE_REL_BASED_MIPS_JMPADDR    5
    #define IMAGE_REL_BASED_SECTION        6
    #define IMAGE_REL_BASED_REL32            7
    #define IMAGE_REL_BASED_HIGH3ADJ        11
    JID: x64j@jabber.ru
    Re: Kernel: Загрузка DLL из памяти (x86)
    От: gear nuke  
    Дата: 17.04.09 01:11
    Оценка:
    Здравствуйте, x64, Вы писали:

    x64>
  • В данном коде отсутствует настройка таблицы экспорта, в случае если DLL экспортирует какие-либо функции.

    Пожалуйста, подробнее, что это такое вообще.

    x64>
  • Обратите внимание, что данный код не добавляет загруженную DLL в список модулей процесса. По моему, совершенно не скромному мнению, это нафиг не нужно

    Согласен, что не нужно. Добавление в Peb.Ldr необходимо для работы функций типа GetProcAddress, то есть что бы позже можно было подгрузить и связать некоторый импортирующий из данной dll модуль. Разумеется никто, кроме загрузившего экспотрирующую dll, это делать не должен. Твоя KmlibGetProcedureAddress может поддерживать свой список, и тогда экспорт будет работать. То есть см. вопрос выше.

    x64>
  • В коде отсутствуют исходники функций с префиксом Kmlib*. Это приватные функции, используемые в коммерческих проектах и я не могу выкладывать их код, однако интересующимся могу объяснить как они реализованы.

    Проще напиши сразу цену Для ленивых искать опенсорс аналоги

    x64>
  • Код оттестирован на 32-битных версиях Windows XP/2003/Vista/2008, работает как часы.

    Мог бы сэкономить на обработке некоторых типов релоков.

    x64>
  • Кстати, ребят, а вы знаете, что этот и ему подобный код вообще-то стоят немалых денег? )))))))))

    Не морочь людям голову, ничего такой код не стоит. Задачи, которые он решает — могут.

    Как получить поддержку Forward Export? Реализовать твою KmlibGetProcedureAddress. Теперь вопрос: как быть с рекурсивной подгрузкой dll, точнее, как дать пользователю возможность гибкого выбора мест для поиска dll (диск, списки загрузчиков)? Да никак — пользователю придётся написать всё самому, разобравшись, и подглядывая в твой образец.
    .
  • People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
    Re[2]: Kernel: Загрузка DLL из памяти (x86)
    От: x64 Россия http://x64blog.name
    Дата: 18.04.09 10:10
    Оценка:
    GN>Пожалуйста, подробнее, что это такое вообще.

    Имелось в виду что-то типа этого
    Автор(ы): Максим М. Гумеров
    Дата: 20.03.2003
    Не вдаваясь в подробности, скажу лишь, что исследование было начато ради сокрытия использования программой на Delphi некоей DLL (написанной на VC++). То есть оператор видит один только Exe-файл, запускает его, а тот каким-то образом подключает функции, содержащиеся изначально (при компиляции проекта) в некоторой DLL.
    . Моя DLL, которую я загружал этим
    Автор: x64
    Дата: 06.12.08
    способом, ничего не экспортировала и к её экспортам никто не обращался.

    GN>Проще напиши сразу цену


    Ну я ничего не продаю здесь, в общем, — просто поделился чем мог.

    GN>Не морочь людям голову, ничего такой код не стоит. Задачи, которые он решает — могут.


    Это вот ты сейчас с высоты своего опыта и своих знаний говоришь. А поди попробуй поручить такую задачу не-системщику, что на выходе получишь и через сколько времени? Так что да, для людей знающих код практически ничего не стоит, тут без базара. А вот кому это нужно, но кто не шарит — те просто берут и платят, да ты и сам это знаешь, что я тебе тут рассказываю...

    GN>Как получить поддержку Forward Export? Реализовать твою KmlibGetProcedureAddress. Теперь вопрос: как быть с рекурсивной подгрузкой dll, точнее, как дать пользователю возможность гибкого выбора мест для поиска dll (диск, списки загрузчиков)? Да никак — пользователю придётся написать всё самому, разобравшись, и подглядывая в твой образец.


    Ну дык, кто ж тебе готовое решение выложит да со всеми юдобствами. Кстати, для полноты картины мог бы и ссылки привести на опен-сорс аналоги, а то и на "готовые решения", если знаешь. Я вот не знаю таких, хотя я и не искал сильно...
    JID: x64j@jabber.ru
    Re[3]: Kernel: Загрузка DLL из памяти (x86)
    От: gear nuke  
    Дата: 20.04.09 03:20
    Оценка: 14 (1)
    Здравствуйте, x64, Вы писали:

    x64>Имелось в виду что-то типа этого
    Автор(ы): Максим М. Гумеров
    Дата: 20.03.2003
    Не вдаваясь в подробности, скажу лишь, что исследование было начато ради сокрытия использования программой на Delphi некоей DLL (написанной на VC++). То есть оператор видит один только Exe-файл, запускает его, а тот каким-то образом подключает функции, содержащиеся изначально (при компиляции проекта) в некоторой DLL.
    .


    Спасибо, со 2го раза дошло... раньше этот момент упускал из виду, слепо отпарваляя всех к статье.

    Не знаю, насколько правилен подобный подход. Загрузчик ОС работает совершенно по другому — forward export обрабатывается при резолве импорта, LdrGetProcedureAddress (точнее, LdrpSnapThunk) подгружает переадресуемую dll только в момент непосредственной необходимости, что похоже на архитектурно грамотное решение.

    x64> Моя DLL, которую я загружал этим
    Автор: x64
    Дата: 06.12.08
    способом, ничего не экспортировала и к её экспортам никто не обращался.


    Она использовала функции вроде kernel32!HeapAlloc? Без исходного кода KmlibGetProcedureAddress неясно как, и будет ли, обработан форвард.

    x64>Это вот ты сейчас с высоты своего опыта и своих знаний говоришь. А поди попробуй поручить такую задачу не-системщику, что на выходе получишь и через сколько времени?


    Не-системщики это кто, изучавшие полный курс CS? Не пробовал, но видел достаточно работающих реализаций, значит у кого-то получается. Я, как промэлектронщик по образованию, не вижу в задаче каких-либо сложностей: читаем доки и пишем код. Это в общем-то тривиальное "программирование микропроцессорных систем", software development'ом тут, к сожалению, или счастью не пахнет.

    x64>Ну дык, кто ж тебе готовое решение выложит да со всеми юдобствами.


    7 строк, раз уж написал, что ничего не стоит

    Задача в общем-то из 2х частей состоит: разместить образ в памяти (с учётом выравнивания секций) и настроить импорты-релоки.

    Первая часть решается разными способами, например, документировано — через маппинг секции (должно быть здесь
    Автор(ы): Сторожевых Сергей
    Дата: 14.11.2007
    При решении многих задач системного программированния зачастую бывает необходимо загрузить динамически подключаемую библиотеку (DLL) в адресное пространство другого процесса, с целью исследования либо изменения его поведения. В данной статье показан способ, позволяющий внедрить DLL в любой процесс (в том числе защищенный) на самом раннем этапе его создания.
    ), либо через "VirtualAlloc", как у тебя (что тоже документировано в общем-то)...
    Есть еще самый простой ход конём — dll собирается с одинаковыми выравниваниями, если образ уже размещён в пуле по адресу dll_data и приатачено АП нужного процесса, можно сделать как ниже.

    Вторая часть доступна в исходниках любого пакера, либо в ntl::pe::image:

      using namespace ntl;
      using namespace ntl::km;
      
      // получаем размер имиджа из PE заголовка
      pe::image * const um_image = pe::image::bind(dll_data);
      const size_t um_image_size = um_image->get_nt_headers()->OptionalHeader32.SizeOfImage;
      
      // проецируем в UM 
      mdl * pmdl = new(um_image, um_image_size) mdl;
      pmdl->build_for_non_paged_pool();
      pe::image * const um_map = pe::image::bind(pmdl->map_locked_pages(UserMode));
    
      // релоцируем
      um_map->relocate(uintptr_t(um_map) - um_map->get_nt_headers()->OptionalHeader32.ImageBase);
      
      // связываем импорт
      um_map->bind_import(nt::ldr_data_table_entry::find_dll(&peb->Ldr->InLoadOrderModuleList));

    Этого должно быть достаточно для поддержки форвард-экспортов на уже загруженные в память dll. Нужно еще указатель на PEB, который можно получить например так:
    template<typename Fn>
    Fn * get_nt_proc(const char name[])
    {
      const system_information<system_modules_information> system_modules;
      if ( system_modules )
      {
        const rtl_process_module_information * nt = system_modules->find_module(0);
        if ( nt ) return nt->image()->find_export<Fn *>(name);
      }
      return 0;
    }
    
    static nt::peb * get_process_peb(kprocess * process)
    {
      static get_process_peb_t * get_peb = get_nt_proc<get_process_peb_t>("PsGetProcessPeb");
      return get_peb ? get_peb(process) : (nt::peb*)0x7ffdf000; // constant on 2K
    }

    Если требуется подгружать модули с диска, то придётся написать для этого функтор (подробнее здесь). Однако остальной код изменений не потребует, "кубики" работают в любом окружении.

    Конечно, за кадром осталась еще куча нюансов вроде "в какой момент можно так делать". Но многие из них будут зависить от конкретного случая, в библиотечном коде их все не учтёшь — поэтому переложено с больной головы на здоровую — вот в этом основное удобство Это не шутка, иначе б у либы не было пользователей.
    .
    People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
     
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.