

#ifndef _WINPE_H
#define _WINPE_H
#define WINPE_VERSION 370

#ifdef USECOMPAT
#include "commdef_v100.h"
#else
#include "commdef.h"
#endif // USECOMPAT

// define specific macro
#ifdef WINPE_API
#undef WINPE_API
#endif
#ifdef WINPE_API_DEF
#undef WINPE_API_DEF
#endif
#ifdef WINPE_API_EXPORT
#undef WINPE_API_EXPORT
#endif
#ifdef WINPE_API_INLINE
#undef WINPE_API_INLINE
#endif

#ifdef WINPE_STATIC
#define WINPE_API_DEF static
#else
#define WINPE_API_DEF extern
#endif // WINPE_STATIC
#ifdef WINPE_SHARED
#define WINPE_API_EXPORT EXPORT
#else
#define WINPE_API_EXPORT
#endif // WINPE_SHARED
#ifdef WINPE_NOINLINE
#define WINPE_API_INLINE
#else
#define WINPE_API_INLINE INLINE
#endif // WINPE_NOINLINE

#define WINPE_API WINPE_API_DEF WINPE_API_EXPORT WINPE_API_INLINE


// declear
#ifdef __cplusplus
extern "C" {
#endif //  __cplusplus
#include <stdint.h>
#include <windows.h>
#include <winternl.h>

typedef struct _RELOCOFFSET
{
    WORD offset : 12;
    WORD type   : 4;
}RELOCOFFSET,*PRELOCOFFSET;

typedef int bool_t;

typedef HMODULE (WINAPI *PFN_LoadLibraryA)(
    LPCSTR lpLibFileName);

typedef FARPROC (WINAPI *PFN_GetProcAddress)(
    HMODULE hModule, LPCSTR lpProcName);

typedef PFN_GetProcAddress PFN_GetProcRVA;

typedef LPVOID (WINAPI *PFN_VirtualAlloc)(
    LPVOID lpAddress, SIZE_T dwSize, 
    DWORD  flAllocationType, DWORD flProtect);

typedef BOOL (WINAPI *PFN_VirtualFree)(
    LPVOID lpAddress, SIZE_T dwSize, 
    DWORD dwFreeType);

typedef BOOL (WINAPI *PFN_VirtualProtect)(
    LPVOID lpAddress, SIZE_T dwSize,
    DWORD  flNewProtect, PDWORD lpflOldProtect);

typedef SIZE_T (WINAPI *PFN_VirtualQuery)(
    LPCVOID lpAddress,
    PMEMORY_BASIC_INFORMATION lpBuffer,
    SIZE_T dwLength);

typedef BOOL (WINAPI *PFN_DllMain)(HINSTANCE hinstDLL,
    DWORD fdwReason, LPVOID lpReserved );

#define WINPE_LDFLAG_MEMALLOC 0x1
#define WINPE_LDFLAG_MEMFIND 0x2

/**
 * load the origin rawpe file in memory buffer by mem align
 * mempe means the pe in memory alignment
 * @param pmemsize mempe buffer size
 * @return mempe buf
*/
WINPE_API
void* STDCALL winpe_memload_file(const char *path, size_t *pmemsize, bool_t same_align);

/**
 * load the overlay data in a pe file
 * @param pmemsize overlay size
 * @return overlay buf
*/
WINPE_API 
void* STDCALL winpe_overlayload_file(const char *path, size_t *poverlaysize);

/**
 * similar to LoadlibrayA, 
 * will load the mempe in a valid imagebase
 * @return hmodule base
*/
WINPE_API 
void* STDCALL winpe_memLoadLibrary(void *mempe);

/**
 * load the mempe in a valid imagebase, will call dll entry
 * @param imagebase if 0, will load on mempe, else in imagebase
 * @param flag WINPE_LDFLAG_MEMALLOC 0x1, will alloc memory to imagebase
 *             WINPE_LDFLAG_MEMFIND 0x2, will find a valid space, 
 * @return hmodule base
*/
WINPE_API
void* STDCALL winpe_memLoadLibraryEx(void *mempe, size_t imagebase, DWORD flag,
    PFN_LoadLibraryA pfnLoadLibraryA, PFN_GetProcAddress pfnGetProcAddress);

/**
 * similar to FreeLibrary, will call dll entry
 * @return True on successful
*/
WINPE_API
BOOL STDCALL winpe_memFreeLibrary(void *mempe);

WINPE_API
BOOL STDCALL winpe_memFreeLibraryEx(void *mempe, 
    PFN_LoadLibraryA pfnLoadLibraryA, PFN_GetProcAddress pfnGetProcAddress);


/**
 * similar to GetProcAddress
 * @return function va
*/
WINPE_API
PROC STDCALL winpe_memGetProcAddress(void *mempe, const char *funcname);

/**
 * use peb and ldr list, to obtain to find kernel32.dll address
 * @return kernel32.dll va
*/
WINPE_API
void* winpe_findkernel32();

/**
 * use peb and ldr list, similar as GetModuleHandleA
 * @return ldr module address
*/
WINPE_API
void* STDCALL winpe_findmoduleaex(PPEB peb, const char *modulename);

WINPE_API
void* STDCALL winpe_findmodulea(const char *modulename)
{
    return winpe_findmoduleaex(NULL, modulename);
}

/**
 * @return LoadLibraryA func addr
*/
WINPE_API
PROC winpe_findloadlibrarya();

/**
 * @return GetProcAddress func addr
*/
WINPE_API
PROC winpe_findgetprocaddress();

/**
 * find a valid space address start from imagebase with imagesize
 * @return va
*/
WINPE_API
void* STDCALL winpe_findspace(
    size_t imagebase, size_t imagesize, size_t alignsize,
    PFN_VirtualQuery pfnVirtualQuery);

/**
 * @return the overlay offset
*/
WINPE_API
size_t STDCALL winpe_overlayoffset(const void *rawpe);

/**
 * load the origin rawpe in memory buffer by mem align
 * @return memsize
*/
WINPE_API
size_t STDCALL winpe_memload(const void *rawpe, size_t rawsize, 
    void *mempe, size_t memsize, bool_t same_align);

/**
 * realoc the addrs for the mempe addr as image base
 * origin image base usually at 0x00400000, 0x0000000180000000
 * new image base mush be divided by 0x10000, if use loadlibrary
 * @return reloc count
*/
WINPE_API
size_t STDCALL winpe_memreloc(void *mempe, size_t newimagebase);

/**
 * load the iat for the mempe, use rvafunc for winpe_memfindexp
 * @return iat count
*/
WINPE_API
size_t STDCALL winpe_membindiat(void *mempe, 
    PFN_LoadLibraryA pfnLoadLibraryA, PFN_GetProcAddress pfnGetProcAddress);

/**
 * exec the tls callbacks for the mempe, before dll oep load
 * @param reason for function PIMAGE_TLS_CALLBACK
 * @return tls count
*/
WINPE_API
size_t STDCALL winpe_membindtls(void *mempe, DWORD reason);

/**
 * find the iat addres, for call [iat]
 * @return target iat va
*/
WINPE_API
void* STDCALL winpe_memfindiat(void *mempe, LPCSTR dllname, LPCSTR funcname);

/**
 * find the exp  addres, the same as GetProcAddress, 
 * without forward to other dll, such as NTDLL.RtlInitializeSListHead
 * @return target exp va
*/
WINPE_API
void* STDCALL winpe_memfindexp(void *mempe, LPCSTR funcname);

WINPE_API
void* STDCALL winpe_memfindexpcrc32(void* mempe, uint32_t crc32);

/**
 * forward the exp to the final expva
 * @return the final exp va
*/
WINPE_API
void* STDCALL winpe_memforwardexp(void *mempe, size_t exprva, 
    PFN_LoadLibraryA pfnLoadLibraryA, PFN_GetProcAddress pfnGetProcAddress);

/**
 * change the oep of the pe if newoeprva!=0
 * @return the old oep rva
*/
WINPE_API
DWORD STDCALL winpe_oepval(void *mempe, DWORD newoeprva);

/**
 * change the imagebase of the pe if newimagebase!=0
 * @return the old imagebase va
*/
WINPE_API
size_t STDCALL winpe_imagebaseval(void *mempe, size_t newimagebase);

/**
 * change the imagesize of the pe if newimagesize!=0
 * @return the old imagesize
*/
WINPE_API
size_t STDCALL winpe_imagesizeval(void *pe, size_t newimagesize);

/**
 * set off the aslr feature of an pe
*/
WINPE_API
void STDCALL winpe_noaslr(void *pe);

/**
 * append a section header in a pe, sect rva will be ignored
 * the mempe size must be enough for extend a section
 * @return image size
*/
WINPE_API
size_t STDCALL winpe_appendsecth(void *mempe, PIMAGE_SECTION_HEADER psecth);


#ifdef __cplusplus
}
#endif // __cplusplus


