Ok, first sign of any real progress has finally occurred.
Checked the Memory map whilst running Outpost 2 as an attached process. Whilst scanning through any modules used by Outpost 2, I found something relevant in .data
The data dump at 0054FC50 contains the beginning of the message history displayed in the communications tab.
I was quite happy to notice the following:
at 0054FCF0 fourth column was FE FF FF FF
at 0054FD00 FF FF FF FF | 3C 4E 3E 36 | 3A 20 3A 20 | 36 36 36 00
This translated into <N>6: : 666
Which is my message sent at mark 6.
Now, a message history is probably a very roundabout way of finding strings players enter.
But, this is the best I could find for now, since I am having serious trouble using OllyDbg to find any run time added/modified value.
EDIT: Relating to the message history dump, it seems to come up on a filled space at 00550FF0. So that's probably the end of said message history.
More EDIT:
I seem to have found the dump address for the last message entered.
005756DA.
Another EDIT:
This one came up after I decided to try and check for instructions relating cheat code verification again. At address 00586430 I notice an ESI := [param1] char * chatText instruction.
So far so good, it appears that OP2 uses a chatText char * type variable to handle text entered in that bottom text box.
I'm afraid this is about as far as I can go, since I see nothing I can do to follow a variable.
And another EDIT:
Now I found my way into the names of variables and functions mostly. I actually forgot how I got here, since I'm too tired. Apart from finding a reference to a Player.GetPlayerName() function (which would be a nice addition to the SDK), I located the following:
Names in Outpost2, item 2261
Address=0040FD7C
Section=.text
Type=User
Name=Player.ProcessCommandPacket(CommandPacket *commandPacket):[ctChat]
Comment=[ctChat]: EBX := &cp.chatText
By the looks of its location, I have a feeling this will not help me in any way. Can't make much of it either.
When the code pane is active, hit Ctrl+<N>, and you'll see a list of names from that module. You can sort the list by the different columns by clicking on them, such as by address, or by (label) name. You can also start typing into the list and it will search for strings with the prefix you're typing.
Find "Player.ProcessCommandPacket", and keep looking through the list until you find the sublabel you want. I formatted the labels as <functionName>:<subLabel>, where subLabel is the particular case of the switch block.
As for code hooks, the easiest case is to find a call instruction you can redirect. Replace the call destination with the address of one of your own functions, and then inside your function, call the original function. Any pre-processing is done before the chained call, and any post-processing will be done after it.
Here's the idea with some untested code. Make sure to replace values in angle brackets with actual values specific to your case. (None of them are meant to be template parameters).
; Code sequence to modify
; ... mov/push/etc.
CALL interestingFunction; Encoded such as E8 A0661700, where E8 represents the CALL, and the remaining 4 bytes are the offset
; <returnAddress> ... more code
const void* returnAddress = (void*)<returnAddress>;
const int* callRelativeAddrPtr = (int*)((int)returnAddress - 4); // Look back 4 bytes for the offset part of the call instruction
void* originalFunctionPtr = 0;
// Perform a code overwrite to install our hook
// Note: Might call this from a DLL load event (process attach)
void InstallHook()
{
// Unprotect section of code at <returnAddress>
DWORD oldAttributes;
if (VirtualProtect(callRelativeAddrPtr, 4, PAGE_EXECUTE_READWRITE, &oldAttributes) == 0)
return; // Could not unprotect pages. Abort
// Store the old destination of the call instruction
// Note: The call operand is an offset relative to the start of the next instruction
originalFunctionPtr = (void*)(*callRelativeAddrPtr + returnAddress);
// Replace the address of the call instruction with a pointer to our function
// Note: The call operand is an offset relative to the start of the next instruction
*callRelativeAddrPtr = (int)(&HookFunction - returnAddress);
}
// Hook function that wraps around previous call
// Note: Make sure the parameter list and return type matches the function being replaced! Very important or your code will crash when it returns (or tries to access parameters).
<ReturnType> Hook(<paramList>)
{
// Do any pre-processing here
// ...
// Chain to the original function (if desired)
<ReturnType> retVal = *originalFunctionPtr(<paramList>);
// Do any post-processing here
// ...
// Return the value from the chained function (or a modified value)
return retVal;
}
// Note: Don't forget the UninstallHook function, which undoes the code modification
// Note: Might call this from a DLL Unload event.
void UninstallHook()
{
*callRelativeAddrPtr = (int)originalFunctionPtr - returnAddress;
}
Now I'll give you some specific hints for your case. Your hook function will probably have a prototype such as:
void HookFunction(char* chatText, int sourcePlayerNum);
You can quite safely drop the call to the original function if this is done the way I'm expecting (at the location I'm expecting).
I trust you're aware of what RETN or RET means? Think what happens if you write an overly complicated function such as the following:
void OverlyComplicatedFunction()
{
}
Yep, very important that you let a function like that complete it's work. :rolleyes:
My first attempt at adapting the code resulted in the following:
const void* returnAddress = (void*) 0x40FD85;
const int* callRelativeAddrPtr = (int*)((int)returnAddress - 4); // Look back 4 bytes for the offset part of the call instruction
void* originalFunctionPtr = 0;
// Hook function that wraps around previous call
// Note: Make sure the parameter list and return type matches the function being replaced! Very important or your code will crash when it returns (or tries to access parameters).
void Hook (char* chatText, int sourcePlayerNum)
{
if (chatText == "Test") TethysGame::AddMessage (1, 1, "Well", 0, sndSavant10);
}
// Perform a code overwrite to install our hook
// Note: Might call this from a DLL load event (process attach)
void InstallHook()
{
// Unprotect section of code at <returnAddress>
DWORD oldAttributes;
if (VirtualProtect(callRelativeAddrPtr, 4, PAGE_EXECUTE_READWRITE, &oldAttributes) == 0) //line 31, error
return; // Could not unprotect pages. Abort
// Store the old destination of the call instruction
// Note: The call operand is an offset relative to the start of the next instruction
originalFunctionPtr = (void*)(*callRelativeAddrPtr + returnAddress); //line 36, error
// Replace the address of the call instruction with a pointer to our function
// Note: The call operand is an offset relative to the start of the next instruction
*callRelativeAddrPtr = (int)(&Hook - returnAddress); //line 40, error
}
BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
if (fdwReason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hinstDLL);
InstallHook();
}
if (fdwReason == DLL_PROCESS_UNLOAD) //line 50, error. Ok, I don't know what it's called
{
*callRelativeAddrPtr = (int)originalFunctionPtr - returnAddress; //line 52, error
}
return TRUE;
}
And the following errors:
Main.cpp|31|error C2664: 'VirtualProtect' : cannot convert parameter 1 from 'const int *' to 'LPVOID'|
Main.cpp|36|error C2036: 'const void *' : unknown size|
Main.cpp|40|error C2296: '-' : illegal, left operand has type 'void (__cdecl *)(char *,int)'|
Main.cpp|50|error C2065: 'DLL_PROCESS_UNLOAD' : undeclared identifier|
Main.cpp|52|error C2113: '-' : pointer can only be subtracted from another pointer|
The address is the CALL for the function that used to check for cheat codes, since it was the only CALL I could find.
Ok, I used your points to correct most of the errors, but there's still a problem left.
*callRelativeAddrPtr = (int)((int)&Hook - returnAddress);
brings up the following error:
Main.cpp|40|error C2113: '-' : pointer can only be subtracted from another pointer|
My attempted solution is (int)returnAddress.
But that brings up the foollowing error:
Main.cpp|40|error C2166: l-value specifies const object|
Doh, bad location for "const".
I probably wrote this thinking of address constants:
const void* returnAddress = (void*) 0x40FD85;
const int* callRelativeAddrPtr = (int*)((int)returnAddress - 4); // Look back 4 bytes for the offset part of the call instruction
When really that means the pointed to values should be constant. Instead, what I should probably have written is:
void* const returnAddress = (void*) 0x40FD85;
int* const callRelativeAddrPtr = (int*)((int)returnAddress - 4); // Look back 4 bytes for the offset part of the call instruction
Or if you're desperate, drop the const all together.
So, C++ syntax for you, read the declarations backwards, (aside from arrays and function pointers which are kind of nested). You want a constant pointer to an int, rather than a pointer to a constant int. Btw, there are 3 ways to write const into a declaration. Can you guess which one means what?
const int* a;
int const * a;
int * const a;
It turns out, the first two have identical meaning, in which case the pointed to integer is the constant, but the pointer itself can change. In the second case, the pointer itself is constant, but the value it points to can change. You can also make both of them constant with either of:
const int * const a;
int const * const a;
Going to post a complete update of the code:
void* const returnAddress = (void*) 0x40FD85;
int* const callRelativeAddrPtr = (int*)((int)returnAddress - 4); // Look back 4 bytes for the offset part of the call instruction
void* originalFunctionPtr = 0;
// Hook function that wraps around previous call
// Note: Make sure the parameter list and return type matches the function being replaced! Very important or your code will crash when it returns (or tries to access parameters).
void Hook (char* chatText, int sourcePlayerNum)
{
if (chatText == "Test") TethysGame::AddMessage (1, 1, "Well", 0, sndSavant10);
}
// Perform a code overwrite to install our hook
// Note: Might call this from a DLL load event (process attach)
void InstallHook()
{
// Unprotect section of code at <returnAddress>
DWORD oldAttributes;
if (VirtualProtect(callRelativeAddrPtr, 4, PAGE_EXECUTE_READWRITE, &oldAttributes) == 0)
return; // Could not unprotect pages. Abort
// Store the old destination of the call instruction
// Note: The call operand is an offset relative to the start of the next instruction
originalFunctionPtr = (void*)(*callRelativeAddrPtr + returnAddress);
// Replace the address of the call instruction with a pointer to our function
// Note: The call operand is an offset relative to the start of the next instruction
*callRelativeAddrPtr = (int)((int)&Hook - returnAddress);
}
BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
if (fdwReason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hinstDLL);
InstallHook();
}
if (fdwReason == DLL_PROCESS_DETACH)
{
*callRelativeAddrPtr = (int)originalFunctionPtr - returnAddress;
}
return TRUE;
}
The following three lines return errors:
originalFunctionPtr = (void*)(*callRelativeAddrPtr + returnAddress);
*callRelativeAddrPtr = (int)((int)&Hook - returnAddress);
*callRelativeAddrPtr = (int)originalFunctionPtr - returnAddress;
First of those returns error C2036: 'void *const ' : unknown size
The next two return error C2113: '-' : pointer can only be subtracted from another pointer
Now, I can see why the last two return errors, but I am not sure what to do with them. Should I add sizeof(returnAddress)?
The first error, well... I'm not so sure :/
Anything I attempted to fix the errors leads to a game crash at that address.
EDIT: Hooman, think we can find each other on IRC sometime? I'm guessing it would speed the work up, provided we both have enough time. I'm asking you, since you're the most involved in this.
The next instruction address I found is 0x40FD8A. It seems to be the right one.
Have you tested it in the game, as well?
Assuming I made the changes the way you expected them to be, OP2 still crashes and OllyDbg doesn't know how to step into the Hooked function because address 00E84227 is unreadable.
My code currently looks like this:
int* const returnAddress = (int*) 0x40FD8A;
int* const callRelativeAddrPtr = (int*)((int)returnAddress - 4); // Look back 4 bytes for the offset part of the call instruction
void* originalFunctionPtr = 0;
// Hook function that wraps around previous call
// Note: Make sure the parameter list and return type matches the function being replaced! Very important or your code will crash when it returns (or tries to access parameters).
void Hook (char* chatText, int sourcePlayerNum)
{
if (strcmp(chatText, "Ping") != false) TethysGame::AddMessage (1, 1, "Pong", 0, sndSavant10);
}
// Perform a code overwrite to install our hook
// Note: Might call this from a DLL load event (process attach)
void InstallHook()
{
// Unprotect section of code at <returnAddress>
DWORD oldAttributes;
if (VirtualProtect(callRelativeAddrPtr, 4, PAGE_EXECUTE_READWRITE, &oldAttributes) == 0)
return; // Could not unprotect pages. Abort
// Store the old destination of the call instruction
// Note: The call operand is an offset relative to the start of the next instruction
originalFunctionPtr = (void*)(*callRelativeAddrPtr + returnAddress);
// Replace the address of the call instruction with a pointer to our function
// Note: The call operand is an offset relative to the start of the next instruction
*callRelativeAddrPtr = (int)((int*)&Hook - returnAddress);
}
BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
if (fdwReason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hinstDLL);
InstallHook();
}
if (fdwReason == DLL_PROCESS_DETACH)
{
*callRelativeAddrPtr = (int*)originalFunctionPtr - returnAddress;
}
return true;
}