I want to hook a Windows process and replace CRT's srand
with my own function that will use a static seed (42
). For this I decided to spawn a new process in a suspended state, install the hook and then resume the target's thread, so the target application when it is actutually gets executed has no way but use my own srand
. I created this code following the EasyHook tutorials:
// hook.cpp
#include "easyhook.h"
#include <string>
#include <iostream>
#include <Windows.h>
extern "C" void __declspec(dllexport) __stdcall NativeInjectionEntryPoint(REMOTE_ENTRY_INFO* inRemoteInfo);
static void StaticSrand(int seed) {
std::cout << "[HOOK] called StaticSrand(" << seed << ")" << std::endl;
const int staticSeed = 42;
srand(staticSeed);
}
static void OverwriteFunc(REMOTE_ENTRY_INFO *inRemoteInfo, const char *dll, const char *funcName, void *func) {
auto address = GetProcAddress(GetModuleHandle(TEXT(dll)), funcName);
HOOK_TRACE_INFO hHook = { NULL };
NTSTATUS result = LhInstallHook(address, func, NULL, &hHook);
if (FAILED(result)) {
std::wstring s(RtlGetLastErrorString());
std::wcout << "[HOOK] failed to install hook: " << s << std::endl;
} else {
std::cout << "[HOOK] successfully installed hook by " << inRemoteInfo->HostPID << ", function '" << funcName << "' at " << address << std::endl;
}
ULONG ACLEntries[1] = { 0 };
LhSetExclusiveACL(ACLEntries, 1, &hHook);
}
void __stdcall NativeInjectionEntryPoint(REMOTE_ENTRY_INFO* inRemoteInfo) {
OverwriteFunc(inRemoteInfo, "ucrtbase", "srand", StaticSrand);
}
// injector.cpp
#include <iostream>
#include "easyhook.h"
#include <Windows.h>
static LPCWSTR TARGET_EXE = L"target.exe";
static LPWSTR DLL_PATH_W = L"hook.dll";
int main() {
// Create suspended process so the srand() hook is installed before the
// target EXE can call it, on its startup for example. When the hook is
// successfully installed we resume the target process, therefore it has no
// other way but use or own srand() instead of the CRT's one.
STARTUPINFOW si = { sizeof(si) };
PROCESS_INFORMATION pi;
BOOL result = CreateProcessW(TARGET_EXE, NULL, NULL, NULL, TRUE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
if (!result) {
std::cout << "[INJECTOR] failed to create process" << std::endl;
return EXIT_FAILURE;
}
NTSTATUS err = RhInjectLibrary(
pi.dwProcessId,
0, // Wake Up TID
EASYHOOK_INJECT_DEFAULT, // Default options
NULL, // 32-bit DLL
DLL_PATH_W, // 64-bit DLL
NULL, 0 // No passthrough data
);
if (err != 0) {
std::cout << "[INJECTOR] failed to inject DLL" << std::endl;
return EXIT_FAILURE;
}
std::cout << "[INJECTOR] successfully inject DLL into the process, resuming the thread" << std::endl;
ResumeThread(pi.hThread);
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
// target.cpp
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <Windows.h>
int main(void) {
while (true) {
const int seed = time(NULL);
srand(seed);
const int r0 = rand();
const int r1 = rand();
const int r2 = rand();
std::cout << "Seed is " << seed << ": " << r0 << " " << r1 << " " << r2 << std::endl;
Sleep(1000);
}
}
I compile the files using MSVC 19.38.33130 x64:
cl /LD hook.cpp EasyHook64.lib /EHsc
cl injector.cpp EasyHook64.lib /EHsc
cl target.cpp /EHsc
Everything compiles without any warnings. But when I run injector.exe
I got the following output:
C:\Users\User\Desktop\Hook>injector.exe
[INJECTOR] successfully inject DLL into the process, resuming the thread
[HOOJ] successfully installed hook by 9304, function 'srand' at 00007Seed is FFEAC5120D10
700850449: 20391 11210 1132
Seed is 1700850450: 20394 21959 18996
Seed is 1700850451: 20397 32707 4092
Seed is 1700850452: 20401 10687 21957
Seed is 1700850453: 20404 21436 7053
Seed is 1700850454: 20407 32184 24917
Seed is 1700850455: 20410 10165 10013
Seed is 1700850456: 20414 20913 27877
Seed is 1700850457: 20417 31661 12973
Seed is 1700850458: 20420 9642 30838
^C
So you can see although it seems that the hook is successfully installed, the srand
call is not proxied by the hook and still uses current time as the seed. I have no prior experience with Windows API and EasyHook, what I am missing here?