// implement
#ifdef WINPE_IMPLEMENTATION
#if defined(__TINYC__)
#ifdef _WIN64
#pragma pack(8)
#else
#pragma pack(4)
#endif
#endif

#include <stdio.h>
#include <assert.h>
#include <windows.h>
#include <winternl.h>

void* STDCALL winpe_memload_file(const char* path, size_t* pmemsize, bool_t same_align)
{
    if (!path) {
        if (pmemsize) *pmemsize = 0;
        return NULL; // Invalid path
    }

    // Open file with error checking
    FILE* fp = fopen(path, "rb");
    if (!fp) {
        perror("fopen failed");
        if (pmemsize) *pmemsize = 0;
        return NULL; // File not found or inaccessible
    }

    // Get file size safely
    if (fseek(fp, 0, SEEK_END) != 0) {
        fclose(fp);
        if (pmemsize) *pmemsize = 0;
        return NULL; // Seek failed
    }
    long rawsize_long = ftell(fp);
    if (rawsize_long <= 0 || rawsize_long > SIZE_MAX) {
        fclose(fp);
        if (pmemsize) *pmemsize = 0;
        return NULL; // Invalid file size
    }
    size_t rawsize = (size_t)rawsize_long;
    rewind(fp);

    // Debug: Log the size before allocation
    printf("Allocating %zu bytes for rawpe (path: %s)\n", rawsize, path);

    // Alternative allocation with VirtualAlloc (Windows-specific workaround)
    void* rawpe = NULL;
    HMODULE hKernel32 = GetModuleHandleA("kernel32.dll");
    if (hKernel32) {
        FARPROC pVirtualAlloc = GetProcAddress(hKernel32, "VirtualAlloc");
        if (pVirtualAlloc) {
            rawpe = ((LPVOID(WINAPI*)(LPVOID, SIZE_T, DWORD, DWORD))pVirtualAlloc)(
                NULL, rawsize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
        }
    }
    // Fallback to malloc if VirtualAlloc isn't available or fails
    if (!rawpe) {
        printf("GOING FOR MALLOC! \n");

        rawpe = malloc(rawsize);
    }
    if (!rawpe) {
        perror("Allocation failed for rawpe");
        printf("Failed to allocate %zu bytes\n", rawsize);
        fclose(fp);
        if (pmemsize) *pmemsize = 0;
        return NULL; // Memory allocation failed
    }

    // Debug: Confirm allocation succeeded
    printf("Successfully allocated %zu bytes at %p\n", rawsize, rawpe);

    // Read file into buffer
    size_t bytes_read = fread(rawpe, 1, rawsize, fp);
    fclose(fp);
    if (bytes_read != rawsize) {
        printf("Read failed: expected %zu, got %zu\n", rawsize, bytes_read);
        if (hKernel32 && rawpe) {
            FARPROC pVirtualFree = GetProcAddress(hKernel32, "VirtualFree");
            if (pVirtualFree) {
                ((BOOL(WINAPI*)(LPVOID, SIZE_T, DWORD))pVirtualFree)(rawpe, 0, MEM_RELEASE);
            }
        }
        else
        {
            free(rawpe);
        }
        if (pmemsize) *pmemsize = 0;
        return NULL; // Incomplete read
    }

    // Calculate required memory size for aligned PE
    void* mempe = NULL;
    if (pmemsize) {
        *pmemsize = winpe_memload(rawpe, rawsize, NULL, 0, FALSE);
        if (*pmemsize == 0) {
            printf("winpe_memload failed to calculate size\n");
            if (hKernel32 && rawpe) {
                FARPROC pVirtualFree = GetProcAddress(hKernel32, "VirtualFree");
                if (pVirtualFree) {
                    ((BOOL(WINAPI*)(LPVOID, SIZE_T, DWORD))pVirtualFree)(rawpe, 0, MEM_RELEASE);
                }
            }
            else
            {
                free(rawpe);
            }
            return NULL;
        }

        mempe = malloc(*pmemsize);
        if (!mempe) {
            perror("malloc failed for mempe");
            printf("Failed to allocate %zu bytes\n", *pmemsize);
            if (hKernel32 && rawpe) {
                FARPROC pVirtualFree = GetProcAddress(hKernel32, "VirtualFree");
                if (pVirtualFree) {
                    ((BOOL(WINAPI*)(LPVOID, SIZE_T, DWORD))pVirtualFree)(rawpe, 0, MEM_RELEASE);
                }
            }
            else
            {
                free(rawpe);
            }
            *pmemsize = 0;
            return NULL;
        }

        size_t loaded_size = winpe_memload(rawpe, rawsize, mempe, *pmemsize, same_align);
        if (loaded_size == 0) {
            printf("winpe_memload failed to load PE\n");
            free(mempe);
            if (hKernel32 && rawpe) {
                FARPROC pVirtualFree = GetProcAddress(hKernel32, "VirtualFree");
                if (pVirtualFree) {
                    ((BOOL(WINAPI*)(LPVOID, SIZE_T, DWORD))pVirtualFree)(rawpe, 0, MEM_RELEASE);
                }
            }
            else
            {
                free(rawpe);
            }
            *pmemsize = 0;
            return NULL;
        }
    }

    if (hKernel32 && rawpe) {
        FARPROC pVirtualFree = GetProcAddress(hKernel32, "VirtualFree");
        if (pVirtualFree) {
            ((BOOL(WINAPI*)(LPVOID, SIZE_T, DWORD))pVirtualFree)(rawpe, 0, MEM_RELEASE);
        }
    }
    else
    {
        free(rawpe);
    }
    return mempe;
}

void* STDCALL winpe_memload_buffer(const void* rawpe, size_t rawsize, size_t* pmemsize, bool_t same_align)
{
    void* mempe = NULL;
    if (pmemsize)
    {
        *pmemsize = winpe_memload(rawpe, rawsize, NULL, 0, FALSE);
        mempe = malloc(*pmemsize);
        winpe_memload(rawpe, rawsize, mempe, *pmemsize, same_align);
    }
    return mempe;
}
 
void* STDCALL winpe_overlayload_file(const char *path, size_t *poverlaysize)
{
    FILE *fp = fopen(path, "rb");
    fseek(fp, 0, SEEK_END);
    size_t rawsize = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    void *rawpe = malloc(rawsize);
    fread(rawpe, 1, rawsize, fp);
    fclose(fp);
    void *overlay = NULL;
    size_t overlayoffset = winpe_overlayoffset(rawpe);
    
    if(poverlaysize)
    {
        *poverlaysize = rawsize - overlayoffset;
        if(*poverlaysize>0)
        {
            overlay = malloc(*poverlaysize);
            memcpy(overlay, (uint8_t*)rawpe+overlayoffset, *poverlaysize);
        }
    }
    free(rawpe);
    return overlay;
}

void* STDCALL winpe_memLoadLibrary(void *mempe)
{
    PFN_LoadLibraryA pfnLoadLibraryA = (PFN_LoadLibraryA)winpe_findloadlibrarya();
    PFN_GetProcAddress pfnGetProcAddress = (PFN_GetProcAddress)winpe_findgetprocaddress();
    return winpe_memLoadLibraryEx(mempe, 0, 
                WINPE_LDFLAG_MEMFIND | WINPE_LDFLAG_MEMALLOC, 
                pfnLoadLibraryA, pfnGetProcAddress);
}

void* STDCALL winpe_memLoadLibraryEx(void *mempe, size_t imagebase, DWORD flag,
    PFN_LoadLibraryA pfnLoadLibraryA, PFN_GetProcAddress pfnGetProcAddress)
{
    // bind windows api
    char name_kernel32[] = { 'k', 'e', 'r', 'n', 'e', 'l', '3', '2', '.', 'd', 'l', 'l' , '\0'};
    char name_VirtualQuery[] = {'V', 'i', 'r', 't', 'u', 'a', 'l', 'Q', 'u', 'e', 'r', 'y', '\0'};
    char name_VirtualAlloc[] = {'V', 'i', 'r', 't', 'u', 'a', 'l', 'A', 'l', 'l', 'o', 'c', '\0'};
    char name_VirtualProtect[] = {'V', 'i', 'r', 't', 'u', 'a', 'l', 'P', 'r', 'o', 't', 'e', 'c', 't', '\0'};
    HMODULE hmod_kernel32 = pfnLoadLibraryA(name_kernel32);
    PFN_VirtualQuery pfnVirtualQuery = (PFN_VirtualQuery)pfnGetProcAddress(hmod_kernel32, name_VirtualQuery);
    PFN_VirtualAlloc pfnVirtualAlloc = (PFN_VirtualAlloc)pfnGetProcAddress(hmod_kernel32, name_VirtualAlloc);
    PFN_VirtualProtect pfnVirtualProtect =(PFN_VirtualProtect)pfnGetProcAddress(hmod_kernel32, name_VirtualProtect);
#ifdef _DEBUG
    assert(pfnVirtualQuery!=0 && pfnVirtualAlloc!=0 && pfnVirtualProtect!=0);
#endif

    // find proper imagebase
    size_t imagesize = winpe_imagesizeval(mempe, 0);
    if(flag & WINPE_LDFLAG_MEMFIND)
    {
        imagebase = winpe_imagebaseval(mempe, 0);
        imagebase = (size_t)winpe_findspace(imagebase, imagesize, 0x10000, pfnVirtualQuery);
    }
    if(flag & WINPE_LDFLAG_MEMALLOC) // find proper memory to reloc
    {

        imagebase = (size_t)pfnVirtualAlloc((void*)imagebase, imagesize, 
                            MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
        if(!imagebase) // try alloc in arbitary place
        {
            imagebase = (size_t)pfnVirtualAlloc(NULL, imagesize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
            if(!imagebase) return NULL;
        }
        else
        {
            imagebase = (size_t)pfnVirtualAlloc((void*)imagebase, imagesize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
            if(!imagebase) return NULL;            
        }
    }

    // copy to imagebase
    if(!imagebase) 
    {
        imagebase = (size_t)mempe;
    }
    else
    {
        DWORD oldprotect;
        pfnVirtualProtect((void*)imagebase, imagesize, PAGE_EXECUTE_READWRITE, &oldprotect);
        inl_memcpy((void*)imagebase, mempe, imagesize);
        pfnVirtualProtect((void*)imagebase, imagesize, oldprotect, &oldprotect);
    }

    // initial memory module
    if(!winpe_memreloc((void*)imagebase, imagebase)) return NULL;
    if(!winpe_membindiat((void*)imagebase, pfnLoadLibraryA, pfnGetProcAddress)) return NULL;
    winpe_membindtls(mempe, DLL_PROCESS_ATTACH);
    PFN_DllMain pfnDllMain = (PFN_DllMain)(imagebase + winpe_oepval((void*)imagebase, 0));
    pfnDllMain((HINSTANCE)imagebase, DLL_PROCESS_ATTACH, NULL);
    return (void*)imagebase;
}

BOOL STDCALL winpe_memFreeLibrary(void *mempe)
{
    PFN_LoadLibraryA pfnLoadLibraryA = (PFN_LoadLibraryA)winpe_findloadlibrarya();
    PFN_GetProcAddress pfnGetProcAddress = (PFN_GetProcAddress)winpe_findgetprocaddress();
    return winpe_memFreeLibraryEx(mempe, pfnLoadLibraryA, pfnGetProcAddress);
}

BOOL STDCALL winpe_memFreeLibraryEx(void *mempe, 
    PFN_LoadLibraryA pfnLoadLibraryA, PFN_GetProcAddress pfnGetProcAddress)
{
    char name_kernel32[] = {'k', 'e', 'r', 'n', 'e', 'l', '3', '2', '\0'};
    char name_VirtualFree[] = {'V', 'i', 'r', 't', 'u', 'a', 'l', 'F', 'r', 'e', 'e', '\0'};
    HMODULE hmod_kernel32 = pfnLoadLibraryA(name_kernel32);
    PFN_VirtualFree pfnVirtualFree = (PFN_VirtualFree)pfnGetProcAddress(hmod_kernel32, name_VirtualFree);
    PFN_DllMain pfnDllMain = (PFN_DllMain)((uint8_t*)mempe + winpe_oepval(mempe, 0));
    winpe_membindtls(mempe, DLL_PROCESS_DETACH);
    pfnDllMain((HINSTANCE)mempe, DLL_PROCESS_DETACH, NULL);
    return pfnVirtualFree(mempe, 0, MEM_FREE);
}

PROC STDCALL winpe_memGetProcAddress(void *mempe, const char *funcname)
{
    // this function might failed if inline and -Os, -O3, if in dll function, use printf might help
    void* expva = winpe_memfindexp(mempe, funcname);
    size_t exprva = (size_t)expva - (size_t)mempe;
    // printf("[winpe_memGetProcAddress] expva=%p\n", expva);
    
    // use pfnLoadLibraryA, pfnGetProcAddress to avoid infinity loop
    void *hmod_kernel32 = winpe_findkernel32();
    char name_GetProcAddress[] = {'G', 'e', 't', 'P', 'r', 'o', 'c', 'A', 'd', 'd', 'r', 'e', 's', 's', '\0'};
    char name_LoadLibraryA[] = {'L', 'o', 'a', 'd', 'L', 'i', 'b', 'r', 'a', 'r', 'y', 'A', '\0'};
    char *name[2] = {name_LoadLibraryA, name_GetProcAddress};
    void *func[2] = {NULL, NULL};
    for(int i=0; i<2;i++)
    {
        func[i] = winpe_memfindexp(hmod_kernel32, name[i]);
    }
    PFN_LoadLibraryA pfnLoadLibraryA =  (PFN_LoadLibraryA)func[0];
    PFN_GetProcAddress pfnGetProcAddress = (PFN_GetProcAddress)func[1];
    return (PROC)winpe_memforwardexp(mempe, exprva, pfnLoadLibraryA, pfnGetProcAddress); 
}

// PE query functions
void* winpe_findkernel32()
{
    // return (void*)LoadLibrary("kernel32.dll");
    // TEB->PEB->Ldr->InMemoryOrderLoadList->curProgram->ntdll->kernel32
    void *kerenl32 = NULL;

#ifdef WINPE_NOASM
    char name_kernel32[] = { 'k', 'e', 'r', 'n', 'e', 'l', '3', '2', '.', 'd', 'l', 'l' , '\0' };
    kerenl32 = winpe_findmodulea(name_kernel32);
#else
#if defined(__GNUC__)
// can not use -masm=intel, as there are some ATT in intrin-impl.h
#ifdef _WIN64
    asm("mov %%gs:0x60, %%rax\n" // peb
        "mov 0x18(%%rax), %%rax\n" // ldr 
        "mov 0x20(%%rax), %%rax\n" // InMemoryOrderLoadList, currentProgramEntry
        "mov (%%rax), %%rax\n" // ntdllEntry, currentProgramEntry->->Flink
        "mov (%%rax), %%rax\n" // kernel32Entry,  ntdllEntry->Flink
        "mov 0x30-0x10(%%rax), %%rax\n" // kernel32.DllBase
        "mov %%rax, %0\n":"=m"(kerenl32));
#else
    asm("mov %%fs:0x30, %%eax\n" //  peb
        "mov 0xc(%%eax), %%eax\n" //  ldr
        "mov 0x14(%%eax), %%eax\n" //  InMemoryOrderLoadList, currentProgramEntry
        "mov (%%eax), %%eax\n" //  ntdllEntry, currentProgramEntry->->Flink
        "mov (%%eax), %%eax\n" //  kernel32Entry,  ntdllEntry->Flink
        "mov -0x8+0x18(%%eax), %%eax\n" //  kernel32.DllBase
        "mov %%eax, %0\n":"=m"(kerenl32));
#endif // _WIN64
#else
#ifdef _WIN64
    __asm{
        mov rax, gs:[60h]; peb
        mov rax, [rax+18h]; ldr
        mov rax, [rax+20h]; InMemoryOrderLoadList, currentProgramEntry
        mov rax, [rax]; ntdllEntry, currentProgramEntry->->Flink
        mov rax, [rax]; kernel32Entry,  ntdllEntry->Flink
        mov rax, [rax-10h+30h]; kernel32.DllBase
        mov kerenl32, rax;
    }
#else
    __asm{
        mov eax, fs:[30h]; peb
        mov eax, [eax+0ch]; ldr
        mov eax, [eax+14h]; InMemoryOrderLoadList, currentProgramEntry
        mov eax, [eax]; ntdllEntry, currentProgramEntry->->Flink
        mov eax, [eax]; kernel32Entry,  ntdllEntry->Flink
        mov eax, [eax - 8h +18h]; kernel32.DllBase
        mov kerenl32, eax;
    }
#endif // _WIN64
#endif // __GNUC__
#endif // WINPE_NOASM
    return kerenl32;
}

void* STDCALL winpe_findmoduleaex(PPEB peb, const char *modulename)
{
    typedef struct _LDR_ENTRY  // has 3 kinds of pointer link list
    {
        LIST_ENTRY InLoadOrderLinks; // this has link pointer
        LIST_ENTRY InMemoryOrderLinks; // order is program, ntdll, kernel32.dll
        LIST_ENTRY InInitializationOrderLinks;//to next entry in same place
        PVOID DllBase; // 0x18, 0x30
        PVOID EntryPoint;
        ULONG SizeOfImage;
        UNICODE_STRING FullDllName;
        UNICODE_STRING BaseDllName;
        ULONG Flags;
        USHORT LoadCount;
        USHORT TlsIndex;
        union
        {
            LIST_ENTRY HashLinks;
            struct
            {
                PVOID SectionPointer;
                ULONG CheckSum;
            };
        };
        ULONG TimeDateStamp;
    } LDR_ENTRY, *PLDR_ENTRY; // use this because of mingw bug
    
    PLDR_ENTRY ldrentry = NULL;
    PPEB_LDR_DATA ldr = NULL;


    if(!peb)
    {
            PTEB teb = NtCurrentTeb(); 
#ifdef _WIN64
            peb = *(PPEB*)((uint8_t*)teb + 0x60);
#else
            peb = *(PPEB*)((uint8_t*)teb + 0x30);
#endif 
    }

#ifdef _WIN64
    ldr = *(PPEB_LDR_DATA*)((uint8_t*)peb + 0x18);
#else
    ldr = *(PPEB_LDR_DATA*)((uint8_t*)peb + 0xC);
#endif 

    // InMemoryOrderModuleList is the second entry
    ldrentry = (PLDR_ENTRY)((size_t)ldr->InMemoryOrderModuleList.Flink - 2*sizeof(size_t));
    if(!modulename)
    {
        return ldrentry->DllBase;
    }
    while(ldrentry->InMemoryOrderLinks.Flink != ldr->InMemoryOrderModuleList.Flink)
    {
        PUNICODE_STRING ustr = &ldrentry->FullDllName;
        int i;
        for(i=ustr->Length/2-1; i>0 && ustr->Buffer[i]!='\\';i--);
        if(ustr->Buffer[i]=='\\') i++;
        if(inl_stricmp2(modulename,  ustr->Buffer + i)==0)
        {
            return ldrentry->DllBase;
        }
        ldrentry = (PLDR_ENTRY)((size_t)ldrentry->InMemoryOrderLinks.Flink - 2*sizeof(size_t));
    }
    return NULL;
}

PROC winpe_findloadlibrarya()
{
    // -Os might make this function not current, such as strcpy(Flink, "LoadLibraryA");
    void* hmod_kernel32 = winpe_findkernel32();
    char name_LoadLibraryA[] = {'L', 'o', 'a', 'd', 'L', 'i', 'b', 'r', 'a', 'r', 'y', 'A', '\0'};
    // suppose exp no forward, to avoid recursive
    return (PROC)winpe_memfindexp(hmod_kernel32, name_LoadLibraryA);
}

PROC winpe_findgetprocaddress()
{
    void* hmod_kernel32 = winpe_findkernel32();
    char name_GetProcAddress[] = {'G', 'e', 't', 'P', 'r', 'o', 'c', 'A', 'd', 'd', 'r', 'e', 's', 's', '\0'};
    return (PROC)winpe_memfindexp(hmod_kernel32, name_GetProcAddress);
}

void* STDCALL winpe_findspace(size_t imagebase, 
    size_t imagesize, size_t alignsize, PFN_VirtualQuery pfnVirtualQuery)
{
#define MAX_QUERY 0x1000
    size_t addr = imagebase;
    MEMORY_BASIC_INFORMATION minfo;
    for (int i=0;i<MAX_QUERY;i++)
    {
        if(addr % alignsize) addr += alignsize - addr% alignsize;
        pfnVirtualQuery((LPVOID)addr, &minfo, sizeof(MEMORY_BASIC_INFORMATION));
        if(minfo.State==MEM_FREE && minfo.RegionSize >= imagesize) return (void*)addr;
        addr += minfo.RegionSize;
    }
    return NULL;
}

// PE load, adjust functions
size_t STDCALL winpe_overlayoffset(const void *rawpe)
{
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)rawpe;
    PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)rawpe + pDosHeader->e_lfanew);
    PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader;
    PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader;
    PIMAGE_DATA_DIRECTORY pDataDirectory = pOptHeader->DataDirectory;
    PIMAGE_SECTION_HEADER pSectHeader = (PIMAGE_SECTION_HEADER)((uint8_t*)pOptHeader + pFileHeader->SizeOfOptionalHeader);
    WORD sectNum = pFileHeader->NumberOfSections;
    return pSectHeader[sectNum-1].PointerToRawData + pSectHeader[sectNum-1].SizeOfRawData;
}

size_t STDCALL winpe_memload(const void *rawpe, size_t rawsize, 
    void *mempe, size_t memsize, bool_t same_align)
{
    // load rawpe to memalign
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)rawpe;
    PIMAGE_NT_HEADERS  pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)rawpe + pDosHeader->e_lfanew);
    PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader;
    PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader;
    PIMAGE_SECTION_HEADER pSectHeader = (PIMAGE_SECTION_HEADER)((uint8_t*)pOptHeader + pFileHeader->SizeOfOptionalHeader);
    WORD sectNum = pFileHeader->NumberOfSections;
    size_t imagesize = pOptHeader->SizeOfImage;
    if(!mempe) return imagesize;
    else if(memsize!=0 && memsize<imagesize) return 0;

    inl_memset(mempe, 0, imagesize);
    inl_memcpy(mempe, rawpe, pOptHeader->SizeOfHeaders);
    
    for(WORD i=0;i<sectNum;i++)
    {
        inl_memcpy((uint8_t*)mempe+pSectHeader[i].VirtualAddress, 
            (uint8_t*)rawpe+pSectHeader[i].PointerToRawData, pSectHeader[i].SizeOfRawData);
    }

    // adjust all to mem align
    if(same_align)
    {
        pDosHeader = (PIMAGE_DOS_HEADER)mempe;
        pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)mempe + pDosHeader->e_lfanew);
        pFileHeader = &pNtHeader->FileHeader;
        pOptHeader = &pNtHeader->OptionalHeader;
        pSectHeader = (PIMAGE_SECTION_HEADER)((uint8_t*)pOptHeader + pFileHeader->SizeOfOptionalHeader);
        sectNum = pFileHeader->NumberOfSections;
        pOptHeader->FileAlignment = pOptHeader->SectionAlignment;
        for(WORD i=0;i<sectNum;i++)
        {
            pSectHeader[i].PointerToRawData = pSectHeader[i].VirtualAddress;
        }
    }
    return imagesize;
}

