NativeInjectionEntryPoint in DLL on x64

Jun 15, 2014 at 6:58 AM
Hello, I am new to dll creation, injection and hooking in general so please forgive my lack of knowledge on this. I've been at this for a day or so trying to wrap my head around everything. I've been following the Unmanaged API Reference and I'm trying to do a remote hook on my own machine. I would just like to confirm how the DLL is supposed to look because the terminology from the reference manual is confusing me.

Part 1

Here is my DLL.
#include <Windows.h>
#include "EasyHook.h"
#define SIZE 6

typedef int (WINAPI *pMessageBoxW)(HWND, LPCWSTR, LPCWSTR, UINT); // Messagebox prototype
int WINAPI MyMessageBoxW(HWND, LPCWSTR, LPCWSTR, UINT); // Our detour

void __stdcall NativeInjectionEntryPoint(REMOTE_ENTRY_INFO* InRemoteInfo);

void BeginRedirect(LPVOID);
pMessageBoxW pOrigMBAddress = NULL; // address of original
BYTE oldBytes[SIZE] = {0};  // backup
BYTE JMP[SIZE] = {0};   // 6 byte JMP instruction
DWORD oldProtect, myProtect = PAGE_EXECUTE_READWRITE;

INT APIENTRY DllMain(HMODULE hDLL, DWORD Reason, LPVOID Reserved){
    switch(Reason){
    case DLL_PROCESS_ATTACH:    // if attached
        // get address of original
        pOrigMBAddress = (pMessageBoxW)GetProcAddress(GetModuleHandle(L"user32.dll"),"MessageBoxW");
        
        // start detouring
        if(pOrigMBAddress != NULL)
            BeginRedirect(MyMessageBoxW);
        break;
        // restore backup
    case DLL_PROCESS_DETACH:
        memcpy(pOrigMBAddress, oldBytes, SIZE);
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
        break;
    }
    return TRUE;
}

void BeginRedirect(LPVOID newFunction){
    BYTE tempJMP[SIZE] = {0xE9, 0x90, 0x90, 0x90, 0x90, 0xC3};  // 0xE9 = JMP, 0x90 = NOP, 0xC3 = RET
    memcpy(JMP, tempJMP, SIZE); // store jmp instruction to JMP
    DWORD JMPSize = ((DWORD)newFunction - (DWORD)pOrigMBAddress - 5);   // calculate jump distance
    // assign read write protection
    VirtualProtect((LPVOID)pOrigMBAddress, SIZE, PAGE_EXECUTE_READWRITE, &oldProtect);
    memcpy(oldBytes, pOrigMBAddress, SIZE); // make backup
    memcpy(&JMP[1], &JMPSize, 4);   // fill the nop's with the jump distance (JMP, distance(4bytes), RET)
    memcpy(pOrigMBAddress, JMP, SIZE);
    VirtualProtect((LPVOID)pOrigMBAddress, SIZE, oldProtect, NULL); // reset protection
}

int WINAPI MyMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uiType){
    VirtualProtect((LPVOID)pOrigMBAddress, SIZE, myProtect, NULL);
    memcpy(pOrigMBAddress, oldBytes, SIZE);
    int retValue = MessageBoxW(hWnd, lpText, lpCaption, uiType);
    memcpy(pOrigMBAddress, JMP, SIZE);
    VirtualProtect((LPVOID)pOrigMBAddress, SIZE, oldProtect, NULL);
    return retValue;
}

void _stdcall NativeInjectionEntryPoint(REMOTE_ENTRY_INFO* InRemoteInfo)(){
        
}
When the Unmanaged Reference manual says on page 26:

EASYHOOK_INJECT_DEFAULT
No special behavior. The given libraries are expected to be unmanaged DLLs.
Further they should export an entry point named "NativeInjectionEntryPoint"
(in case of 64-bit) and "_NativeInjectionEntryPoint@4" (in case of 32-bit). The
expected entry point signature is REMOTE_ENTRY_POINT.

Am I doing everything right? This is for x64 with a 64bit program. Is every function that I declare at the top an entry point or is only DLLMain an entry point, the functions declared at the top of the file are only exported functions and the terminology being used incorrectly?

I'm honestly confused.


Part 2
Here is my injection program. All I need to know is if rhInjectLibrary is all I need to call in this case. I would seriously pay for an unmanaged hooking tutorial with easyhook. Right now I'm floundering with no feedback as to whether what I'm doing is right or wrong.
#ifndef _M_X64
    #pragma comment(lib, "EasyHook32.lib")
