brahmana 发表于 2022-8-13 14:45:46

分享一个简短的内存加载DLL EXE C++源码

实现类似功能的代码很多,但是我这份没有参考到别人的代码,纯粹是看微软文档撸出来的。这个需求是源于我的一个比较复杂的x86_64 shellcode,需要实现的功能很多,依赖也很多。因此用传统的方法弄的shellcode很不方便。于是我想着直接用dll/exe当作shellcode,写一个简短的linker控制在0x1000字节以内,将linker直接填在PE头处,调用时直接call头部的linker实现dll/exe的self link并执行。需求已经测试通过,完整项目不能发出来,但是可以跟大家分享验证阶段的测试代码。

主要原理:
SDK_GetProcAddress 解析kernel32拿到LoadLibraryA和GetProcAddress供linker使用。如果不是裸的代码,这里不需要。
SDK_LoadLibrary 解析dos头和nt头,并将section展开到内存。
RelocImage进行image base重定向
LinkDLL加载和连接依赖的dll

这里并没有处理TLS和32位的支持。这个有机会再说吧。

以下代码用mingw-w64编译,可加载运行vs和mingw-w64编译出来的pe。



#include <windows.h>
#include <stdio.h>
#include <string.h>

#include "winapi.h"

#ifndef MIN
#define MIN(a, b) ((a) > (b) ? (b) : (a))
#endif

#define RVA(a) ((PVOID)((ULONG_PTR)hExecutable + (a)))
#define K32RVA(a) ((PVOID)((ULONG_PTR)hKernel32 + (a)))

#define API pContext->

typedef struct {
    union {
#ifdef _WIN64
      struct {
            DWORD64Unused1:63;
            DWORD64OrdinalIfSet:1;
      } Flag;
      DWORD64 Long;
#else
      struct {
            DWORDUnused1:31;
            DWORDOrdinalIfSet:1;
      } Flag;
      DWORD Long;
#endif
      WORD OrdinalNumber;
      DWORD NameAddress;
    } u;
} IMPORT_LOOKUP_DESC, *PIMPORT_LOOKUP_DESC;

typedef struct {
    LoadLibraryA_t LoadLibraryA;
    GetProcAddress_t GetProcAddress;
    GetModuleHandleA_t GetModuleHandleA;
} LINK_CONTEXT, *PLINK_CONTEXT;

typedef struct {
    DWORD Page;
    DWORD BlockSize;
    struct {
      WORD Offset:12;
      WORD Type:4;
    } TypeOffset;
} BASE_RELOCATION_BLOCK, *PBASE_RELOCATION_BLOCK;

HMODULE hExecutable = NULL;

int SDK_strcmp(const char *s1, const char *s2)
{
    while(*s1 && *s2) {
      if (*s1 != *s2)
            return -1;
      s1 ++;
      s2 ++;
    }
    return *s2 - *s1;
}

PVOID SDK_GetProcAddress(HMODULE hKernel32, const char *FuncName)
{
    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)hKernel32;
    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((char *)pDos + pDos->e_lfanew);

    PIMAGE_EXPORT_DIRECTORY EDT = (PIMAGE_EXPORT_DIRECTORY)K32RVA(pNt->OptionalHeader.DataDirectory.VirtualAddress);

    // printf("DLL Name: %s\n", K32RVA(EDT->Name));
    // printf("Number Of Functions: %d\n", EDT->NumberOfFunctions);
    // printf("Number Of Names: %d\n", EDT->NumberOfNames);

    PDWORD ExportAddressTable = (PDWORD)MOD_RVA(EDT->AddressOfFunctions);
    PDWORD ExportNamePointerTable = (PDWORD)MOD_RVA(EDT->AddressOfNames);
    PWORD ExportOrdinalTable= (PWORD)MOD_RVA(EDT->AddressOfNameOrdinals);

    for(size_t i = 0; i < EDT->NumberOfNames; i++)
    {
      PCHAR name = (PCHAR)MOD_RVA(ExportNamePointerTable);

      if (SDK_strcmp(name, FuncName) == 0) {
            WORD Ordinal = ExportOrdinalTable;
            return (PVOID)MOD_RVA(ExportAddressTable);
      }
    }

    return NULL;
}

void LinkDLL(PLINK_CONTEXT pContext, PIMAGE_IMPORT_DESCRIPTOR IDT)
{
    const char* DLLName = (const char*)RVA(IDT->Name);
    HMODULE hDLL = API GetModuleHandleA(DLLName);

    if (!hDLL) {
      hDLL = API LoadLibraryA(DLLName);
    }

    if (!hDLL) {
      return -1;
    }

    printf("** %s **\n", DLLName);

    PIMPORT_LOOKUP_DESC ILD = (PIMPORT_LOOKUP_DESC)RVA(IDT->OriginalFirstThunk);
    PULONG_PTR IAT = (PULONG_PTR)RVA(IDT->FirstThunk);

    while (ILD->u.Long) {
      if (!ILD->u.Flag.OrdinalIfSet) {
            *IAT = (ULONG_PTR) API GetProcAddress(hDLL, (PCSTR)RVA(ILD->u.NameAddress+2));
            printf("%s %p\n", RVA(ILD->u.NameAddress+2), *IAT);
      } else {
            *IAT = (ULONG_PTR) API GetProcAddress(hDLL, (PCSTR)MAKEINTRESOURCE(ILD->u.OrdinalNumber));
            printf("%d %p\n", RVA(ILD->u.OrdinalNumber), *IAT);
      }
      IAT ++;
      ILD ++;
    }
}