size_t STDCALL winpe_memreloc(void *mempe, size_t newimagebase)
{
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mempe;
    PIMAGE_NT_HEADERS  pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)mempe + pDosHeader->e_lfanew);
    PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader;
    PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader;
    PIMAGE_DATA_DIRECTORY pDataDirectory = pOptHeader->DataDirectory;
    PIMAGE_DATA_DIRECTORY pRelocEntry = &pDataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
	
    DWORD reloc_count = 0;
    DWORD reloc_offset = 0;
    int64_t shift = (int64_t)newimagebase - (int64_t)pOptHeader->ImageBase;
	while (reloc_offset < pRelocEntry->Size)
	{
		PIMAGE_BASE_RELOCATION pBaseReloc = (PIMAGE_BASE_RELOCATION)
            ((uint8_t*)mempe + pRelocEntry->VirtualAddress + reloc_offset);
        PRELOCOFFSET pRelocOffset = (PRELOCOFFSET)((uint8_t*)pBaseReloc + sizeof(IMAGE_BASE_RELOCATION));
		DWORD item_num = (pBaseReloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(RELOCOFFSET);
		for (size_t i = 0; i < item_num; i++)
		{
			if (!pRelocOffset[i].type && !pRelocOffset[i].offset) continue;
			DWORD targetoffset = pBaseReloc->VirtualAddress + pRelocOffset[i].offset;
            size_t *paddr = (size_t *)((uint8_t*)mempe + targetoffset);
            size_t relocaddr = (size_t)((int64_t)*paddr + shift);
            //printf("reloc 0x%08x->0x%08x\n", *paddr, relocaddr);
            *paddr = relocaddr;
		}
		reloc_offset += sizeof(IMAGE_BASE_RELOCATION) + sizeof(RELOCOFFSET) * item_num;
		reloc_count += item_num;
	}
    pOptHeader->ImageBase = newimagebase;
	return reloc_count;
}
 
