hasherezade / pe_to_shellcode

Converts PE into a shellcode
https://www.youtube.com/watch?v=WQCiM0X11TA
BSD 2-Clause "Simplified" License
2.27k stars 423 forks source link

Not work with x64 independent code #9

Closed pokevas closed 4 years ago

pokevas commented 4 years ago

Hello I tried to convert the PE file without imports (base-independent file) If the file is built in x86 mode, then no problem, it is launched through runshc32.exe But if x64, then it fails and crashes. I don’t understand what’s the matter. This seems to be a bug. Source code of .exe file (entrypoint - CodeRun function):


#include <Windows.h>
#include <winternl.h>

#define ROTR32(value, shift) (((DWORD) value >> (BYTE) shift) | ((DWORD) value << (32 - (BYTE) shift)))

typedef struct _MY_PEB_LDR_DATA {
    ULONG Length;
    BOOL Initialized;
    PVOID SsHandle;
    LIST_ENTRY InLoadOrderModuleList;
    LIST_ENTRY InMemoryOrderModuleList;
    LIST_ENTRY InInitializationOrderModuleList;
} MY_PEB_LDR_DATA, * PMY_PEB_LDR_DATA;

typedef struct _MY_LDR_DATA_TABLE_ENTRY
{
    LIST_ENTRY InLoadOrderLinks;
    LIST_ENTRY InMemoryOrderLinks;
    LIST_ENTRY InInitializationOrderLinks;
    PVOID DllBase;
    PVOID EntryPoint;
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;
} MY_LDR_DATA_TABLE_ENTRY, * PMY_LDR_DATA_TABLE_ENTRY;

typedef HMODULE(WINAPI* FuncLoadLibraryA) (LPCSTR lpFileName);
typedef int(WINAPI* FuncMessageBoxA)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);

