Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Implementing Software [email protected]

adr
April 20, 2018

Implementing Software [email protected]

Software developers wish to prevent their applications from cracking, analyzing by others, even looking for vulnerabilities. Generally, developers protect their applications by software packer, e.g. reducing the size of software by UPX Packer, using VMProtect Packer to obfuscate the execution logic, or using Themida Packer to deal with more complicated issues.

* What's PE structure?
* How compilers build a PE like executable file
* The logic of creating processes in PE Loader
* Case Study- RunPE — Malware Injection like a Loader
* Build your packer in C

adr

April 20, 2018
Tweet

More Decks by adr

Other Decks in Technology

Transcript

  1. Implementing Software Packer
    [email protected]

    View Slide

  2. •Core member of CHROOT Security Group and
    TDOHacker security community in Taiwan.

    •Ten years of experience in reverse engineering
    and machine language, also mastered the intel
    8086. Expert in Windows vulnerability, reverse
    engineering.
    •Many papers presented in security conferences
    such as BSidesLV, ICNC, MC2015 and CISC.

    •Speaker at BlackHat Asia Arsenal, HITCON
    (Hackers In Taiwan Conference), SITCON (Students
    In Taiwan Conference), iThome#Chatbot.
    ./bio

    View Slide

  3. Today...
    We're talking about How to
    Build A Software Packer

    View Slide

  4. Today...
    We're talking about How to
    Build A Software Packer
    Nope, Is Application Loader

    View Slide

  5. Exe File?
    The Portable Executable (PE) format is a file format for executables, object
    code, DLLs, FON Font files, and others used in 32-bit and 64-bit versions of
    Windows operating systems.

    The PE format is a data structure that encapsulates the information
    necessary for the Windows OS loader to manage the wrapped executable
    code. This includes dynamic library references for linking, API export and
    import tables, resource management data and thread-local storage (TLS)
    data.

    On NT operating systems, the PE format is used for EXE, DLL, SYS (device
    driver), and other file types.
    https://en.wikipedia.org/wiki/Portable_Executable

    View Slide

  6. A Easy Beginning!
    #include
    int main()
    {
    MessageBoxA(
    0, "hi there.", "info", 0
    );
    return 0;
    }

    View Slide

  7. Compile Source to Exe File
    via Visual Studio C++ 2017 Community

    View Slide

  8. General Compiler
    Source.cpp Object Files Main.exe
    Compiler
    Assembly Codes
    Assembler
    Linker

    View Slide

  9. Source Code
    #include
    int main()
    {
    MessageBoxA(
    0, "hi there.", "info", 0
    );
    return 0;
    }

    View Slide

  10. Source Code → Assembly Codes
    #include
    int main() {
    MessageBoxA(
    0,
    "hi there.",
    "info", 0
    );
    return 0;
    }
    push 0
    push "info"
    push "hi there."
    push 0
    call MessageBoxA
    xor eax, eax
    ret

    View Slide

  11. based on
    x86 Calling Convention
    #include
    int main() {
    MessageBoxA(
    0,
    "hi there.",
    "info", 0
    );
    return 0;
    }
    push 0
    push "info"
    push "hi there."
    push 0
    call MessageBoxA
    xor eax, eax
    ret

    View Slide

  12. xor eax, eax
    ret
    push 0
    push "info"
    push "hi there."
    push 0
    call MessageBoxA
    0xdead: "info"
    0xbeef: "hi there."
    .rdata section
    0xcafe: 0x7630EA99
    .idata section

    (Import Address Table)
    How Compiler Works

    View Slide

  13. xor eax, eax
    ret
    push 0
    push offset "info"
    push offset "hi there."
    push 0
    call MessageBoxA
    0xdead: "info"
    0xbeef: "hi there."
    .rdata section
    0xcafe: 0x7630EA99
    How Linker Works
    .idata section

    (Import Address Table)

    View Slide

  14. xor eax, eax
    ret
    push 0
    push 0xdead
    push 0xbeef
    push 0
    call ds:0xcafe
    0xdead: "info"
    0xbeef: "hi there."
    .rdata section
    0xcafe: 0x7630EA99
    How Linker Works
    .idata section

    (Import Address Table)

    View Slide

  15. Assembler Works
    push 0 ; 6A 00
    push 0xdead ; 68 AD DE 00 00
    push 0xbeef ; 68 EF BE 00 00
    push 0 ; 6A 00
    call ds:0xcafe ; FF 15 FE CA 00 00
    xor eax, eax ; 33 C0
    ret ; C3

    View Slide

  16. Main.exe
    .text Section
    6A 00
    68 AD DE 00 00
    68 EF BE 00 00
    6A 00
    FF 15 FE CA 00 00
    33 C0
    C3
    0xdead: "info"
    0xbeef: "hi there."
    0xcafe: 0x7630EA99
    .rdata Section
    .idata Section

    View Slide

  17. There's 3 issues We need to Deal with:
    • Where The Exe File Data Should be Placed
    • How to Place Each Section in Memory
    • Fix The Import Address Table (IAT)
    So...

    Let's Take a Look at PE Structure :)

    View Slide

  18. PE File 101 DOS Program
    NT Header
    ⋯⋯
    File Header
    Optional Header
    DOS Header

    View Slide

  19. PE File 101 DOS Program
    NT Header
    ⋯⋯
    File Header
    Optional Header
    DOS Header
    1.DOSHeader starts with 'MZ'

    2.DOSHeader->e_lfanew point to NT
    Header

    View Slide

  20. BYTE* getNtHdrs(BYTE *pe_buffer) {
    if (pe_buffer == NULL) return NULL;
    IMAGE_DOS_HEADER *idh;= (IMAGE_DOS_HEADER *)pe_buffer;
    if (idh->e_magic != IMAGE_DOS_SIGNATURE)
    return NULL;
    IMAGE_NT_HEADERS32 *inh;
    inh = (IMAGE_NT_HEADERS32 *)(
    (BYTE*)pe_buffer + pe_offset
    );
    if (inh->Signature != IMAGE_NT_SIGNATURE)
    return NULL;
    return (BYTE*)inh;
    }

    View Slide

  21. Exe File (PE)
    DOS Program
    OptionalHeader
    NtHeader
    ...
    .ImageBase (0x400000)
    .SizeOfHeaders
    File Header
    .NumberOfSections
    .AddressOfEntryPoint
    SizeOfHeaders
    Section
    Header 1
    (.text)
    ...
    sizeof(Section Header) =
    IMAGE_SIZEOF_SECTION_HEADER = 40(fixed)
    .SizeOfImage
    Section
    Header 2
    Section
    Header 3
    Section
    Data 1
    (.text)
    .DataDirectory

    View Slide

  22. Exe File (PE)
    DOS Program NtHeader
    ...
    Section
    Header 1
    (.text)
    ...
    Section
    Header 2
    Section
    Header 3
    Section
    Data 1
    (.text)
    SectionHeader[i] = PIMAGE_SECTION_HEADER(
    NtHeader +
    sizeof(IMAGE_NT_HEADERS) +
    IMAGE_SIZEOF_SECTION_HEADER * index
    );
    IMAGE SECTION HEADER
    .VirtualAddress .SizeOfRawData
    .PointerToRawData

    View Slide

  23. IMAGE_NT_HEADERS32 *ntHeader;
    ntHeader = (IMAGE_NT_HEADERS32 *)getNtHdrs(data);
    memcpy(pImageBase, data, ntHeader->OptionalHeader.SizeOfHeaders);
    int maxNum = ntHeader->FileHeader.NumberOfSections;
    IMAGE_SECTION_HEADER * SectionHeader;
    for (DWORD count = 0; count < maxNum; count++) {
    SectionHeader = (IMAGE_SECTION_HEADER *)(
    DWORD(ntHeader) + sizeof(IMAGE_NT_HEADERS) +
    IMAGE_SIZEOF_SECTION_HEADER * count
    );
    memcpy(
    LPVOID(DWORD(pImageBase) + SectionHeader->VirtualAddress),
    LPVOID(DWORD(data) + SectionHeader->PointerToRawData),
    SectionHeader->SizeOfRawData
    );
    }

    View Slide

  24. Example:
    Process Hollowing (aka RunPE)

    View Slide

  25. ntdll!LdrVerifyImageMatchesChecksum

    View Slide

  26. ntdll!LdrVerifyImageMatchesChecksumEx

    View Slide

  27. View Slide

  28. View Slide

  29. Ntdll.dll
    ...
    Process
    Kernel32.dll
    User32.dll
    ...
    ...
    Process
    Chrome
    Memory
    Allocated
    VirtualAllocEx()
    (Length = SizeOfImage)
    0x400000
    Malware.exe
    Allocate memory at ImageBase(0x400000)

    View Slide

  30. Ntdll.dll
    ...
    Process
    Kernel32.dll
    User32.dll
    ...
    ...
    Process
    Chrome
    WriteProcessMemory() at
    ImageBase + 0x00
    Copy SizeOfHeaders bytes from
    (malware.exe + 0x00)
    Image Header
    Malware.exe

    View Slide

  31. DOS Program
    ...
    malware.exe
    ...
    Process
    Chrome
    NtHeader
    Section Header 1
    Section Data 1
    ...
    .VirtualAddress = 0xbeef
    [email protected]
    .PointerToRawData
    .SizeOfRawData
    copy SizeOfRaowData bytes from
    PointerToRawData
    via WriteProcessMemory()
    Image Header

    View Slide

  32. [email protected]
    Section Header 1
    ...
    malware.exe
    ...
    Process
    Chrome
    NtHeader
    Section Header2
    Section Data 2
    ...
    .VirtualAddress = 0xcafe
    [email protected]
    .PointerToRawData
    .SizeOfRawData
    copy SizeOfRaowData bytes from
    PointerToRawData
    via WriteProcessMemory()
    Image Header
    DOS Program

    View Slide

  33. .text
    ...
    Process
    Chrome
    Section 2
    Section 3
    ...
    Ntdll.dll
    Process
    User32.dll
    SetThreadContext() &
    ResumeThread()
    eax = AddressOfEntryPoint
    ...
    Malware.exe Image Header
    Kernel32.dll

    View Slide

  34. BYTE* MapFileToMemory(LPCSTR filename, LONGLONG &filelen){
    FILE *fileptr;
    BYTE *buffer;
    fileptr = fopen(filename, "rb");
    fseek(fileptr, 0, SEEK_END);
    filelen = ftell(fileptr);
    rewind(fileptr);
    buffer = (BYTE *)malloc((filelen + 1) * sizeof(char));
    fread(buffer, filelen, 1, fileptr);
    fclose(fileptr);
    return buffer;
    }

    View Slide

  35. DOSHeader = PIMAGE_DOS_HEADER(Image);
    NtHeader = PIMAGE_NT_HEADERS(
    DWORD(Image) + DOSHeader->e_lfanew
    );
    // process in suspended state, for the new image.
    if (CreateProcessA(
    path, 0, 0, 0, 0, CREATE_SUSPENDED, 0, 0, &SI, &PI)) {
    if (GetThreadContext(PI.hThread, LPCONTEXT(CTX))) {
    //if context is in thread
    ...

    View Slide

  36. pImageBase = VirtualAllocEx(
    PI.hProcess,
    LPVOID(NtHeader->OptionalHeader.ImageBase),
    NtHeader->OptionalHeader.SizeOfImage,
    MEM_COMMIT | MEM_RESERVE,
    PAGE_EXECUTE_READWRITE);
    WriteProcessMemory(
    PI.hProcess,
    pImageBase,
    Image,
    NtHeader->OptionalHeader.SizeOfHeaders, NULL);

    View Slide

  37. int maxNum = NtHeader->FileHeader.NumberOfSections;
    for (int count = 0; count < maxNum; count++) {
    SectionHeader = PIMAGE_SECTION_HEADER(
    DWORD(NtHeader) + sizeof(IMAGE_NT_HEADERS) +
    IMAGE_SIZEOF_SECTION_HEADER * count
    );
    WriteProcessMemory(
    PI.hProcess,
    LPVOID(DWORD(pImageBase) + SectionHeader->VirtualAddress),
    LPVOID(DWORD(Image) + SectionHeader->PointerToRawData),
    SectionHeader->SizeOfRawData, 0
    );
    }

    View Slide

  38. WriteProcessMemory(
    PI.hProcess,
    LPVOID(CTX->Ebx + 8),
    LPVOID(&pImageBase),
    sizeof(DWORD), 0
    );
    CTX->Eax = DWORD(pImageBase) +
    NtHeader->OptionalHeader.AddressOfEntryPoint;
    SetThreadContext(PI.hThread, LPCONTEXT(CTX));
    //Start the process/call main()
    ResumeThread(PI.hThread);

    View Slide

  39. Code & Demo
    github.com/aaaddress1/RunPE-In-Memory/blob/master/runPE/runPE/runPE.cpp

    View Slide

  40. But...
    We Want to Build a Software Packer.
    We need to fix IAT by myself :/

    View Slide

  41. Exe File (PE)
    DOS Program
    OptionalHeader
    NtHeader
    ...
    .ImageBase (0x400000)
    .SizeOfHeaders
    File Header
    .NumberOfSections
    .AddressOfEntryPoint
    SizeOfHeaders
    Section
    Header 1
    (.text)
    ...
    sizeof(Section Header) =
    IMAGE_SIZEOF_SECTION_HEADER = 40(fixed)
    .SizeOfImage
    Section
    Header 2
    Section
    Header 3
    Section
    Data 1
    (.text)
    .DataDirectory

    View Slide

  42. IMAGE_DATA_DIRECTORY* getPeDir(PVOID pe_buffer, DWORD dir_id)
    {
    if (dir_id >= IMAGE_NUMBEROF_DIRECTORY_ENTRIES) return NULL;
    IMAGE_NT_HEADERS32* nt_header;
    nt_header = (IMAGE_NT_HEADERS32*)getNtHdrs((BYTE *)pe_buffer);
    if (nt_header == NULL) return NULL;
    IMAGE_DATA_DIRECTORY* peDir = NULL;
    peDir = &(nt_header->OptionalHeader.DataDirectory[dir_id]);
    if (peDir->VirtualAddress == NULL)
    return NULL;
    return peDir;
    }

    View Slide

  43. Exe File (PE)
    DOS Program
    OptionalHeader
    NtHeader
    ...
    DataDirectory
    Export Directory
    index
    0
    Import Directory
    1
    Exception Directory
    2
    Security Directory
    Base Relocation Table
    Resource Directory
    Import Address Table
    3
    4
    5
    ...
    12
    ...
    IMAGE_DATA_DIRECTORY[16]
    typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD VirtualAddress;
    DWORD Size;
    } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

    View Slide

  44. Exe File (PE)
    DOS Program
    OptionalHeader
    NtHeader
    ...
    DataDirectory
    index
    Import Address Table
    ...
    12
    ...
    typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD VirtualAddress;
    DWORD Size;
    } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
    .VirtualAddress .Size

    View Slide

  45. Exe File (PE)
    DOS Program
    OptionalHeader
    NtHeader
    ...
    DataDirectory
    index
    Import Address Table
    ...
    12
    ...
    .VirtualAddress
    .Size
    IMAGE_IMPORT_DESCRIPTOR

    View Slide

  46. IMAGE_IMPORT_DESCRIPTOR Array
    IMAGE_IMPORT_DESCRIPTOR 1
    IMAGE_IMPORT_DESCRIPTOR 2
    IMAGE_IMPORT_DESCRIPTOR 3
    ...
    \x00\x00\x00\x00\x00\x00\x00
    Fixed Size
    Fixed Size
    Fixed Size
    Exe File (PE) OptionalHeader DataDirectory
    index
    Import Address Table
    12
    .VirtualAddress
    .Size
    sizeof(IMAGE_IMPORT_DESCRIPTOR)
    NT Header
    sizeof(Descriptor Array)

    View Slide

  47. bool fixIAT(PVOID modulePtr) {
    printf("[+] Fix Import Address Table\n");
    IMAGE_DATA_DIRECTORY *importsDir = getPeDir(
    modulePtr, IMAGE_DIRECTORY_ENTRY_IMPORT
    );
    if (importsDir == NULL) return false;
    DWORD maxSize = importsDir->Size;
    DWORD impAddr = importsDir->VirtualAddress;
    IMAGE_IMPORT_DESCRIPTOR* lib_desc = NULL;
    DWORD parsedSize = 0;
    while (parsedSize < maxSize) {
    lib_desc = (IMAGE_IMPORT_DESCRIPTOR*)(
    impAddr + parsedSize + (ULONG_PTR)modulePtr
    );
    ...
    parsedSize += sizeof(IMAGE_IMPORT_DESCRIPTOR);
    }

    View Slide

  48. IMAGE_IMPORT_DESCRIPTOR
    typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
    DWORD Characteristics;// 0 for terminating null import descriptor
    DWORD OriginalFirstThunk;// RVA to original unbound IAT (PIMAGE_THUNK_DATA)
    } DUMMYUNIONNAME;
    DWORD TimeDateStamp;// 0 if not bound,
    // -1 if bound, and real date\time stamp
    // in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
    // O.W. date/time stamp of DLL bound to (Old BIND)
    DWORD ForwarderChain; // -1 if no forwarders
    DWORD Name;
    DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses)
    } IMAGE_IMPORT_DESCRIPTOR;
    typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

    View Slide

  49. IMAGE_IMPORT_DESCRIPTOR 1
    Exe File (PE) OptionalHeader DataDirectory
    index
    Import Address Table
    12
    .VirtualAddress
    .Size
    NT Header
    .OriginalFirstThunk
    .FirstThunk
    .Name (User32.dll)
    IMAGE_IMPORT_BY_NAME: MessageBoxA
    typedef struct _IMAGE_IMPORT_BY_NAME {
    WORD Hint;
    CHAR Name[1];
    } IMAGE_IMPORT_BY_NAME;

    View Slide

  50. while (parsedSize < maxSize) {
    lib_desc = (IMAGE_IMPORT_DESCRIPTOR*)(
    impAddr + parsedSize + (ULONG_PTR)modulePtr
    );
    if (lib_desc->OriginalFirstThunk == NULL &&
    lib_desc->FirstThunk == NULL) break;
    LPSTR lib_name = (LPSTR)((DWORD)modulePtr + lib_desc->Name);
    printf(" [+] Import DLL: %s\n", lib_name);
    DWORD call_via = lib_desc->FirstThunk;
    DWORD thunk_addr = lib_desc->OriginalFirstThunk;
    if (thunk_addr == NULL) thunk_addr = lib_desc->FirstThunk;
    ...
    parsedSize += sizeof(IMAGE_IMPORT_DESCRIPTOR);
    }

    View Slide

  51. xor eax, eax
    ret
    push 0
    push 0xdead
    push 0xbeef
    push 0
    call ds:0xcafe
    IMAGE_IMPORT_DESCRIPTOR 1
    Exe File (PE) OptionalHeader DataDirectory
    index
    Import Address Table
    12
    .VirtualAddress
    .Size
    NT Header
    .OriginalFirstThunk
    .FirstThunk = 0xcafe
    .Name (User32.dll)
    IMAGE_IMPORT_BY_NAME: MessageBoxA

    View Slide

  52. IMAGE_IMPORT_DESCRIPTOR 1
    Exe File (PE) OptionalHeader DataDirectory
    index
    Import Address Table
    12
    .VirtualAddress
    .Size
    NT Header
    .FirstThunk = 0xcafe
    .FirstThunk
    .Name (User32.dll)
    IMAGE_IMPORT_BY_NAME: MessageBoxA
    HANDLE mod = LoadLibrary("User32.dll");
    GetProcAddress(mod, "MessageBoxA") = 0x7547EA99
    0x7547EA99

    View Slide

  53. Thunk Array
    IMAGE_IMPORT_DESCRIPTOR 1
    Exe File (PE) OptionalHeader DataDirectory
    index
    Import Address Table
    12
    .VirtualAddress
    .Size
    NT Header
    MessageBoxA: *(uint32_t *)0xcafe = 0x7547EA99
    .FirstThunk = 0xcafe
    .Thunk = 0xcaff
    .Thunk = 0xcb00
    .Thunk = 0xcb02
    .Name (User32.dll)
    LoadStringA: *(uint32_t *)0xcaff = 0x75130D4D
    KillTimer: *(uint32_t *)0xcb01 = 0x754364C7
    Lastest Thunk: *(uint32_t *)0xcb02 = NULL

    View Slide

  54. DWORD offsetField = 0;
    DWORD offsetThunk = 0;
    while (true) {
    IMAGE_THUNK_DATA32* fieldThunk = (IMAGE_THUNK_DATA32*)(
    DWORD(modulePtr) + offsetField + call_via
    );
    IMAGE_THUNK_DATA32* orginThunk = (IMAGE_THUNK_DATA32*)(
    DWORD(modulePtr) + offsetThunk + thunk_addr
    );
    if (fieldThunk->u1.Function == NULL) break;
    ...
    offsetField += sizeof(IMAGE_THUNK_DATA32);
    offsetThunk += sizeof(IMAGE_THUNK_DATA32);
    }

    View Slide

  55. if (fieldThunk->u1.Function == orginThunk->u1.Function) {
    PIMAGE_IMPORT_BY_NAME by_name = (PIMAGE_IMPORT_BY_NAME)(
    DWORD(modulePtr) + orginThunk->u1.AddressOfData
    );
    LPSTR func_name = (LPSTR)by_name->Name;
    DWORD addr = (DWORD)GetProcAddress(
    LoadLibraryA(lib_name), func_name
    );
    printf(" [V] API %s at %x\n", func_name, addr);
    fieldThunk->u1.Function = addr;
    }

    View Slide

  56. And What's Relocation?

    View Slide

  57. Process
    Main.exe
    .text
    Address
    0x400000
    0x401000
    ...
    .idata
    0x40D000
    .rdata
    0x40D110

    View Slide

  58. Process
    Main.exe
    .text
    Address
    0x400000
    0x401000
    ...
    .idata
    0x40D000
    .rdata
    0x40D110

    View Slide

  59. Address space layout randomization
    (ASLR)
    Address space layout randomization (ASLR) is a computer security
    technique involved in preventing exploitation of memory corruption
    vulnerabilities.

    In order to prevent an attacker from reliably jumping to, for example,
    a particular exploited function in memory, ASLR randomly arranges
    the address space positions of key data areas of a process,
    including the base of the executable and the positions of the stack,
    heap and libraries.
    https://en.wikipedia.org/wiki/Address_space_layout_randomization

    View Slide

  60. Process
    Main.exe
    .text
    Address
    0x400000
    0x401000
    ...
    .idata
    0x40D000
    .rdata
    0x40D110

    View Slide

  61. Process
    Main.exe
    .text
    Address
    0x500000
    0x501000
    ...
    .idata
    0x50D000
    .rdata
    0x50D110

    View Slide

  62. Process
    Main.exe
    .text
    Address
    0x600000
    0x601000
    ...
    .idata
    0x60D000
    .rdata
    0x60D110

    View Slide

  63. Process
    Main.exe
    .text
    Address
    0x600000
    0x601000
    ...
    .idata
    0x60D000
    .rdata
    0x60D110
    Access violation

    View Slide

  64. Exe File (PE)
    DOS Program
    OptionalHeader
    NtHeader
    ...
    Export Directory
    index
    0
    Import Directory
    1
    Exception Directory
    2
    Security Directory
    Base Relocation Table
    Resource Directory
    Import Address Table
    3
    4
    5
    ...
    12
    ...
    IMAGE_DATA_DIRECTORY[16]
    typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD VirtualAddress;
    DWORD Size;
    } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
    DataDirectory

    View Slide

  65. Exe File (PE)
    DOS Program
    OptionalHeader
    NtHeader
    ...
    Export Directory
    index
    0
    Import Directory
    1
    Exception Directory
    2
    Security Directory
    Base Relocation Table
    Resource Directory
    Import Address Table
    3
    4
    5
    ...
    12
    ...
    IMAGE_DATA_DIRECTORY[16]
    typedef struct _IMAGE_DATA_DIRECTORY {
    DWORD VirtualAddress;
    DWORD Size;
    } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
    DataDirectory

    View Slide

  66. Exe File (PE)
    DOS Program
    OptionalHeader
    NtHeader
    ...
    index ...
    ...
    .VirtualAddress
    .Size
    IMAGE_BASE_RELOCATION
    typedef struct _IMAGE_BASE_RELOCATION {
    DWORD VirtualAddress;
    DWORD SizeOfBlock;
    } IMAGE_BASE_RELOCATION;
    Base Relocation Table
    5
    DataDirectory

    View Slide

  67. IMAGE_DATA_DIRECTORY* relocDir = getPeDir(
    modulePtr, IMAGE_DIRECTORY_ENTRY_BASERELOC
    );
    /* Cannot relocate - application have no relocation table */
    if (relocDir == NULL) return false;
    DWORD maxSize = relocDir->Size;
    DWORD relocAddr = relocDir->VirtualAddress;
    IMAGE_BASE_RELOCATION* reloc = NULL;
    DWORD parsedSize = 0;
    while (parsedSize < maxSize) {
    reloc = (IMAGE_BASE_RELOCATION*)(
    relocAddr + parsedSize + DWORD(modulePtr));
    if (reloc->VirtualAddress == NULL || reloc->SizeOfBlock == 0)
    break;
    ...
    parsedSize += reloc->SizeOfBlock;
    }

    View Slide

  68. OptionalHeader index ...
    ...
    .VirtualAddress .Size
    Base Relocation Table
    5
    IMAGE_BASE_RELOCATION 1
    VirtualAddress
    Exe File (PE)
    NT Header
    SizeOfBlock
    IMAGE_BASE_RELOCATION 2
    VirtualAddress
    SizeOfBlock
    IMAGE_BASE_RELOCATION 3
    ...
    Relocation
    Table
    DataDirectory

    View Slide

  69. typedef struct _BASE_RELOCATION_ENTRY {
    WORD Offset : 12;
    WORD Type : 4;
    } BASE_RELOCATION_ENTRY;
    OptionalHeader index ...
    ...
    .VirtualAddress
    .Size
    Base Relocation Table
    5
    IMAGE_BASE_RELOCATION 1
    VirtualAddress
    Exe File (PE)
    NT Header
    _BASE_RELOCATION_ENTRY 1
    _BASE_RELOCATION_ENTRY 2
    _BASE_RELOCATION_ENTRY 3
    ...
    SizeOfBlock
    We can get the count of RELOCATION_ENTRY by:
    (SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION))
    / sizeof(RELOCATION_ENTRY) =
    (SizeOfBlock - 2 * sizeof(DWORD)) / 2;
    DataDirectory

    View Slide

  70. OptionalHeader index ...
    ...
    .VirtualAddress
    .Size
    Base Relocation Table
    5
    IMAGE_BASE_RELOCATION 1
    VirtualAddress
    Exe File (PE)
    NT Header
    _BASE_RELOCATION_ENTRY 1
    SizeOfBlock
    0x1000
    Offset: 0x003
    Type: 03
    ...
    00601000 - 6A 00 - push 00

    00601002 - 68 E8194100 - push 004119E8

    00601007 - 68 F0194100 - push 004119F0

    0060100C - 6A 00 - push 00

    0060100E - FF 15 08D14000 - call dword ptr [0040D108]

    ...
    typedef struct _BASE_RELOCATION_ENTRY {
    WORD Offset : 12;
    WORD Type : 4;
    } BASE_RELOCATION_ENTRY;
    #define RELOC_32BIT_FIELD 3
    #define RELOC_64BIT_FIELD 0xA
    DataDirectory

    View Slide

  71. OptionalHeader index ...
    ...
    .VirtualAddress
    .Size
    Base Relocation Table
    5
    IMAGE_BASE_RELOCATION 1
    VirtualAddress
    Exe File (PE)
    NT Header
    _BASE_RELOCATION_ENTRY 1
    SizeOfBlock
    0x1000
    Offset: 0x003
    Type: 03
    ...
    00601000 - 6A 00 - push 00

    00601002 - 68 E8196100 - push 006119E8

    00601007 - 68 F0194100 - push 004119F0

    0060100C - 6A 00 - push 00

    0060100E - FF 15 08D14000 - call dword ptr [0040D108]

    ...
    typedef struct _BASE_RELOCATION_ENTRY {
    WORD Offset : 12;
    WORD Type : 4;
    } BASE_RELOCATION_ENTRY;
    #define RELOC_32BIT_FIELD 3
    #define RELOC_64BIT_FIELD 0xA
    DataDirectory

    View Slide

  72. OptionalHeader index ...
    ...
    .VirtualAddress
    .Size
    Base Relocation Table
    5
    IMAGE_BASE_RELOCATION 1
    VirtualAddress
    Exe File (PE)
    NT Header
    _BASE_RELOCATION_ENTRY 2
    SizeOfBlock
    0x1000
    Offset: 0x008
    Type: 03
    ...
    00601000 - 6A 00 - push 00

    00601002 - 68 E8196100 - push 006119E8

    00601007 - 68 F0194100 - push 004119F0

    0060100C - 6A 00 - push 00

    0060100E - FF 15 08D14000 - call dword ptr [0040D108]

    ...
    typedef struct _BASE_RELOCATION_ENTRY {
    WORD Offset : 12;
    WORD Type : 4;
    } BASE_RELOCATION_ENTRY;
    #define RELOC_32BIT_FIELD 3
    #define RELOC_64BIT_FIELD 0xA
    DataDirectory

    View Slide

  73. OptionalHeader index ...
    ...
    .VirtualAddress
    .Size
    Base Relocation Table
    5
    IMAGE_BASE_RELOCATION 1
    VirtualAddress
    Exe File (PE)
    NT Header
    _BASE_RELOCATION_ENTRY 2
    SizeOfBlock
    0x1000
    Offset: 0x008
    Type: 03
    ...
    00601000 - 6A 00 - push 00

    00601002 - 68 E8196100 - push 006119E8

    00601007 - 68 F0196100 - push 006119F0

    0060100C - 6A 00 - push 00

    0060100E - FF 15 08D14000 - call dword ptr [0040D108]

    ...
    typedef struct _BASE_RELOCATION_ENTRY {
    WORD Offset : 12;
    WORD Type : 4;
    } BASE_RELOCATION_ENTRY;
    #define RELOC_32BIT_FIELD 3
    #define RELOC_64BIT_FIELD 0xA
    DataDirectory

    View Slide

  74. OptionalHeader index ...
    ...
    .VirtualAddress
    .Size
    Base Relocation Table
    5
    IMAGE_BASE_RELOCATION 1
    VirtualAddress
    Exe File (PE)
    NT Header
    _BASE_RELOCATION_ENTRY 3
    SizeOfBlock
    0x1000
    Offset: 0x010
    Type: 03
    ...
    00601000 - 6A 00 - push 00

    00601002 - 68 E8196100 - push 006119E8

    00601007 - 68 F0196100 - push 006119F0

    0060100C - 6A 00 - push 00

    0060100E - FF 15 08D14000 - call dword ptr [0040D108]

    ...
    typedef struct _BASE_RELOCATION_ENTRY {
    WORD Offset : 12;
    WORD Type : 4;
    } BASE_RELOCATION_ENTRY;
    #define RELOC_32BIT_FIELD 3
    #define RELOC_64BIT_FIELD 0xA
    DataDirectory

    View Slide

  75. OptionalHeader index ...
    ...
    .VirtualAddress
    .Size
    Base Relocation Table
    5
    IMAGE_BASE_RELOCATION 1
    VirtualAddress
    Exe File (PE)
    NT Header
    _BASE_RELOCATION_ENTRY 3
    SizeOfBlock
    0x1000
    Offset: 0x010
    Type: 03
    ...
    00601000 - 6A 00 - push 00

    00601002 - 68 E8196100 - push 006119E8

    00601007 - 68 F0196100 - push 006119F0

    0060100C - 6A 00 - push 00

    0060100E - FF 15 08D16000 - call dword ptr [0060D108]

    ...
    typedef struct _BASE_RELOCATION_ENTRY {
    WORD Offset : 12;
    WORD Type : 4;
    } BASE_RELOCATION_ENTRY;
    #define RELOC_32BIT_FIELD 3
    #define RELOC_64BIT_FIELD 0xA
    DataDirectory

    View Slide

  76. DWORD entriesNum = (
    reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)
    ) / sizeof(BASE_RELOCATION_ENTRY);
    DWORD page = reloc->VirtualAddress;
    BASE_RELOCATION_ENTRY* entry = (BASE_RELOCATION_ENTRY*)(
    DWORD(reloc) + sizeof(IMAGE_BASE_RELOCATION)
    );

    View Slide

  77. for (DWORD i = 0; i < entriesNum; i++) {
    DWORD offset = entry->Offset;
    DWORD type = entry->Type;
    DWORD reloc_field = page + offset;
    if (entry == NULL || type == 0)
    break;
    DWORD* relocateAddr = (DWORD*)(DWORD(modulePtr) + reloc_field);
    (*relocateAddr) = ((*relocateAddr) - oldBase + newBase);
    printf(" [V] Apply Reloc Field at %x\n", relocateAddr);
    entry = (BASE_RELOCATION_ENTRY*)(
    DWORD(entry) + sizeof(BASE_RELOCATION_ENTRY)
    );
    }

    View Slide

  78. Code & Demo
    github.com/aaaddress1/RunPE-In-Memory

    View Slide

  79. Thanks for listening :)
    [email protected]
    Slides
    Github Project

    View Slide