size_t STDCALL winpe_membindiat(void *mempe, 
    PFN_LoadLibraryA pfnLoadLibraryA, PFN_GetProcAddress pfnGetProcAddress)
{
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mempe;
    PIMAGE_NT_HEADERS  pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)mempe + pDosHeader->e_lfanew);
    PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader;
    PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader;
    PIMAGE_DATA_DIRECTORY pDataDirectory = pOptHeader->DataDirectory;
    PIMAGE_DATA_DIRECTORY pImpEntry = &pDataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
    PIMAGE_IMPORT_DESCRIPTOR pImpDescriptor =  (PIMAGE_IMPORT_DESCRIPTOR)((uint8_t*)mempe + pImpEntry->VirtualAddress);

    PIMAGE_THUNK_DATA pFtThunk = NULL;
    PIMAGE_THUNK_DATA pOftThunk = NULL;
    LPCSTR pDllName = NULL;
    PIMAGE_IMPORT_BY_NAME pImpByName = NULL;
    size_t funcva = 0;
    char *funcname = NULL;

    // origin GetProcAddress will crash at InitializeSListHead 
    if(!pfnLoadLibraryA) pfnLoadLibraryA = (PFN_LoadLibraryA)winpe_findloadlibrarya();
    if(!pfnGetProcAddress) pfnGetProcAddress = (PFN_GetProcAddress)winpe_findgetprocaddress();

    DWORD iat_count = 0;
    for (; pImpDescriptor->Name; pImpDescriptor++) 
    {
        pDllName = (LPCSTR)((uint8_t*)mempe + pImpDescriptor->Name);
        pFtThunk = (PIMAGE_THUNK_DATA)((uint8_t*)mempe + pImpDescriptor->FirstThunk);
        pOftThunk = (PIMAGE_THUNK_DATA)((uint8_t*)mempe + pImpDescriptor->OriginalFirstThunk);
        size_t dllbase = (size_t)pfnLoadLibraryA(pDllName);
        if(!dllbase) return 0;

        for (int j=0; pFtThunk[j].u1.Function &&  pOftThunk[j].u1.Function; j++) 
        {
            size_t _addr = (size_t)((uint8_t*)mempe + pOftThunk[j].u1.AddressOfData);
            if(sizeof(size_t)>4) // x64
            {
                if(((uint64_t)_addr>>63) == 1)
                {
                    funcname = (char *)(_addr & 0x000000000000ffff);
                }
                else
                {
                    pImpByName=(PIMAGE_IMPORT_BY_NAME)_addr;
                    funcname = pImpByName->Name;
                }
            }
            else
            {
                if(((size_t)pImpByName>>31) == 1)
                {
                    funcname = (char *)(_addr & 0x0000ffff);
                }
                else
                {
                    pImpByName=(PIMAGE_IMPORT_BY_NAME)_addr;
                    funcname = pImpByName->Name;
                }
            }

            funcva = (size_t)pfnGetProcAddress((HMODULE)dllbase, funcname);
            if(!funcva) continue;
            pFtThunk[j].u1.Function = funcva;
#ifdef _DEBUG
            assert(funcva == (size_t)GetProcAddress((HMODULE)dllbase, funcname));
#endif
            iat_count++;
        }
    }
    return iat_count;
}