#else
    #pragma comment(lib, "EasyHook64.lib")
#endif

#define FORCE(expr)     {if(!SUCCEEDED(NtStatus = (expr))) goto ERROR_ABORT;}

DWORD FindProcessId(const std::wstring& processName);
int DoHook(DWORD processID);

extern "C" int main(int argc, wchar_t* argv[])
{
    int dhook = 0;
    std::cout << "START" << std::endl;
    //const TCHAR substring[] = TEXT("Window_list");
    //EnumWindows(FindWindowBySubstr, (LPARAM)substring);
    std::wstring processName;

    std::wcout << "Enter the process name: ";
    std::getline(std::wcin, processName);
    
    DWORD processID = FindProcessId(processName);

    if ( processID == 0 ){
        std::wcout << "Could not find " << processName.c_str() << std::endl;
    }
    else{
        std::wcout << "Process ID is " << processID << ".  Attempting hook." << std::endl;
        dhook = DoHook(processID);
    }

    return dhook;
}

DWORD FindProcessId(const std::wstring& processName)
{
    PROCESSENTRY32 processInfo;
    processInfo.dwSize = sizeof(processInfo);

    HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
    if ( processesSnapshot == INVALID_HANDLE_VALUE )
        return 0;

    Process32First(processesSnapshot, &processInfo);
    if ( !processName.compare(processInfo.szExeFile) )
    {
        CloseHandle(processesSnapshot);
        return processInfo.th32ProcessID;
    }

    while ( Process32Next(processesSnapshot, &processInfo) )
    {
        if ( !processName.compare(processInfo.szExeFile) )
        {
            CloseHandle(processesSnapshot);
            return processInfo.th32ProcessID;
        }
    }
    
    CloseHandle(processesSnapshot);
    return 0;
}

int DoHook(DWORD processID){

    HMODULE                 hUser32 = LoadLibraryA("user32.dll");
    TRACED_HOOK_HANDLE      hHook = new HOOK_TRACE_INFO();
    NTSTATUS                NtStatus;
    ULONG                   ACLEntries[1] = {0};
    UNICODE_STRING*         NameBuffer = NULL;
    HANDLE                  hRemoteThread;

    FORCE(RhInjectLibrary(processID,
            0,
            EASYHOOK_INJECT_DEFAULT,
            L"testHook.dll",
            NULL,
            0,
            0));

    return 0;

    ERROR_ABORT:

    if(hHook != NULL)
        delete hHook;

    if(NameBuffer != NULL)
        free(NameBuffer );

    printf("\n[Error(0x%p)]: \"%S\" (code: %d {0x%p})\n", (PVOID)NtStatus, RtlGetLastErrorString(), RtlGetLastError(), (PVOID)RtlGetLastError());

    _getch();

    return NtStatus;
}
ANY help would be deeply appreciated.
Coordinator
Jun 18, 2014 at 8:50 AM
At a brief glance your Part 2 looks fine.

I only skimmed Part 1, but are you actually exporting the function anywhere? I.e. a dllexport?

I see you are doing your own hook/redirect, you could also use LhInstallHook for this purpose.

Lastly - perhaps try stepping through using the debugger, to do this I have the easyhook project open with breakpoints as necessary, attach the debugger to the hook target, run the injector app and inject.
Jun 19, 2014 at 2:15 AM
The problem was that I didn't know how to do it and I couldn't figure out what to do when I was also dealing with Typedef'd functions so I thought I had to export REMOTE_ENTRY_POINT like so:
extern "C" __declspec(dllexport) REMOTE_ENTRY_POINT NativeInjectionEntryPoint;
or like so:
extern "C" __declspec(dllexport) REMOTE_ENTRY_POINT NativeInjectionEntryPoint(REMOTE_ENTRY_INFO* InRemoteInfo);
while also trying to match up that declaration with a definition of the same thing. I tried almost every iteration I could think of until stumbled upon the correct way. The right way is this:
extern "C" __declspec(dllexport) void __stdcall NativeInjectionEntryPoint(REMOTE_ENTRY_INFO* InRemoteInfo) {
    
}
I am a noob and I didn't know what the hell I was doing. I probably still don't but at least I have my .dll loaded into the process. It only took me 5 days. T_T There are like no tutorials for the unmanaged portion of the code. Good learning experience but it's hella painful.

Thank you and everyone who's contributed to this project. You guys are awesome. BTW does anyone have any idea what else I might be missing considering I don't know simple things like this?
Marked as answer by spazzarama on 6/22/2014 at 2:14 AM