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.09k stars 177 forks source link

How to pass arguments to main payload in run-PE (or any program written by libPeConv)? #56

Open mhscXXl opened 8 months ago

mhscXXl commented 8 months ago

Issue: I have tried to pass arguments to a payload loaded by libpeconv but it wasn't possible directly. So i decided to go a little bit deeper and modified some stack related parts of the code, it was successful but this method is heavily relied on Non-standard methods and requires more or less complicated modifications on the main code.

Is there any possible ongoing features that are not yet published or any other methods that i could use for this matter?

kem0x commented 1 month ago

I've looked into this, you can't simply do EntryPoint(argc, argv), This happens due to the fact that normal PEs entry point is not actually int main(...) but rather int64_t crt_main() which is responsible of calling and _p___argc and __p___argv or __p___wargv and passing them to the real main function after setting up the CRT. seems like there is no straightforward way to approach this but the solution i went with was hooking the real main

here's a quick demo:

constexpr inline ptrdiff_t RealMainOffset = ...;

inline peconv::PatchBackup PayloadMainBackup;

const wchar_t* NewArgv[] = { L"....exe", L"--version" };
constexpr auto NewArgc = sizeof(NewArgv) / sizeof(NewArgv[0]);

inline auto RealEntryPoint = (int(*)(int , const wchar_t** , const char**))nullptr;

static int PayloadMainHook(int argc, const wchar_t** argv, const char** envp)
{
    PayloadMainBackup.applyBackup();

    argc = NewArgc;
    argv = NewArgv;

    return RealEntryPoint(argc, argv, envp);
}

int main()
{
    // Load the PE normally

    // ...

    auto EntryPointOffset = peconv::get_entry_point_rva((BYTE*)PayloadModuleBase);

    RealEntryPoint = (decltype(RealEntryPoint))(PayloadModuleBase + RealMainOffset);

    peconv::redirect_to_local(RealEntryPoint, &PayloadMainHook, &PayloadMainBackup);

    auto EntryPoint = (int(*)())(PayloadModuleBase + EntryPointOffset);

    return EntryPoint();
}

This for sure isn't convenient for a library, The way the library can approach this imo is doing some simple dynamic analysis on the crt main to find the real main and hook it, i think this is the best way instead of editing the args pushed to the host process.