HMODULE GetProcAddressByHash(_In_ DWORD dwModuleFunctionHash)
{
    PPEB PebAddress;
    PMY_PEB_LDR_DATA pLdr;

```PMY_LDR_DATA_TABLE_ENTRY pDataTableEntry;
    PVOID pModuleBase;
    PIMAGE_NT_HEADERS pNTHeader;
    DWORD dwExportDirRVA;
    PIMAGE_EXPORT_DIRECTORY pExportDir;
    PLIST_ENTRY pNextModule;
    DWORD dwNumFunctions;
    USHORT usOrdinalTableIndex;
    PDWORD pdwFunctionNameBase;
    PCSTR pFunctionName;
    UNICODE_STRING BaseDllName;
    DWORD dwModuleHash;
    DWORD dwFunctionHash;
    PCSTR pTempChar;
    DWORD i;

#if defined(_WIN64)
    PebAddress = (PPEB)__readgsqword(0x60);
#elif defined(_M_ARM)
    // I can assure you that this is not a mistake. The C compiler improperly emits the proper opcodes
    // necessary to get the PEB.Ldr address
    PebAddress = (PPEB)((ULONG_PTR)_MoveFromCoprocessor(15, 0, 13, 0, 2) + 0);
    __emit(0x00006B1B);
#else
    PebAddress = (PPEB)__readfsdword(0x30);
#endif

    pLdr = (PMY_PEB_LDR_DATA)PebAddress->Ldr;
    pNextModule = pLdr->InLoadOrderModuleList.Flink;
    pDataTableEntry = (PMY_LDR_DATA_TABLE_ENTRY)pNextModule;

    while (pDataTableEntry->DllBase != NULL)
    {
        dwModuleHash = 0;
        pModuleBase = pDataTableEntry->DllBase;
        BaseDllName = pDataTableEntry->BaseDllName;
        pNTHeader = (PIMAGE_NT_HEADERS)((ULONG_PTR)pModuleBase + ((PIMAGE_DOS_HEADER)pModuleBase)->e_lfanew);
        dwExportDirRVA = pNTHeader->OptionalHeader.DataDirectory[0].VirtualAddress;

        // Get the next loaded module entry
        pDataTableEntry = (PMY_LDR_DATA_TABLE_ENTRY)pDataTableEntry->InLoadOrderLinks.Flink;

        // If the current module does not export any functions, move on to the next module.
        if (dwExportDirRVA == 0)
        {
            continue;
        }

        // Calculate the module hash
        for (i = 0; i < BaseDllName.MaximumLength; i++)
        {
            pTempChar = ((PCSTR)BaseDllName.Buffer + i);

            dwModuleHash = ROTR32(dwModuleHash, 13);

            if (*pTempChar >= 0x61)
            {
                dwModuleHash += *pTempChar - 0x20;
            }
            else
            {
                dwModuleHash += *pTempChar;
            }
        }

        pExportDir = (PIMAGE_EXPORT_DIRECTORY)((ULONG_PTR)pModuleBase + dwExportDirRVA);

        dwNumFunctions = pExportDir->NumberOfNames;
        pdwFunctionNameBase = (PDWORD)((PCHAR)pModuleBase + pExportDir->AddressOfNames);

        for (i = 0; i < dwNumFunctions; i++)
        {
            dwFunctionHash = 0;
            pFunctionName = (PCSTR)(*pdwFunctionNameBase + (ULONG_PTR)pModuleBase);
            pdwFunctionNameBase++;

            pTempChar = pFunctionName;

            do
            {
                dwFunctionHash = ROTR32(dwFunctionHash, 13);
                dwFunctionHash += *pTempChar;
                pTempChar++;
            } while (*(pTempChar - 1) != 0);

            dwFunctionHash += dwModuleHash;

            if (dwFunctionHash == dwModuleFunctionHash)
            {
                usOrdinalTableIndex = *(PUSHORT)(((ULONG_PTR)pModuleBase + pExportDir->AddressOfNameOrdinals) + (2 * i));
                return (HMODULE)((ULONG_PTR)pModuleBase + *(PDWORD)(((ULONG_PTR)pModuleBase + pExportDir->AddressOfFunctions) + (4 * usOrdinalTableIndex)));
            }
        }
    }

    // All modules have been exhausted and the function was not found.
    return NULL;
}

// this string need to add .reloc table to x64 .exe file
const char* data = "reloc_fix";

VOID CodeRun(VOID)
{
    char module[] = { 'u', 's', 'e', 'r', '3', '2', '.', 'd', 'l', 'l', 0 };
    FuncLoadLibraryA MyLoadLibraryA = (FuncLoadLibraryA)GetProcAddressByHash(0x0726774C);
    MyLoadLibraryA((LPCSTR)module);
    FuncMessageBoxA MyMessageBoxA = (FuncMessageBoxA)GetProcAddressByHash(0x07568345);
    MyMessageBoxA(0, 0, 0, 0);
}
hasherezade commented 4 years ago

Hmm, did you launch the 64 bit app with runshc32.exe? It is a version for 32 bit binaries only.Try launching it with runshc64.exe rather than runshc32.exe and let me know if it helps. In the meanwhile I will check your code...

pokevas commented 4 years ago

My mistake is in the description. Of course, I tested the x64 shellcode with runshc64.exe And I thought maybe a problem with runshc64.exe, and tried other methods (injection into the process, for example). But the result is negative. I will attach compiled versions in the archive just in case (something can help): test_x86.exe - compiled .exe (working) test_x86_shellcode.bin - compiled shellcode (working) test_x64.exe - compiled .exe (working) test_x64_shellcode.bin - compiled shellcode (NOT working) attach_files.zip

hasherezade commented 4 years ago

Ok, thank you for the clarification. Sometimes users commit such mistakes, so I just wanted to start by making sure it is not the case. I will examine your materials and check what could be the cause.

hasherezade commented 4 years ago

I compiled your code as multi-threaded (flag /MT) and it works fine. Possibly the compilation options were the problem. Check it out: test_case1_64.zip

pokevas commented 4 years ago

You example working. Maybe differences in the compiler options? Could you attach the sln of your project, I will try on it. I attach my, can you see? Maybe something is wrong. I also compile in MT mode. test_project.zip

hasherezade commented 4 years ago

I generated my sln with the help of CMake, using this CMakeLists.txt:

cmake_minimum_required (VERSION 2.8)
project (test_case1)

set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")

set (srcs
    main.cpp
)

set (hdrs
)

add_executable ( ${PROJECT_NAME} ${hdrs} ${srcs})

And the main.cpp is just your code (with the main function added). Summing up, it looks like this:

main.cpp

#include <Windows.h>
#include <winternl.h>
#include <iostream>

#define ROTR32(value, shift) (((DWORD) value >> (BYTE) shift) | ((DWORD) value << (32 - (BYTE) shift)))

typedef struct _MY_PEB_LDR_DATA {
    ULONG Length;
    BOOL Initialized;
    PVOID SsHandle;
    LIST_ENTRY InLoadOrderModuleList;
    LIST_ENTRY InMemoryOrderModuleList;
    LIST_ENTRY InInitializationOrderModuleList;
} MY_PEB_LDR_DATA, *PMY_PEB_LDR_DATA;

typedef struct _MY_LDR_DATA_TABLE_ENTRY
{
    LIST_ENTRY InLoadOrderLinks;
    LIST_ENTRY InMemoryOrderLinks;
    LIST_ENTRY InInitializationOrderLinks;
    PVOID DllBase;
    PVOID EntryPoint;
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;
} MY_LDR_DATA_TABLE_ENTRY, *PMY_LDR_DATA_TABLE_ENTRY;

typedef HMODULE(WINAPI* FuncLoadLibraryA) (LPCSTR lpFileName);
typedef int(WINAPI* FuncMessageBoxA)(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);

HMODULE GetProcAddressByHash(_In_ DWORD dwModuleFunctionHash)
{
    PPEB PebAddress;
    PMY_PEB_LDR_DATA pLdr;

    PMY_LDR_DATA_TABLE_ENTRY pDataTableEntry;
    PVOID pModuleBase;
    PIMAGE_NT_HEADERS pNTHeader;
    DWORD dwExportDirRVA;
    PIMAGE_EXPORT_DIRECTORY pExportDir;
    PLIST_ENTRY pNextModule;
    DWORD dwNumFunctions;
    USHORT usOrdinalTableIndex;
    PDWORD pdwFunctionNameBase;
    PCSTR pFunctionName;
    UNICODE_STRING BaseDllName;
    DWORD dwModuleHash;
    DWORD dwFunctionHash;
    PCSTR pTempChar;
    DWORD i;

#if defined(_WIN64)
    PebAddress = (PPEB)__readgsqword(0x60);
#elif defined(_M_ARM)
    // I can assure you that this is not a mistake. The C compiler improperly emits the proper opcodes
    // necessary to get the PEB.Ldr address
    PebAddress = (PPEB)((ULONG_PTR)_MoveFromCoprocessor(15, 0, 13, 0, 2) + 0);
    __emit(0x00006B1B);
#else
    PebAddress = (PPEB)__readfsdword(0x30);
#endif

    pLdr = (PMY_PEB_LDR_DATA)PebAddress->Ldr;
    pNextModule = pLdr->InLoadOrderModuleList.Flink;
    pDataTableEntry = (PMY_LDR_DATA_TABLE_ENTRY)pNextModule;

    while (pDataTableEntry->DllBase != NULL)
    {
        dwModuleHash = 0;
        pModuleBase = pDataTableEntry->DllBase;
        BaseDllName = pDataTableEntry->BaseDllName;
        pNTHeader = (PIMAGE_NT_HEADERS)((ULONG_PTR)pModuleBase + ((PIMAGE_DOS_HEADER)pModuleBase)->e_lfanew);
        dwExportDirRVA = pNTHeader->OptionalHeader.DataDirectory[0].VirtualAddress;

        // Get the next loaded module entry
        pDataTableEntry = (PMY_LDR_DATA_TABLE_ENTRY)pDataTableEntry->InLoadOrderLinks.Flink;

        // If the current module does not export any functions, move on to the next module.
        if (dwExportDirRVA == 0)
        {
            continue;
        }

        // Calculate the module hash
        for (i = 0; i < BaseDllName.MaximumLength; i++)
        {
            pTempChar = ((PCSTR)BaseDllName.Buffer + i);

            dwModuleHash = ROTR32(dwModuleHash, 13);

            if (*pTempChar >= 0x61)
            {
                dwModuleHash += *pTempChar - 0x20;
            }
            else
            {
                dwModuleHash += *pTempChar;
            }
        }

        pExportDir = (PIMAGE_EXPORT_DIRECTORY)((ULONG_PTR)pModuleBase + dwExportDirRVA);

        dwNumFunctions = pExportDir->NumberOfNames;
        pdwFunctionNameBase = (PDWORD)((PCHAR)pModuleBase + pExportDir->AddressOfNames);

        for (i = 0; i < dwNumFunctions; i++)
        {
            dwFunctionHash = 0;
            pFunctionName = (PCSTR)(*pdwFunctionNameBase + (ULONG_PTR)pModuleBase);
            pdwFunctionNameBase++;

            pTempChar = pFunctionName;

            do
            {
                dwFunctionHash = ROTR32(dwFunctionHash, 13);
                dwFunctionHash += *pTempChar;
                pTempChar++;
            } while (*(pTempChar - 1) != 0);

            dwFunctionHash += dwModuleHash;

            if (dwFunctionHash == dwModuleFunctionHash)
            {
                usOrdinalTableIndex = *(PUSHORT)(((ULONG_PTR)pModuleBase + pExportDir->AddressOfNameOrdinals) + (2 * i));
                return (HMODULE)((ULONG_PTR)pModuleBase + *(PDWORD)(((ULONG_PTR)pModuleBase + pExportDir->AddressOfFunctions) + (4 * usOrdinalTableIndex)));
            }
        }
    }

    // All modules have been exhausted and the function was not found.
    return NULL;
}

// this string need to add .reloc table to x64 .exe file
const char* data = "reloc_fix";

VOID CodeRun(VOID)
{
    char module[] = { 'u', 's', 'e', 'r', '3', '2', '.', 'd', 'l', 'l', 0 };
    FuncLoadLibraryA MyLoadLibraryA = (FuncLoadLibraryA)GetProcAddressByHash(0x0726774C);
    MyLoadLibraryA((LPCSTR)module);
    FuncMessageBoxA MyMessageBoxA = (FuncMessageBoxA)GetProcAddressByHash(0x07568345);
    MyMessageBoxA(0, 0, 0, 0);
}

int main()
{
    CodeRun();
    return 0;
}
pokevas commented 4 years ago

Maybe problem that i use custom entrypoint and /NODEFAULTLIB ?

hasherezade commented 4 years ago

Possibly... I am gonna check in details how exactly it affects the loading process... I will let you know what I found.

pokevas commented 4 years ago

Thanks you. I use simple project, but can't find where problem.

hasherezade commented 4 years ago

Thanks to your testcase I found some bugs in the stub, and I fixed them. It should work now. Please let me know if everything is fine.

You can download the latest build from the build server: x64bit build

pokevas commented 4 years ago

Now all okey. Thanks you!