-2

I am trying to learn some manual dll injection, but cant seem to get the execution of the dlls code to work. I am new to Windows C++ so any tips on improving my code is appreciated. I have also only posted the relevant code.

Injector program:

hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, getPID(TARGET_NAME));

DWORD gotDLL = GetFullPathName(DLL_NAME, MAX_PATH, dllPath, NULL);

hFile = CreateFile(dllPath, GENERIC_READ, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

dllFileSize = GetFileSize(hFile, NULL);
memAddrForDLL = VirtualAllocEx(hProcess, NULL, dllFileSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);

loadedDLL = HeapAlloc(GetProcessHeap(), NULL, dllFileSize);

// Load dll into allocated memory in current process
ReadFile(hFile, loadedDLL, dllFileSize, &bytesRead, NULL))

// Find offset of dll entry point
IMAGE_NT_HEADERS* pOldNtHeader = reinterpret_cast<IMAGE_NT_HEADERS*>(reinterpret_cast<BYTE*>(loadedDLL) + reinterpret_cast<IMAGE_DOS_HEADER*>(loadedDLL)->e_lfanew);
IMAGE_OPTIONAL_HEADER* pOldOptHeader = &pOldNtHeader->OptionalHeader;

entryPointOffset = pOldOptHeader->AddressOfEntryPoint;

// Load dll into allocated memory in target process
WriteProcessMemory(hProcess, memAddrForDLL, loadedDLL, bytesRead, NULL)

LPTHREAD_START_ROUTINE entryPoint = (LPTHREAD_START_ROUTINE)((unsigned __int64)memAddrForDLL + entryPointOffset);

CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibrary, entryPoint, NULL, NULL)

DLL:

DWORD WINAPI OnDllAttach(LPVOID base){
    typedef void func(void);
    func* f = (func*)0x00007FF605EC5835;
    f();
    FreeLibraryAndExitThread(static_cast<HMODULE>(base),1);
}

BOOL WINAPI OnDllDetach(){
    return TRUE;
}

BOOL WINAPI DllMain(_In_ HINSTANCE hinstDll,
                    _In_ DWORD fdwReason,
                    _In_opt_ LPVOID lpvReserved){
    typedef void func(void);
    func* f = (func*)0x00007FF605EC5835;
    f();
    switch(fdwReason) {
        case DLL_PROCESS_ATTACH:
            DisableThreadLibraryCalls(hinstDll);
            CreateThread(nullptr, 0, OnDllAttach, hinstDll, 0, nullptr);
            return TRUE;
        case DLL_PROCESS_DETACH:
            if(lpvReserved == nullptr)
                return OnDllDetach();
            return TRUE;
        default:
            return TRUE;
    }
}

Target program contains this function:

void printer(){
    cout << "test" << endl;
}

My injector produces the following output

1. Attempting to attatch to process target.exe
--- Got target.exe PID: 14640
--- Got target.exe Handle: 0x0000000000000084
2. Attempting to allocate memory
--- Found dll: D:\projects\injector\hack.dll
--- Got hack.dll Handle: 0x0000000000000088
--- Allocated memory in target.exe at 0x0000017BEB690000
3. Attempting to copy dll to target.exe
--- Allocated memory at 0x00000226A060FFE0
--- Loaded hack.dll in current process at 0x00000226A060FFE0
--- hack.dll is a valid DLL
--- Loaded hack.dll into target.exe at 0x0000017BEB690000
4. Attempting to execute dll
--- Offset from start of file to entrypoint: 0x3cf6
--- Began execution of hack.dll in target.exe at 0x0000017BEB693CF6

Using Ghidra I can confirm this is the correct offset for the dll entrypoint. But when running my injector nothing happens in the target process, I've also tried using cout to print a message from the dll but I get nothing(I dont think it would even work because nothing has been relocated)

I was using

CreateRemoteThread(hProcess, NULL, NULL, entryPoint, memAddrForDLL, NULL, NULL)

before as the 4th parameter is called lpStartAddress and I thought this should want the entry point but it was causing the target process to crash and every example I saw used the way I currently have it in my code.

In my dll I am calling the function in the target process by the address.

EDIT: I am testing this on my own console application.

Mumphus
  • 303
  • 4
  • 20
  • You've only really proved that std::cout doesn't work. That's entirely normal, worst possible way to prove that injection worked. Might not be a console app you are injecting. The injected process might not use the same C++ library that you are using. Use a function that is guaranteed to be the same, like CreateFile(). – Hans Passant Apr 19 '19 at 22:28
  • in my test case it is a console program and it would be using the same library as I compiled them. I will edit post to make clear – Mumphus Apr 19 '19 at 22:29
  • Don't create threads in DllMain – Anders Apr 20 '19 at 00:42
  • @Anders I just saw that in other examples. But regardless that doesnt explain why calling the function doesnt work – Mumphus Apr 20 '19 at 03:01

2 Answers2

4

The most basic form of DLL injection is:

  • Allocating Memory in target process using VirtualAllocEx()
  • Writing the path of the DLL to that memory location with WriteProcessMemory
  • Calling LoadLibrary() via CreateRemoteThread() in the target process
  • Passing the memory location where the DLL path is written to that call

You already have this but, your goal is to manually map the DLL and avoid using LoadLibrary(). The code you have provided is not going to work out, there are about 5 more steps. You need to emulate everything that LoadLibrary() normally does:

  • Load raw binary data
  • Map sections into target process
  • Inject loader shellcode
  • Do relocations
  • Fix imports
  • Execute TLS callbacks
  • Call DllMain
  • Cleanup

The benefits of manually mapping are that you will be hidden from ToolHelp32Snapshot(), walking the module linked list in the PEB and NtQueryVirtualMemory.

If you want to do it right, with decent error checking, it's about 350 lines of code and it gets complicated. It's all done by parsing the PE header.

  1. Get the process id of the target
  2. Read the DLL file
  3. Allocate memory in target process the same size as ImageBase from the PE header
  4. Loop through PE sections after parsing the PE header
  5. Write the section to memory in the correct relative address
  6. Write shellcode into target process
  7. Call CreateRemoteThread and set your shellcode to execute
  8. Your shellcode fixes imports and does relocations
  9. Your Shellcode Execute TLS callbacks
  10. The above 2 steps are done parsing the DataDirectory in the optional header
  11. Call DllMain(), with DLL_PROCESS_ATTACH argument

Now your DLL is loaded and the DLL_PROCESS_ATTACH switch case is executing. Obivously it's more complicated than that, but that's the idea.

I wouldn't know anything about this without my friend Broihon teaching me it, so I want credit this answer to him. Good luck

GuidedHacking
  • 3,628
  • 1
  • 9
  • 59
0

A .DLL loaded in memory is not the same thing as a .DLL file on disk. The section layout is not the same and you need to deal with relocations, the import table and the PEB loaded module list. You basically have to re-implement NTDLL!Ldr*.

Calling CreateRemoteThread on LoadLibrary is a different technique and when you do this the thread parameter needs to point to the .DLLs path in the remote process, not the entrypoint.

Anders
  • 97,548
  • 12
  • 110
  • 164
  • so calling on LoadLibrary is for loading the dll automatically. And if I am wanting to manually map the DLL I would have to write my own version of LoadLibrary, Is that correct? – Mumphus Apr 20 '19 at 23:23
  • Yes. Most people just use LoadLibrary because it matches the signature of a thread function. – Anders Apr 21 '19 at 00:36