size_t STDCALL winpe_membindtls(void *mempe, DWORD reason)
{
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mempe;
    PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)mempe + pDosHeader->e_lfanew);
    PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader;
    PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader;
    PIMAGE_DATA_DIRECTORY pDataDirectory = pOptHeader->DataDirectory;
    PIMAGE_DATA_DIRECTORY pTlsDirectory = &pDataDirectory[IMAGE_DIRECTORY_ENTRY_TLS];
    if(!pTlsDirectory->VirtualAddress) return 0;

    size_t tls_count = 0;
    PIMAGE_TLS_DIRECTORY pTlsEntry = (PIMAGE_TLS_DIRECTORY)((uint8_t*)mempe + pTlsDirectory->VirtualAddress);
    PIMAGE_TLS_CALLBACK *tlscb= (PIMAGE_TLS_CALLBACK*)pTlsEntry->AddressOfCallBacks;
    if(tlscb)
    {
        while(*tlscb)
        {
            (*tlscb)(mempe, reason, NULL);
            tlscb++;
            tls_count++;
        }
    }
    return tls_count;
}

void* STDCALL winpe_memfindiat(void *mempe, LPCSTR dllname, LPCSTR funcname)
{
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mempe;
    PIMAGE_NT_HEADERS  pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)mempe + pDosHeader->e_lfanew);
    PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader;
    PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader;
    PIMAGE_DATA_DIRECTORY pDataDirectory = pOptHeader->DataDirectory;
    PIMAGE_DATA_DIRECTORY pImpEntry =  &pDataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
    PIMAGE_IMPORT_DESCRIPTOR pImpDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((uint8_t*)mempe + pImpEntry->VirtualAddress);
    PIMAGE_THUNK_DATA pFtThunk = NULL;
    PIMAGE_THUNK_DATA pOftThunk = NULL;
    LPCSTR pDllName = NULL;
    PIMAGE_IMPORT_BY_NAME pImpByName = NULL;

    for (; pImpDescriptor->Name; pImpDescriptor++) 
    {
        pDllName = (LPCSTR)((uint8_t*)mempe + pImpDescriptor->Name);
        if(dllname && inl_stricmp(pDllName, dllname)!=0) continue;
        pFtThunk = (PIMAGE_THUNK_DATA)((uint8_t*)mempe + pImpDescriptor->FirstThunk);
        pOftThunk = (PIMAGE_THUNK_DATA)((uint8_t*)mempe + pImpDescriptor->OriginalFirstThunk);

        for (int j=0; pFtThunk[j].u1.Function &&  pOftThunk[j].u1.Function; j++) 
        {
            pImpByName=(PIMAGE_IMPORT_BY_NAME)((uint8_t*)mempe + pOftThunk[j].u1.AddressOfData);
            if((size_t)funcname < MAXWORD) // ordinary
            {
                WORD funcord = LOWORD(funcname);
                if(pImpByName->Hint == funcord) return &pFtThunk[j];
            }
            else
            {
                if(inl_stricmp(pImpByName->Name, funcname)==0) return &pFtThunk[j];
            }
        }
    }
    return 0;
}

