Post subject: Feature suggestion: rand() tracking and prediction
upthorn
He/Him
Emulator Coder, Active player (388)
Joined: 3/24/2006
Posts: 1802
I've discovered (and tested) the algorithm that windows' implementation of cstdlib uses to produce random numbers.
Compare how many numbers? 15
With what seed? 528
rand()  myrand()
1762    1762
14014   14014
16336   16336
26650   26650
15462   15462
20726   20726
13339   13339
26780   26780
12282   12282
28464   28464
26063   26063
18589   18589
25061   25061
20453   20453
7055    7055
Press any key to continue . . .
Numbers on the left are produced by actually calling rand(), numbers on the right are produced by calling my reimplementation with the same seed value. This seems like it would be useful functionality for hourglass, to have, but unfortunately, in real use cases random seed is stored in dynamically allocated memory, pointed to from dynamically allocated memory, pointed to from dynamically allocated memory which means that, even using MHS, it is very difficult to find and track in a meaningful way. However, it seems like it should be relatively simple for hourglass to hook rand() and track the per-thread _storerand value somewhere user accessible.
How fleeting are all human passions compared with the massive continuity of ducks.
Emulator Coder, Skilled player (1301)
Joined: 12/21/2004
Posts: 2687
I think the CRT is often statically linked, in which case I wouldn't know how to hook rand() since it's not exported by the game and I don't know its address. It might be easier to find _holdrand in the _tiddata in the game's TEB, or expose the TLS slots as if they're at fixed addresses. Another possibility is: save a state, call rand() in a thread of interest, compare the savestate with current memory to determine which address the seed is stored at (since it's the only thing that should have changed), then overwrite the seed with the old value and cache its address.
upthorn
He/Him
Emulator Coder, Active player (388)
Joined: 3/24/2006
Posts: 1802
nitsuja wrote:
I think the CRT is often statically linked, in which case I wouldn't know how to hook rand() since it's not exported by the game and I don't know its address. It might be easier to find _holdrand in the _tiddata in the game's TEB, or expose the TLS slots as if they're at fixed addresses.
I noticed that, and did a preliminary experiment hooking TlsGetValue, which is called by _getptd (also statically linked), which is called by rand. TlsGetValue, unfortunately, is called so frequently that simply adding a 1-line debugprintf slowed the game significantly and ended up creating 80 megabyte log files for ~100 frames of cave story... Edit: Additionally, while TlsAlloc should only be called once per thread, it either isn't getting called at all in cave story, or I can't get it hooked before that one call happens.
How fleeting are all human passions compared with the massive continuity of ducks.
upthorn
He/Him
Emulator Coder, Active player (388)
Joined: 3/24/2006
Posts: 1802
I've made a breakthrough. It turns out _getptd() prefers to use Fls functions to Tls functions when they're available. By hooking FlsAlloc and GetFlsValue, and installing some temporary hacks, I've been able to coerce hourglass into automatically adding each fiber-local random seed to the watchlist.
How fleeting are all human passions compared with the massive continuity of ducks.
Emulator Coder, Skilled player (1301)
Joined: 12/21/2004
Posts: 2687
Maybe this is a difference from XP, since FlsAlloc doesn't seem to exist on XP, and I see TlsAlloc is easily hooked and gets called by doukutsu.exe on my system. Ideally, a feature like this could be implemented without any hooking at all (using only calls to ReadProcessMemory and GetThreadSelectorEntry running outside of the game). Failing that, hopefully there's a way to only hook TlsAlloc/FlsAlloc, since I'd expect TlsGetValue/FlsGetValue to get called often enough that hooking them should at least be optional.
upthorn
He/Him
Emulator Coder, Active player (388)
Joined: 3/24/2006
Posts: 1802
Unfortunately, TlsAlloc and FlsAlloc appear not to actually initialize (or allocate) the local storage when they're called, so if we hook only the Allocs, we would then have to wait x milliseconds (causing a race condition) for the program to subsequently initialize a tiddata struct and assign its non-null pointer to the local storage index. So our options are to hook *lsAlloc and *lsGetData or to identify at least one different exported function that is only ever called after the thread/fiber local storage is entirely initialized. Perhaps we could have the custom *lsAlloc hook *lsGetValue, and *lsGetValue unhook itself when it's called with the index *lsAlloc last returned... There's another wrinkle to this, though. On my Windows 7, hooking TlsAlloc causes all sorts of weird issues, like the game immediately crashing the first time it's run, and sound failing to play, while FlsAlloc works with no trouble at all.
How fleeting are all human passions compared with the massive continuity of ducks.
Emulator Coder, Skilled player (1301)
Joined: 12/21/2004
Posts: 2687
upthorn wrote:
Unfortunately, TlsAlloc and FlsAlloc appear not to actually initialize (or allocate) the local storage when they're called, so if we hook only the Allocs, we would then have to wait x milliseconds (causing a race condition) for the program to subsequently initialize a tiddata struct and assign its non-null pointer to the local storage index.
Maybe I'll make a "hook nothing" version later, but to get the addresses without hooking F/TlsAlloc or F/TlsGetValue, try:
// in moduletramps.h

