instead CreateRemoteThread
with a thread subroutine pointing to LoadLibraryW
we can inject tiny shell code to remote process which first call LoadLibraryW
and than, if it fail, GetLastError
- as result remote thread return error code (0 if no error) - and you will be know exactly - are LoadLibrary
ok and if not - have error code. the 64 asm code can be:
CONST segment
SHELLDATA struct
LoadLibrary DQ ?
GetLastError DQ ?
SHELLDATA ends
public RemoteThreadProc_begin
public RemoteThreadProc_end
RemoteThreadProc_begin:
RemoteThreadProc proc
nop
nop
nop
call @@0
___ SHELLDATA <>
@@0:
xchg [rsp],rbp
sub rsp,20h
call SHELLDATA.LoadLibrary[rbp]
test rax,rax
jz @@1
xor eax,eax
@@2:
add rsp,20h
pop rbp
ret
@@1:
call SHELLDATA.GetLastError[rbp]
jmp @@2
RemoteThreadProc endp
RemoteThreadProc_end:
CONST ends
and c++ code:
extern "C"
{
extern UCHAR RemoteThreadProc_begin[], RemoteThreadProc_end[];
}
enum INJECT_PHASE {
fOpenProcess, fVirtualAlloc, fWriteProcessMemory, fCreateRemoteThread, fMax
};
ULONG injectDll(ULONG dwprocessId, PCWSTR dllFilePath, INJECT_PHASE& phase)
{
ULONG err = 0;
struct SHELLDATA
{
__int64 code;
PVOID LoadLibrary, GetLastError;
};
if (HANDLE hProcess = OpenProcess(PROCESS_CREATE_THREAD|PROCESS_VM_OPERATION|PROCESS_VM_WRITE, FALSE, dwprocessId))
{
SIZE_T cbStr = (wcslen(dllFilePath) + 1) * sizeof(WCHAR);
SIZE_T cbCode = ((RemoteThreadProc_end - RemoteThreadProc_begin) + sizeof(WCHAR) - 1) & ~(sizeof(WCHAR) - 1);
union {
PVOID RemoteAddress;
PBYTE pbRemote;
PTHREAD_START_ROUTINE lpStartAddress;
};
if (RemoteAddress = VirtualAllocEx(hProcess, 0, cbStr + cbCode, MEM_COMMIT, PAGE_EXECUTE_READWRITE))
{
union {
PVOID pv;
PBYTE pb;
SHELLDATA* ps;
};
pv = alloca(cbStr + cbCode);
memcpy(pv, RemoteThreadProc_begin, cbCode);
memcpy(pb + cbCode, dllFilePath, cbStr);
HMODULE hmod = GetModuleHandle(L"kernel32");
ps->GetLastError = GetProcAddress(hmod, "GetLastError");
ps->LoadLibrary = GetProcAddress(hmod, "LoadLibraryW");
if (WriteProcessMemory(hProcess, RemoteAddress, pv, cbStr + cbCode, 0))
{
if (HANDLE hThread = CreateRemoteThread(hProcess, 0, 0, lpStartAddress, pbRemote + cbCode, 0, 0))
{
phase = fMax;
WaitForSingleObject(hThread, INFINITE);
GetExitCodeThread(hThread, &err);
CloseHandle(hThread);
}
else
{
phase = fCreateRemoteThread;
err = GetLastError();
}
}
else
{
phase = fWriteProcessMemory;
err = GetLastError();
}
VirtualFreeEx(hProcess, RemoteAddress, 0, MEM_RELEASE);
}
else
{
phase = fVirtualAlloc;
err = GetLastError();
}
CloseHandle(hProcess);
}
else
{
phase = fOpenProcess;
err = GetLastError();
}
return err;
}