void* STDCALL winpe_memfindexp(void *mempe, LPCSTR funcname)
{
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mempe;
    PIMAGE_NT_HEADERS  pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)mempe + pDosHeader->e_lfanew);
    PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader;
    PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader;
    PIMAGE_DATA_DIRECTORY pDataDirectory = pOptHeader->DataDirectory;
    PIMAGE_DATA_DIRECTORY pExpEntry =  &pDataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
    PIMAGE_EXPORT_DIRECTORY  pExpDescriptor =  (PIMAGE_EXPORT_DIRECTORY)((uint8_t*)mempe + pExpEntry->VirtualAddress);

    WORD *ordrva = (WORD*)((uint8_t*)mempe + pExpDescriptor->AddressOfNameOrdinals);
    DWORD *namerva = (DWORD*)((uint8_t*)mempe + pExpDescriptor->AddressOfNames);
    DWORD *funcrva = (DWORD*)((uint8_t*)mempe + pExpDescriptor->AddressOfFunctions);
    if((size_t)funcname <= MAXWORD) // find by ordnial
    {
        WORD ordbase = LOWORD(pExpDescriptor->Base) - 1;
        WORD funcord = LOWORD(funcname);
        return (void*)((uint8_t*)mempe + funcrva[ordrva[funcord-ordbase]]);
    }
    else
    {
        for(DWORD i=0;i<pExpDescriptor->NumberOfNames;i++)
        {
            LPCSTR curname = (LPCSTR)((uint8_t*)mempe+namerva[i]);
            if(inl_stricmp(curname, funcname)==0)
            {
                return (void*)((uint8_t*)mempe + funcrva[ordrva[i]]);
            }       
        }
    }
    return NULL;
}

