Solution 1: Loading symbol information through
SymInitialize.
On PC we load symbol information using
the DbgHelp library. In our first solution we use the
SymInitialize
function from the DbgHelp library:
BOOL
SymInitialize(
HANDLE
hProcess,
PCTSTR
UserSearchPath,
BOOL
fInvadeProcess
);
The first parameter is a handle to a
process. If you pass a valid handle to a process, SymInitialize
will enumerate all of the loaded modules for that process and search
for the symbol files. It will search for the symbol files in their
default locations. Additional searching can be being performed using
UserSearchPath,
the second parameter.
There are some pros and cons to this
approach:
Pros:
Cons:
-
The tool can only use this if the
game is running locally – i.e. not over the network.
-
The tool can only use this as long
as the game is running. If we decide to save our ‘project’ for
offline analysis, the process is no longer alive.
If the drawbacks prove not be a problem
for you, possibly when your tool serves as a debugger for a running
process, I would suggest using this method to load symbol
information. Listing 2 displays a sample program that demonstrates
how this works.
Let’s go over this code. Our
application is a very simple win32 console application that outputs
the current running process Id and it outputs an address in the range
of the function Foo.
Because you cannot read the instruction pointer (EIP) directly, I
used a trick from Dan Moulding to obtain the instruction
pointer by using the return address. You can read his article at 6.
Another way to do this is to use the ReturnAddress
intrinsic, which is Microsoft specific, but will also work at
architectures other than x86.
After printing, the program goes into
sleep state to avoid the process from exiting.
The program
output shows the current process Id and the function address of Foo:
process
id: 0xe9c
Function
address: 0x411553
Listing 3 shows the program that is
going to use the process Id and the function address.
|
|
#include "stdafx.h"
#include
#include
#include
#include
#include
#include
using namespace std;
string FindFunction(DWORD64 address, HANDLE process)
{
string result = "Unknown";
ULONG64 buffer[
(sizeof(SYMBOL_INFO) +
MAX_SYM_NAME * sizeof(char) +
sizeof(ULONG64) - 1) /
sizeof(ULONG64)];
PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer;
DWORD64 displ = 0;
pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
pSymbol->MaxNameLen = MAX_SYM_NAME;
if(::SymFromAddr(process, address, &displ, pSymbol))
{
result = pSymbol->Name;
}
else
{
DWORD error = ::GetLastError();
ostringstream str;
str << "SymFromAddr returned error " << error;
throw exception(str.str().c_str());
}
return result;
}
void Init(HANDLE process)
{
::SymSetOptions(
SYMOPT_UNDNAME |
SYMOPT_DEFERRED_LOADS |
SYMOPT_LOAD_LINES);
if(!::SymInitialize(process, NULL, TRUE))
{
throw exception(
"Could not initialize symbol loader!");
}
}
void Exit(HANDLE process)
{
::SymCleanup(process);
}
int main(int argc, char* argv[])
{
HANDLE processHandle = NULL;
try
{
if (argc != 3)
{
throw invalid_argument(
"Usage: FileLineInfo ");
}
DWORD processId;
istringstream iss1(argv[1], ios_base::in);
iss1 >> hex >> processId;
processHandle = ::OpenProcess(
PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,
FALSE, processId);
if(processHandle == NULL)
{
throw invalid_argument(
"Process ID does not refer to a running process!");
}
char fileName[MAX_PATH];
::GetModuleFileNameExA(
processHandle,
NULL,
fileName,
sizeof(fileName));
cout << "Process refers to: " << fileName << endl;
Init(processHandle);
DWORD64 address;
istringstream iss2(argv[2], ios_base::in);
iss2 >> hex >> address;
string name = FindFunction(address, processHandle);
cout << "Function name: " << name << endl;
}
catch(exception& ex)
{
cerr << "Error: " << ex.what( ) << endl;
}
if(processHandle != NULL)
{
Exit(processHandle);
::CloseHandle(processHandle);
}
return 0;
}
|
 |
 |
 |
Listing 3: The
FindFunctionInProcess program.
|
|