#define TlsSetValue TrampTlsSetValue
TRAMPFUNC BOOL WINAPI TlsSetValue(DWORD dwTlsIndex, LPVOID lpTlsValue) TRAMPOLINE_DEF
#define FlsSetValue TrampFlsSetValue
TRAMPFUNC BOOL WINAPI FlsSetValue(DWORD dwFlsIndex, LPVOID lpFlsData) TRAMPOLINE_DEF

// in modulehooks.cpp

HOOKFUNC BOOL WINAPI MyTlsSetValue(DWORD dwTlsIndex, LPVOID lpTlsValue)
{
	// don't put any code before the call to TlsSetValue, since there's a danger of recursion
	BOOL rv = TlsSetValue(dwTlsIndex, lpTlsValue);
	if(lpTlsValue)
		debugprintf(__FUNCTION__ " called. thread=0x%X, index=%d, address=0x%X.\n", GetCurrentThreadId(), dwTlsIndex, lpTlsValue);
	return rv;
}

HOOKFUNC BOOL WINAPI MyFlsSetValue(DWORD dwFlsIndex, LPVOID lpFlsData)
{
	// don't put any code before the call to FlsSetValue, since there's a danger of recursion
	BOOL rv = FlsSetValue(dwFlsIndex, lpFlsData);
	if(lpFlsData)
		debugprintf(__FUNCTION__ " called. thread=0x%X, index=%d, address=0x%X.\n", GetCurrentThreadId(), dwFlsIndex, lpFlsData);
	return rv;
}

// in ApplyModuleIntercepts table

		MAKE_INTERCEPT(1, KERNEL32, TlsSetValue),
		MAKE_INTERCEPT(1, KERNEL32, FlsSetValue),
(To actually pass the data up, I'd use cmdprintf instead of debugprintf, with some new command prefix.)
upthorn wrote:
On my Windows 7, hooking TlsAlloc causes all sorts of weird issues, like the game immediately crashing the first time it's run, and sound failing to play, while FlsAlloc works with no trouble at all.
I'm guessing this is because the Tls or Fls functions might not exist in kernel32.dll depending on the OS version, so if you try to call them anywhere (even if the code won't run and you don't actually hook anything) it will cause the game to crash on startup. For example, on XP, if I try to hook FlsAlloc and call FlsGetValue from inside MyFlsAlloc, it always crashes the game on startup because FlsGetValue doesn't exist, but if I hook FlsGetValue also then it won't crash because my call to it becomes a call to the trampoline, which always exists (even if I disable the hook by using 0 instead of 1 in its MAKE_INTERCEPT). Of course if the trampoline to a non-existent function actually gets called then it won't work but that's not usually a problem, for example in this case FlsGetValue won't really get called on XP because FlsAlloc doesn't exist either and thus MyFlsAlloc won't get called in the first place.
Patashu
He/Him
Joined: 10/2/2005
Posts: 4017
Where on earth do TRAMPFUNC and HOOKFUNC and WINAPI and so on come from? The top results on google seem to be your project.
My Chiptune music, made in Famitracker: http://soundcloud.com/patashu My twitch. I stream mostly shmups & rhythm games http://twitch.tv/patashu My youtube, again shmups and rhythm games and misc stuff: http://youtube.com/user/patashu
upthorn
He/Him
Emulator Coder, Active player (388)
Joined: 3/24/2006
Posts: 1802
TRAMPFUNC, HOOKFUNC, and WINAPI are calling convention macros. TRAMPFUNC and HOOKFUNC are #define'd in the project itself, but WINAPI is #define'd in the standard windows headers. TRAMPFUNC is an alias for __declspec(noinline), and is used to force the compiler not to optimize out calls to this function by replacing function calls with the function contents. This is important, because the contents of TRAMPFUNC functions change at runtime, and the compiler doesn't know that, so the inlined function contents wouldn't. HOOKFUNC currently does nothing, but it may later be determined that a certain calling specification is ideal for hook functions, at which time it can be applied to all of them simply by changing the HOOKFUNC #define. WINAPI is an alias for __stdcall, which is a built-in option that tells the compiler how the function should behave at an assembly level. __stdcall functions receive parameters from the stack, handle stack-cleanup internally, use CPU registers EAX, EDX, and ECX for internal processing, without care for preserving their state, and return their values in the EAX register. For comparison, __fastcall functions receive one or more parameters in CPU registers to begin with, so the function doesn't have to take as much time reading values from the stack, and __thiscall functions receive a pointer to an object in the ECX register in addition to their declared parameters. Most of the time this stuff isn't important when you're working in C, C++, or any other high-level languages, but when you need to write part of a project in assembly, or you need your code to modify itself, this stuff becomes important.
How fleeting are all human passions compared with the massive continuity of ducks.