void* STDCALL winpe_memfindexpcrc32(void* mempe, uint32_t crc32)
{
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)mempe;
    PIMAGE_NT_HEADERS  pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)mempe + pDosHeader->e_lfanew);
    PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader;
    PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader;
    PIMAGE_DATA_DIRECTORY pDataDirectory = pOptHeader->DataDirectory;
    PIMAGE_DATA_DIRECTORY pExpEntry = &pDataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
    PIMAGE_EXPORT_DIRECTORY  pExpDescriptor =(PIMAGE_EXPORT_DIRECTORY)((uint8_t*)mempe + pExpEntry->VirtualAddress);

    WORD* ordrva = (WORD*)((uint8_t*)mempe + pExpDescriptor->AddressOfNameOrdinals);
    DWORD* namerva = (DWORD*)((uint8_t*)mempe + pExpDescriptor->AddressOfNames);
    DWORD* funcrva = (DWORD*)((uint8_t*)mempe + pExpDescriptor->AddressOfFunctions);
    for (DWORD i = 0; i < pExpDescriptor->NumberOfNames; i++)
    {
        LPCSTR curname = (LPCSTR)((uint8_t*)mempe + namerva[i]);
        if (crc32==inl_crc32(curname, inl_strlen(curname)))
        {
            return (void*)((uint8_t*)mempe + funcrva[ordrva[i]]);
        }
    }
    return NULL;
}

