hasherezade / libpeconv

A library to load, manipulate, dump PE files. See also: https://github.com/hasherezade/libpeconv_tpl
https://hasherezade.github.io/libpeconv
BSD 2-Clause "Simplified" License
1.07k stars 176 forks source link

issue with load_pe_module #23

Closed 11philip22 closed 3 years ago

11philip22 commented 3 years ago

Hi there, I am trying to load a DLL from a byte array with the load_pe_module function. I tried replicating the methods in https://github.com/hasherezade/libpeconv/blob/master/tests/test_load_ntdll.cpp. But when i call a function from the loaded DLL i am getting an access violation exception :(

#include <Windows.h>

#include <peconv.h>
#include "Payload.h"

//typedef int (*moduleMainProc)();
int(_cdecl* moduleMain) () = NULL;

int main()
{
    size_t v_size = 0;
    BYTE* payloadModule = peconv::load_pe_module(
        payload, 
        sizeof(payload), 
        v_size, 
        true, 
        true
    );
    if (!payloadModule) {
        return -1;
    }

    bool is64 = peconv::is64bit(payloadModule);
    FARPROC nOffset = peconv::get_exported_func(payloadModule, (LPSTR)"main");
    if (nOffset == nullptr) {
        return -1;
    }

    int (*loaded_pe_entry)(void) = (int (*)(void)) nOffset;
    return loaded_pe_entry();

    //moduleMain = (int(_cdecl*) ()) nOffset;
    //return moduleMain();
}

exception Am i doing something wrong or is there an issue with the lib or my dll? This is the dll that im using: https://github.com/11philip22/DllShellSimple

hasherezade commented 3 years ago

hi! can you share the exact builds of the library that you are using? I will check. you can drop them here as an attachment.

hasherezade commented 3 years ago

Check if those builds work for you: DllShellSample_builds.zip

11philip22 commented 3 years ago

hi! can you share the exact builds of the library that you are using? I will check. you can drop them here as an attachment.

These are the files that i'm trying to load. dll.zip

Check if those builds work for you: DllShellSample_builds.zip

Sadly i'm getting the same results exception2

I also forgot to mention that i had to put some casts at line 11, 22 and 23 in hooks.cpp to make it compile in visual studio. cast I ran this snippet from your ntdll test to make sure libpeconv still works after applying those casts.

#include <Windows.h>

#include <peconv.h>

int(_cdecl* ntdll_tolower) (int) = NULL;

int main()
{
    size_t v_size = 0;
    BYTE* ntdll_module = peconv::load_pe_module("C:\\Windows\\system32\\ntdll.dll", v_size, true, true);
    if (!ntdll_module) {
        return -1;
    }

    bool is64 = peconv::is64bit(ntdll_module);
    std::cout << "NTDLL loaded" << is64 << std::endl;
    FARPROC n_offset = peconv::get_exported_func(ntdll_module, (LPSTR)"tolower");
    if (n_offset == NULL) {
        return -1;
    }

    std::cout << "Got tolower: " << n_offset << std::endl;
    ntdll_tolower = (int(_cdecl*) (int)) n_offset;
    int out = ntdll_tolower('C');
    std::cout << "To lower char: " << (char)out << std::endl;
}

result And it apears to work correctly

hasherezade commented 3 years ago

Something is odd. I tested the builds that I shared with you ( dll.zip ) and they both loaded fine with libpeconv. Maybe something is wrong with your loader? Try to use this ready made template: https://github.com/hasherezade/libpeconv_tpl and just change the loaded function prototype... I will check your builds in the meanwhile.

hasherezade commented 3 years ago

ah, I just noticed right now: you used load_pe_module - this function doesn't load imports:

    /**
    Reads PE from the given buffer into memory and maps it into vitual format.
    (Automatic raw to virtual conversion).
    If the executable flag is true, the PE file is loaded into executable memory.
    If the relocate flag is true, applies relocations. Does not load imports.
    Automatically allocates buffer of the needed size (the size is returned in outputSize). The buffer can be freed by the function free_pe_buffer.
    */
    BYTE* load_pe_module(BYTE* dllRawData, size_t r_size, OUT size_t &v_size, bool executable, bool relocate);

you should use another variant from the API: load_pe_executable:

    /**
    Loads full PE from the raw buffer in a way in which it can be directly executed: remaps to virual format, applies relocations, loads imports.
    Allows for supplying custom function resolver.
    */
    BYTE* load_pe_executable(BYTE* dllRawData, size_t r_size, OUT size_t &v_size, t_function_resolver* import_resolver=NULL);

...and then everything should be fine. NTDLL is a special case that doesn't have any imports, that's why both ways it will work. Either way, I strongly recommend you using the template, rather than modifying test cases.

11philip22 commented 3 years ago

Yay! I got it to work! Thanks for your help. Everything works as expected with load_pe_executable. Here is the src if you are interested:

#include <Windows.h>

#include <peconv.h>
#include "Payload.h"

//typedef int (*moduleMainProc)();
int(_cdecl* moduleMain) () = NULL;

int main()
{
    size_t v_size = 0;
    BYTE* payloadModule = peconv::load_pe_executable(
        payload,
        sizeof(payload),
        v_size
    );
    if (!payloadModule) {
        return -1;
    }

    bool is64 = peconv::is64bit(payloadModule);
    FARPROC nOffset = peconv::get_exported_func(payloadModule, (LPSTR)"main");
    if (nOffset == nullptr) {
        return -1;
    }

    //int (*loaded_pe_entry)(void) = (int (*)(void)) nOffset;
    //int ret = loaded_pe_entry();
    //return ret;

    moduleMain = (int(_cdecl*) ()) nOffset;
    return moduleMain();
}

Too bad bad windows defender flags it as wacatac but i think that's the case with everything that loads a PE. i think turning it into another DLL will fix that haha. Anyways thanks a lot for the help :)