void RelocImage(PBASE_RELOCATION_BLOCK BRB, ptrdiff_t Difference)
{
    printf("Difference: %ld\n", Difference);
    for(size_t i = 0; i < (BRB->BlockSize - 8)/2; i++)
    {
      WORD Offset = BRB->TypeOffset.Offset;
      printf("0x%.4x\t%d ", Offset, BRB->TypeOffset.Type);

      switch(BRB->TypeOffset.Type) {
            case IMAGE_REL_BASED_ABSOLUTE:break; // The base relocation is skipped
            case IMAGE_REL_BASED_HIGHLOW: // TODO Not test yet
                *(DWORD*)RVA(BRB->Page + Offset) += Difference;
                break;
            case IMAGE_REL_BASED_DIR64:
                printf("DIR64 %lx => ", *(ptrdiff_t*)RVA(BRB->Page + Offset));
                *(ptrdiff_t*)RVA(BRB->Page + Offset) += Difference;
                printf("%lx", *(ptrdiff_t*)RVA(BRB->Page + Offset));
                break;
            default:
                fprintf(stderr, "Not support this relocation type yet.");
                abort();
                break;
      }
      putchar('\n');
    }
}

#define LLRVA(a) ((PVOID)((char*)Image + (a)))

HMODULE SDK_LoadLibrary(PCSTR LibraryName)
{
    IMAGE_DOS_HEADER DosHdr;
    IMAGE_NT_HEADERS NtHdr;
    HMODULE hModule = NULL;

    FILE *libfp = fopen(LibraryName, "rb");

    if (!libfp)
      goto CLEAN;

    fread(&DosHdr, sizeof(IMAGE_DOS_HEADER), 1, libfp);

    fseek(libfp, DosHdr.e_lfanew, SEEK_SET);
    fread(&NtHdr, sizeof(IMAGE_NT_HEADERS), 1, libfp);

    PVOID Image = VirtualAlloc(NULL, NtHdr.OptionalHeader.SizeOfImage, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if (!Image)
      goto CLEAN;

    fseek(libfp, 0, SEEK_SET);
    fread(Image, NtHdr.OptionalHeader.SizeOfHeaders, 1, libfp);

    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((char *)Image + DosHdr.e_lfanew);

    PIMAGE_SECTION_HEADER Sections = IMAGE_FIRST_SECTION(pNt);

    for(size_t i = 0; i < pNt->FileHeader.NumberOfSections; i++)
    {
      fseek(libfp, Sections.PointerToRawData, SEEK_SET);
      fread(LLRVA(Sections.VirtualAddress), Sections.SizeOfRawData, 1, libfp);
      printf("%s\n", Sections.Name);
    }

    hModule = (HMODULE)Image;
CLEAN:
    if (libfp)
      fclose(libfp);
    return hModule;
}

void SDK_FreeLibrary(HMODULE hModule)
{
    VirtualFree(hModule, 0, MEM_RELEASE);
}

int main(int argc, const char *argv[])
{
    LINK_CONTEXT Context;

    if (argc != 2) {
      fprintf(stderr, "Missing dll\n");
      return -1;
    }

    HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");
   
    Context.LoadLibraryA = (LoadLibraryA_t)SDK_GetProcAddress(hKernel32, "LoadLibraryA");
    Context.GetProcAddress = (GetProcAddress_t)SDK_GetProcAddress(hKernel32, "GetProcAddress");
    Context.GetModuleHandleA = (GetModuleHandleA_t)SDK_GetProcAddress(hKernel32, "GetModuleHandleA");

    printf("LoadLibraryA\t\t%p\n", Context.LoadLibraryA);
    printf("GetProcAddress\t\t%p\n", Context.GetProcAddress);
    printf("GetModuleHandleA\t%p\n", Context.GetModuleHandleA);

    hExecutable = SDK_LoadLibrary(argv);

    if (hExecutable == NULL) {
      fprintf(stderr, "Can not load dll\n");
      exit(-1);
    }

    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)hExecutable;
    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((char *)pDos + pDos->e_lfanew);

    if ((PVOID)hExecutable != (PVOID)pNt->OptionalHeader.ImageBase)
    {

      printf("Image Base: %p\n", pNt->OptionalHeader.ImageBase);
      printf("Loaded Base: %p\n", hExecutable);

      ptrdiff_t Difference = (ptrdiff_t)hExecutable - (ptrdiff_t)pNt->OptionalHeader.ImageBase;

      PBASE_RELOCATION_BLOCK BRB = (PBASE_RELOCATION_BLOCK)RVA(pNt->OptionalHeader.DataDirectory.VirtualAddress);

      while (BRB->BlockSize) {
            RelocImage(BRB, Difference);
            BRB = (PBASE_RELOCATION_BLOCK)((char*)BRB + BRB->BlockSize);
      }
    }

    PIMAGE_IMPORT_DESCRIPTOR IDT = (PIMAGE_IMPORT_DESCRIPTOR)RVA(pNt->OptionalHeader.DataDirectory.VirtualAddress);
    while (IDT->Name != 0) {
      LinkDLL(&Context, IDT);
      IDT ++;
    }

    ((void(*)(HMODULE,DWORD,PVOID))RVA(pNt->OptionalHeader.AddressOfEntryPoint))(hExecutable, DLL_PROCESS_ATTACH, NULL);

    SDK_FreeLibrary(hExecutable);

    return 0;
}
页: [1]
查看完整版本: 分享一个简短的内存加载DLL EXE C++源码