void* STDCALL winpe_memforwardexp(void *mempe, size_t exprva, 
    PFN_LoadLibraryA pfnLoadLibraryA, PFN_GetProcAddress pfnGetProcAddress)
{
    // this function might have infinite loop
    // kerenl32.dll, GetProcessMitigationPolicy -> api-ms-win-core-processthreads-l1-1-1.dll -> kerenl32.dll, GetProcessMitigationPolicys
    size_t dllbase = (size_t)mempe;
    while (1)
    {
        PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)dllbase;
        PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)dllbase + pDosHeader->e_lfanew);
        PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader;
        PIMAGE_DATA_DIRECTORY pDataDirectory = pOptHeader->DataDirectory;
        PIMAGE_DATA_DIRECTORY pExpEntry = &pDataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
        if(exprva >= pExpEntry->VirtualAddress && exprva < pExpEntry->VirtualAddress + pExpEntry->Size)
        {
            char namebuf[MAX_PATH];
            char *dllname = (char*)(dllbase + exprva);
            char *funcname = dllname;
            //printf("[winpe_memforwardexp] dllname=%s funcname=%s\n", dllname, funcname);
            int i=0, j=0;
            while(dllname[i]!=0)
            {
                if(dllname[i]=='.')
                {
                    namebuf[j] = dllname[i];
                    namebuf[++j] = 'd';
                    namebuf[++j] = 'l';
                    namebuf[++j] = 'l';
                    namebuf[++j] = '\0';
                    funcname = namebuf + j + 1;
                }
                else
                {
                    namebuf[j] = dllname[i];
                }
                i++;
                j++;
            }
            namebuf[j] = '\0';
            dllname = namebuf;
            dllbase = (size_t)pfnLoadLibraryA(dllname);
            size_t expva = (size_t)pfnGetProcAddress((HMODULE)dllbase, funcname);
            exprva = expva - dllbase;
        }
        else
        {
            return (void*)(dllbase + exprva);
        } 
    }
    return NULL;
}

// PE setting function
void STDCALL winpe_noaslr(void *pe)
{
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pe;
    PIMAGE_NT_HEADERS  pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)pe + pDosHeader->e_lfanew);
    PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader;
    PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader;
    #ifndef IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
    #define IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE 0x0040
    #endif
    pOptHeader->DllCharacteristics &= ~IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE;
}

DWORD STDCALL winpe_oepval(void *pe, DWORD newoeprva)
{
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pe;
    PIMAGE_NT_HEADERS  pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)pe + pDosHeader->e_lfanew);
    PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader;
    PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader;
    DWORD orgoep = pOptHeader->AddressOfEntryPoint;
    if(newoeprva) pOptHeader->AddressOfEntryPoint = newoeprva;
    return orgoep;
}

size_t STDCALL winpe_imagebaseval(void *pe, size_t newimagebase)
{
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pe;
    PIMAGE_NT_HEADERS  pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)pe + pDosHeader->e_lfanew);
    PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader;
    PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader;
    size_t imagebase = pOptHeader->ImageBase;
    if(newimagebase) pOptHeader->ImageBase = newimagebase;
    return imagebase; 
}

size_t STDCALL winpe_imagesizeval(void *pe, size_t newimagesize)
{
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pe;
    PIMAGE_NT_HEADERS  pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)pe + pDosHeader->e_lfanew);
    PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader;
    PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader;
    size_t imagesize = pOptHeader->SizeOfImage;
    if(newimagesize) pOptHeader->SizeOfImage = (DWORD)newimagesize;
    return imagesize; 
}

size_t STDCALL winpe_appendsecth(void *pe, PIMAGE_SECTION_HEADER psecth)
{
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pe;
    PIMAGE_NT_HEADERS  pNtHeader = (PIMAGE_NT_HEADERS)((uint8_t*)pe + pDosHeader->e_lfanew);
    PIMAGE_FILE_HEADER pFileHeader = &pNtHeader->FileHeader;
    PIMAGE_OPTIONAL_HEADER pOptHeader = &pNtHeader->OptionalHeader;
    PIMAGE_SECTION_HEADER pSectHeader = (PIMAGE_SECTION_HEADER)((uint8_t*)pOptHeader + pFileHeader->SizeOfOptionalHeader);
    WORD sectNum = pFileHeader->NumberOfSections;
    PIMAGE_SECTION_HEADER pLastSectHeader = &pSectHeader[sectNum-1];
    DWORD addr, align;

    // check the space to append section
    if(pFileHeader->SizeOfOptionalHeader + sizeof(IMAGE_SECTION_HEADER)> pSectHeader[0].PointerToRawData) return 0;

    // fill rva addr
    align = pOptHeader->SectionAlignment;
    addr = pLastSectHeader->VirtualAddress + pLastSectHeader->Misc.VirtualSize;
    if(addr % align) addr += align - addr%align;
    psecth->VirtualAddress = addr;

    // fill file offset
    align = pOptHeader->FileAlignment;
    addr =  pLastSectHeader->PointerToRawData+ pLastSectHeader->SizeOfRawData;
    if(addr % align) addr += align - addr%align;
    psecth->PointerToRawData = addr;

    // adjust the section and imagesize 
    pFileHeader->NumberOfSections++;
    inl_memcpy(&pSectHeader[sectNum], psecth, sizeof(IMAGE_SECTION_HEADER));
    align = pOptHeader->SectionAlignment;
    addr = psecth->VirtualAddress + psecth->Misc.VirtualSize;
    if(addr % align) addr += align - addr%align;
    pOptHeader->SizeOfImage = addr; 
    return pOptHeader->SizeOfImage;
}

#endif // WINPE_IMPLEMENTATION
#endif // _WINPE_H
