首页
社区
课程
招聘
[原创] 分享一个简短的内存加载DLL EXE
2019-5-7 16:14 10342

[原创] 分享一个简短的内存加载DLL EXE

2019-5-7 16:14
10342
实现类似功能的代码很多,但是我这份没有参考到别人的代码,纯粹是看微软文档撸出来的。这个需求是源于我的一个比较复杂的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。

/*
    See https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format
*/
#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 {
            DWORD64  Unused1:63;
            DWORD64  OrdinalIfSet:1;
        } Flag;
        DWORD64 Long;
#else
        struct {
            DWORD  Unused1:31;
            DWORD  OrdinalIfSet: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[0];
} 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[IMAGE_DIRECTORY_ENTRY_EXPORT].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[i]);

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

    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[i].Offset;
        printf("0x%.4x\t%d ", Offset, BRB->TypeOffset[i].Type);

        switch(BRB->TypeOffset[i].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[i].PointerToRawData, SEEK_SET);
        fread(LLRVA(Sections[i].VirtualAddress), Sections[i].SizeOfRawData, 1, libfp);
        printf("%s\n", Sections[i].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[1]);

    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[IMAGE_DIRECTORY_ENTRY_BASERELOC].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[IMAGE_DIRECTORY_ENTRY_IMPORT].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;
}



[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。

最后于 2019-6-14 20:19 被malokch编辑 ,原因: 修一出bug
收藏
点赞2
打赏
分享
最新回复 (12)
雪    币: 4709
活跃值: (1549)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
如斯咩咩咩 2019-5-8 12:17
2
0
占楼,吃瓜
雪    币: 6014
活跃值: (3925)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
pxhb 2 2019-5-8 12:22
3
0
感谢分享
雪    币: 707
活跃值: (1301)
能力值: ( LV9,RANK:190 )
在线值:
发帖
回帖
粉丝
qyc 4 2019-5-8 15:06
4
0
吃瓜,占楼,谢谢楼主分享学习
雪    币: 37
活跃值: (1928)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
X-Blades 2019-5-8 17:04
5
0
借鉴楼主代码 学习一下
雪    币: 2507
活跃值: (5297)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
quanIngo 2019-5-8 17:15
6
0
谢谢分享经验
雪    币: 221
活跃值: (82)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ntoskrnlwin 2019-5-8 17:43
7
0
谢谢分享
雪    币: 3113
活跃值: (28)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
vae力 2019-5-9 09:36
8
0
感谢分享
雪    币: 920
活跃值: (1580)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
wuxiwudi 2019-5-9 09:52
9
0
反射?
雪    币: 19586
活跃值: (60203)
能力值: (RANK:125 )
在线值:
发帖
回帖
粉丝
Editor 2019-5-10 09:22
10
0
666 感谢分享~
雪    币: 310
活跃值: (1917)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
niuzuoquan 2019-5-10 14:53
11
0
mark
雪    币: 6
活跃值: (152)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
一学徒工 2019-6-8 22:40
12
0
头蒙 不管用
雪    币: 247
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wx_G-DRAGON 2019-7-25 14:31
13
0
感谢分享
游客
登录 | 注册 方可回帖
返回