Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not work with x64 independent code #9

Closed
pokevas opened this issue Apr 17, 2020 · 11 comments
Closed

Not work with x64 independent code #9

pokevas opened this issue Apr 17, 2020 · 11 comments

Comments

@pokevas
Copy link

pokevas commented Apr 17, 2020

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
Copy link
Owner

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
Copy link
Author

pokevas commented Apr 17, 2020

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
Copy link
Owner

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
Copy link
Owner

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
Copy link
Author

pokevas commented Apr 17, 2020

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
Copy link
Owner

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
Copy link
Author

pokevas commented Apr 17, 2020

Maybe problem that i use custom entrypoint and /NODEFAULTLIB ?

@hasherezade
Copy link
Owner

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

@pokevas
Copy link
Author

pokevas commented Apr 17, 2020

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

hasherezade added a commit that referenced this issue Apr 17, 2020
@hasherezade
Copy link
Owner

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
Copy link
Author

pokevas commented Apr 17, 2020

Now all okey.
Thanks you!

@pokevas pokevas closed this as completed Apr 17, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants