237 lines
6.3 KiB
C++
237 lines
6.3 KiB
C++
#include "stdafx.h"
|
|
#ifdef _WIN64
|
|
// Source code: https://github.com/hasherezade/pe_to_shellcode
|
|
#include <windows.h>
|
|
#include <iostream>
|
|
|
|
#include "peconv.h"
|
|
#include "resource.h"
|
|
|
|
#define VERSION "1.2"
|
|
#include "peloader.h"
|
|
|
|
#ifdef _DEBUG
|
|
#pragma comment(lib, "libpeconv/libpeconv_x64d.lib")
|
|
#else
|
|
#pragma comment(lib, "libpeconv/libpeconv_x64.lib")
|
|
#endif
|
|
|
|
bool overwrite_hdr(BYTE *my_exe, size_t exe_size, DWORD raw, bool is64b)
|
|
{
|
|
const size_t value_pos = 8;
|
|
size_t redir_size = 0;
|
|
BYTE* redir_code = nullptr;
|
|
|
|
BYTE redir_code32_64[] = "\x4D" //dec ebp
|
|
"\x5A" //pop edx
|
|
"\x45" //inc ebp
|
|
"\x52" //push edx
|
|
"\xE8\x00\x00\x00\x00" //call <next_line>
|
|
"\x5B" // pop ebx
|
|
"\x48\x83\xEB\x09" // sub ebx,9
|
|
"\x53" // push ebx (Image Base)
|
|
"\x48\x81\xC3" // add ebx,
|
|
"\x59\x04\x00\x00" // value
|
|
"\xFF\xD3" // call ebx
|
|
"\xc3"; // ret
|
|
|
|
BYTE redir_code32[] = "\x4D" //dec ebp
|
|
"\x5A" //pop edx
|
|
"\x45" //inc ebp
|
|
"\x52" //push edx
|
|
"\xE8\x00\x00\x00\x00" //call <next_line>
|
|
"\x58" // pop eax
|
|
"\x83\xE8\x09" // sub eax,9
|
|
"\x50" // push eax (Image Base)
|
|
"\x05" // add eax,
|
|
"\x59\x04\x00\x00" // value
|
|
"\xFF\xD0" // call eax
|
|
"\xc3"; // ret
|
|
|
|
BYTE redir_code64[] = "\x4D\x5A" //pop r10
|
|
"\x45\x52" //push r10
|
|
"\xE8\x00\x00\x00\x00" //call <next_line>
|
|
"\x59" // pop rcx
|
|
"\x48\x83\xE9\x09" // sub rcx,9 (rcx -> Image Base)
|
|
"\x48\x8B\xC1" // mov rax,rcx
|
|
"\x48\x05" // add eax,
|
|
"\x59\x04\x00\x00" // value
|
|
"\xFF\xD0" // call eax
|
|
"\xc3"; // ret
|
|
|
|
#ifdef OLD_LOADER
|
|
redir_code = redir_code32_64;
|
|
redir_size = sizeof(redir_code32_64);
|
|
#else
|
|
redir_code = redir_code32;
|
|
redir_size = sizeof(redir_code32);
|
|
|
|
if (is64b) {
|
|
redir_code = redir_code64;
|
|
redir_size = sizeof(redir_code64);
|
|
}
|
|
#endif
|
|
if (!redir_code) return false;
|
|
if (redir_size > MAX_REDIR_SIZE) {
|
|
std::cerr << "The selected redir stub exceed the maximal size: " << std::dec << MAX_REDIR_SIZE << "\n";
|
|
return false;
|
|
}
|
|
size_t offset = redir_size - value_pos;
|
|
memcpy(redir_code + offset, &raw, sizeof(DWORD));
|
|
|
|
min_hdr_t* my_hdr = (min_hdr_t*)my_exe;
|
|
memcpy(my_hdr->redir, redir_code, redir_size);
|
|
my_hdr->load_status = LDS_CLEAN;
|
|
return true;
|
|
}
|
|
|
|
BYTE* shellcodify(BYTE *my_exe, size_t exe_size, size_t &out_size, bool is64b)
|
|
{
|
|
out_size = 0;
|
|
size_t stub_size = 0;
|
|
int res_id = is64b ? STUB64 : STUB32;
|
|
BYTE *stub = peconv::load_resource_data(stub_size, res_id);
|
|
if (!stub) {
|
|
std::cerr << "[ERROR] Stub not loaded" << std::endl;
|
|
return nullptr;
|
|
}
|
|
size_t ext_size = exe_size + stub_size;
|
|
BYTE *ext_buf = peconv::alloc_aligned(ext_size, PAGE_READWRITE);
|
|
if (!ext_buf) {
|
|
return nullptr;
|
|
}
|
|
memcpy(ext_buf, my_exe, exe_size);
|
|
memcpy(ext_buf + exe_size, stub, stub_size);
|
|
|
|
DWORD raw_addr = exe_size;
|
|
overwrite_hdr(ext_buf, ext_size, raw_addr, is64b);
|
|
|
|
out_size = ext_size;
|
|
return ext_buf;
|
|
}
|
|
|
|
template <typename IMAGE_TLS_DIRECTORY>
|
|
bool has_tls_callbacks(BYTE *my_exe, size_t exe_size)
|
|
{
|
|
IMAGE_DATA_DIRECTORY* tls_dir = peconv::get_directory_entry(my_exe, IMAGE_DIRECTORY_ENTRY_TLS);
|
|
if (!tls_dir) return false;
|
|
|
|
IMAGE_TLS_DIRECTORY* tls = peconv::get_type_directory<IMAGE_TLS_DIRECTORY>((HMODULE)my_exe, IMAGE_DIRECTORY_ENTRY_TLS);
|
|
if (!tls) return false;
|
|
|
|
ULONGLONG base = peconv::get_image_base(my_exe);
|
|
ULONGLONG callback_rva = tls->AddressOfCallBacks;
|
|
if (callback_rva > base) {
|
|
callback_rva -= base;
|
|
}
|
|
if (!peconv::validate_ptr(my_exe, exe_size, my_exe + callback_rva, sizeof(ULONGLONG))) {
|
|
return false;
|
|
}
|
|
ULONGLONG *callback_addr = (ULONGLONG *)(my_exe + callback_rva);
|
|
if (callback_addr == 0) {
|
|
return false;
|
|
}
|
|
if (*callback_addr == 0) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool is_supported_pe(BYTE *my_exe, size_t exe_size)
|
|
{
|
|
if (!my_exe) return false;
|
|
if (!peconv::has_relocations(my_exe)) {
|
|
std::cerr << "[ERROR] The PE must have relocations!" << std::endl;
|
|
return false;
|
|
}
|
|
if (peconv::get_subsystem(my_exe) != IMAGE_SUBSYSTEM_WINDOWS_GUI) {
|
|
std::cout << "[INFO] This is a console application." << std::endl;
|
|
}
|
|
IMAGE_DATA_DIRECTORY* dotnet_dir = peconv::get_directory_entry(my_exe, IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR);
|
|
if (dotnet_dir) {
|
|
std::cerr << "[ERROR] .NET applications are not supported!" << std::endl;
|
|
return false;
|
|
}
|
|
IMAGE_DATA_DIRECTORY* tls_dir = peconv::get_directory_entry(my_exe, IMAGE_DIRECTORY_ENTRY_TLS);
|
|
if (tls_dir) {
|
|
bool has_callback = false;
|
|
if (!peconv::is64bit(my_exe)) {
|
|
if (has_tls_callbacks<IMAGE_TLS_DIRECTORY32>(my_exe, exe_size)) {
|
|
has_callback = true;
|
|
}
|
|
}
|
|
else {
|
|
if (has_tls_callbacks<IMAGE_TLS_DIRECTORY64>(my_exe, exe_size)) {
|
|
has_callback = true;
|
|
}
|
|
}
|
|
if (has_callback) {
|
|
std::cout << "[INFO] This application has TLS callbacks." << std::endl;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool is_supported_pe(const std::string &in_path)
|
|
{
|
|
std::cout << "Reading module from: " << in_path << std::endl;
|
|
size_t exe_size = 0;
|
|
BYTE *my_exe = peconv::load_pe_module(in_path.c_str(), exe_size, false, false);
|
|
if (!my_exe) {
|
|
std::cerr << "[ERROR] Could not read the input file!" << std::endl;
|
|
return false;
|
|
}
|
|
|
|
bool is_ok = is_supported_pe(my_exe, exe_size);
|
|
peconv::free_pe_buffer(my_exe);
|
|
|
|
if (!is_ok) {
|
|
std::cerr << "[ERROR] Not supported input file!" << std::endl;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int pe_2_shellcode(const std::string &in_path, const std::string &out_str)
|
|
{
|
|
if (!is_supported_pe(in_path)) {
|
|
return -2;
|
|
}
|
|
|
|
size_t exe_size = 0;
|
|
BYTE *my_exe = peconv::load_pe_module(in_path.c_str(), exe_size, false, false);
|
|
if (!my_exe) {
|
|
std::cout << "[-] Could not read the input file!" << std::endl;
|
|
return -1;
|
|
}
|
|
|
|
bool is64b = peconv::is64bit(my_exe);
|
|
size_t ext_size = 0;
|
|
BYTE *ext_buf = shellcodify(my_exe, exe_size, ext_size, is64b);
|
|
if (!ext_buf) {
|
|
std::cerr << "[ERROR] Adding the stub failed!" << std::endl;
|
|
peconv::free_pe_buffer(my_exe);
|
|
return -3;
|
|
}
|
|
// remap pe to raw == virtual, so that remapping on load will not be required
|
|
peconv::t_pe_dump_mode dump_mode = peconv::PE_DUMP_REALIGN;
|
|
ULONGLONG current_base = peconv::get_image_base(ext_buf);
|
|
if (peconv::dump_pe(out_str.c_str(), ext_buf, ext_size, current_base, dump_mode)) {
|
|
std::cout << "[INFO] Saved as: " << out_str << std::endl;
|
|
}
|
|
else {
|
|
std::cerr << "[ERROR] Failed to save the output!" << std::endl;
|
|
}
|
|
peconv::free_pe_buffer(my_exe);
|
|
peconv::free_aligned(ext_buf);
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
|
|
int pe_2_shellcode(const std::string& in_path, const std::string& out_str) {
|
|
return -1; // Don't support x86 master program
|
|
}
|
|
|
|
#endif
|