#include "stdafx.h"
#include <algorithm>
#include "ex.h"
extern std::wstring theRootPath;
unsigned long findDeepestFrame(const unsigned long ebp, const unsigned long esp)
{
// ebp is the top of the searchable area
// esp is the bottom of the searchable area
if (!ebp || !esp || esp > ebp)
{
RaiseException(EXCEPTION_BREAKPOINT, 0, 0, 0);
}
unsigned long deepest = ebp;
std::list<unsigned long> potential_frames;
potential_frames.push_back(ebp);
unsigned long search = ebp - 4;
while (search >= esp)
{
unsigned long candidate = *reinterpret_cast<unsigned long*>(search);
std::list<unsigned long>::iterator iter = std::find(potential_frames.begin(),potential_frames.end(), candidate);
if (iter != potential_frames.end())
{
potential_frames.push_back(search);
deepest = search;
search -= 4;
} else
{
--search;
}
}
return deepest;
}
std::list<unsigned long> findFrames(const unsigned long ebp, const unsigned long esp)
{
// ebp is the top of the searchable area
// esp is the bottom of the searchable area
if (!ebp || !esp || esp > ebp)
{
RaiseException(EXCEPTION_BREAKPOINT, 0, 0, 0);
}
std::list<unsigned long> frames;
frames.push_back(ebp);
unsigned long searchData = ebp - 4;
while (searchData >= esp)
{
unsigned long candidate = *reinterpret_cast<unsigned long*>(searchData);
std::list<unsigned long>::iterator iter = std::find(frames.begin(), frames.end(), candidate);
if (iter != frames.end())
{
frames.push_back(searchData);
searchData -= 4;
} else
{
--searchData;
}
}
return frames;
}
std::list<unsigned long> findCodeAddress(std::list<unsigned long> frames)
{
const unsigned char CALL = 0xE8;
const unsigned char JMP = 0xE9;
unsigned long callReturnAddress = 0;
unsigned char firstInstruction = 0;
long firstOffset = 0;
unsigned long secondAddress = 0;
unsigned char secondInstruction = 0;
long secondOffset = 0;
unsigned long codeAddress = 0;
unsigned long n = 0;
unsigned long data = 0;
HANDLE process = GetCurrentProcess();
std::list<unsigned long> code_frames;
std::list<unsigned long>::iterator iter;
for (iter = frames.begin(); iter != frames.end(); ++iter)
{
// Note ReadProcessMemory is more safe. In case address points to unaccessable memory, we won't crash on it
//callReturnAddress = *(unsigned long*)(*iter + 4);
if (ReadProcessMemory(process, (unsigned long*)(*iter + 4), &callReturnAddress, sizeof(unsigned long), &n) == FALSE) continue;
if (callReturnAddress < 0x00010000) continue; // 0x00400000 desktop Windows
if (callReturnAddress > 0x7FFFFFFF) continue;
//firstInstruction = *(unsigned char*)(callReturnAddress - 4 - 1); // returnAddress=4, instruction=1
//firstOffset = *( long*)(callReturnAddress - 4 - 0); // returnAddress=4
if (ReadProcessMemory(process, (unsigned char*)(callReturnAddress - 4 - 1), &firstInstruction, sizeof(unsigned char), &n) == FALSE) continue;
if (ReadProcessMemory(process, ( long*)(callReturnAddress - 4 - 0), &firstOffset, sizeof( long), &n) == FALSE) continue;
secondAddress = callReturnAddress + firstOffset;
if (firstInstruction == CALL)
{
secondInstruction = *(unsigned char*)(secondAddress + 0);
secondOffset = *( long*)(secondAddress + 1);
if (secondInstruction == JMP)
{
codeAddress = secondAddress + secondOffset + 1 + 4; // JMP to '4byte address'
} else // real CALL?
{
codeAddress = secondAddress;
}
} else if (firstInstruction == JMP) // No CALL, but JMP? (but what is callReturnAddress doing then on stack?)
{
// We come here at the first frame. It points to where program should resume after the catch()
codeAddress = secondAddress;
} else
{
codeAddress = -1;
}
if (codeAddress != -1)
{
code_frames.push_back(codeAddress);
}
}
return code_frames;
}
std::list<unsigned long> findCodeAddress(const unsigned long ebp, const unsigned long esp)
{
const unsigned char CALL = 0xE8;
const unsigned char JMP = 0xE9;
unsigned long callReturnAddress = 0;
unsigned char firstInstruction = 0;
long firstOffset = 0;
unsigned long secondAddress = 0;
unsigned char secondInstruction = 0;
long secondOffset = 0;
unsigned long codeAddress = 0;
std::list<MODULEENTRY32> modulesList = GetModulesList();
std::list<unsigned long> potential_code;
unsigned long search = ebp;
while (search >= esp)
{
--search;
callReturnAddress = *(unsigned long*)(search);
if (callReturnAddress < 0x00010000) continue; // 0x00400000 desktop Windows
if (callReturnAddress > 0x7FFFFFFF) continue;
if (IsValidCodeMemory(callReturnAddress - 4 - 1, modulesList) == false) continue;
if (IsValidCodeMemory(callReturnAddress - 4 - 0, modulesList) == false) continue;
firstInstruction = *(unsigned char*)(callReturnAddress - 4 - 1); // returnAddress=4, instruction=1
firstOffset = *( long*)(callReturnAddress - 4 - 0); // returnAddress=4
secondAddress = callReturnAddress + firstOffset;
if (firstInstruction == CALL)
{
secondInstruction = *(unsigned char*)(secondAddress + 0);
secondOffset = *( long*)(secondAddress + 1);
if (secondInstruction == JMP)
{
codeAddress = secondAddress + secondOffset + 1 + 4; // JMP to '4byte address'
} else // real CALL?
{
codeAddress = secondAddress;
}
} else if (firstInstruction == JMP) // No CALL, but JMP? (but what is callReturnAddress doing then on stack?)
{
// We come here at the first frame. It points to where program should resume after the catch()
codeAddress = secondAddress;
} else
{
codeAddress = -1;
}
if (codeAddress != -1)
{
potential_code.push_back(codeAddress);
}
}
return potential_code;
}
std::list<MODULEENTRY32> GetModulesList()
{
bool ok = false;
HANDLE snapShot = INVALID_HANDLE_VALUE;
std::list<MODULEENTRY32> listOfModules;
std::list<PROCESSENTRY32> listOfProcesses;
// Need to use __try __except on ToolHelp API.
// If a process is being destroyed (shutdown), the API crashes (AV on NULL pointer)
// Can use try catch if /EHa compiler settings is used
// __try
try
{
// Clear previous list
listOfModules.clear();
snapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snapShot != INVALID_HANDLE_VALUE)
{
PROCESSENTRY32 processEntry;
processEntry.dwSize = sizeof(PROCESSENTRY32);
BOOL ret = Process32First(snapShot, &processEntry);
while (ret == TRUE)
{
listOfProcesses.push_back(processEntry);
#if defined(UNDER_CE)
// By default the current exe is not in the MODULES list under CE,
// so we have to find the info elsewhere
if (processEntry.th32ProcessID == GetCurrentProcessId())
{
MODULEENTRY32 moduleEntry;
moduleEntry.dwSize = sizeof(MODULEENTRY32);
moduleEntry.modBaseAddr = (BYTE*)processEntry.th32MemoryBase;
moduleEntry.modBaseSize = 0x10000000 - (unsigned long)moduleEntry.modBaseAddr; // Not correct, but will work in Windows CE 6.0
moduleEntry.th32ModuleID = processEntry.th32ModuleID;
moduleEntry.th32ProcessID = processEntry.th32ProcessID;
moduleEntry.hModule = 0;
wcscpy_s(moduleEntry.szModule, MAX_PATH, processEntry.szExeFile);
listOfModules.push_back(moduleEntry);
}
#endif
ret = Process32Next(snapShot, &processEntry);
}
#if defined(UNDER_CE)
CloseToolhelp32Snapshot(snapShot);
#endif
}
snapShot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
if (snapShot != INVALID_HANDLE_VALUE)
{
// Build new list
MODULEENTRY32 moduleEntry;
moduleEntry.dwSize = sizeof(MODULEENTRY32);
BOOL ret = Module32First(snapShot, &moduleEntry);
while (ret == TRUE)
{
listOfModules.push_back(moduleEntry);
ret = Module32Next(snapShot, &moduleEntry);
}
#if defined(UNDER_CE)
CloseToolhelp32Snapshot(snapShot);
#endif
} else
{
DWORD err = GetLastError();
LPVOID lpMsgBuf;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
err,
0, // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL);
lpMsgBuf = lpMsgBuf;
}
ok = true;
// } __except ( Filter(GetExceptionCode(), GetExceptionInformation()) )
} catch (...)
{
ok = false;
if (snapShot != INVALID_HANDLE_VALUE)
{
#if defined(UNDER_CE)
CloseToolhelp32Snapshot(snapShot);
#endif
}
}
return listOfModules;
}
bool IsValidCodeMemory(unsigned long address, std::list<MODULEENTRY32> modulesList)
{
std::list<MODULEENTRY32>::iterator iter;
for (iter = modulesList.begin(); iter != modulesList.end(); iter++)
{
if (((unsigned long)iter->modBaseAddr <= address) && (address < ((unsigned long)iter->modBaseAddr + (unsigned long)iter->modBaseSize)))
{
return true;
}
}
return false;
}
std::map<unsigned long, std::wstring> LoadAsm(const wchar_t* filename)
{
std::map<unsigned long, std::wstring> table;
errno_t err;
FILE* file;
err = _wfopen_s(&file, filename, L"r");
if (err == 0)
{
wchar_t line[512] = L"";
// search start
while (fgetws(line, 512, file) != NULL)
{
if (wcsstr(line, L"Publics by Value") != 0)
{
break;
}
}
// start reading
wchar_t address[64];
wchar_t publicsByValue[512];
wchar_t libObject[128];
unsigned long rvaBase;
while (fgetws(line, 512, file) != NULL)
{
int n;
n = swscanf_s(line, L"%s%s%x%s\r\n", address, 64, publicsByValue, 512, &rvaBase, libObject, 128);
if (n == 4)
{
table[rvaBase] = std::wstring(publicsByValue);
}
}
fclose(file);
}
return table;
}
std::map<unsigned long, std::wstring> LoadPdb(const wchar_t* filename)
{
std::map<unsigned long, std::wstring> table;
errno_t err;
FILE* file;
err = _wfopen_s(&file, filename, L"r");
if (err == 0)
{
wchar_t line[512] = L"";
// search start
while (fgetws(line, 512, file) != NULL)
{
if (wcsstr(line, L"Publics by Value") != 0)
{
break;
}
}
// start reading
wchar_t address[64];
wchar_t publicsByValue[512];
wchar_t libObject[128];
unsigned long rvaBase;
while (fgetws(line, 512, file) != NULL)
{
int n;
n = swscanf_s(line, L"%s%s%x%s\r\n", address, 64, publicsByValue, 512, &rvaBase, libObject, 128);
if (n == 4)
{
table[rvaBase] = std::wstring(publicsByValue);
}
}
fclose(file);
}
return table;
}
unsigned long GetLoadAddress(const wchar_t* filename)
{
HANDLE process = GetCurrentProcess();
HMODULE module = GetModuleHandle(filename);
MODULEINFO moduleInfo;
BOOL ret = GetModuleInformation(process, module, &moduleInfo, sizeof(MODULEINFO));
if (ret == 0)
{
return 0;
} else
{
return (unsigned long)moduleInfo.lpBaseOfDll;
}
}
unsigned long GetLoadAddressEx(const wchar_t* filename, std::list<MODULEENTRY32> listOfModules)
{
std::list<MODULEENTRY32>::iterator iter;
for (iter = listOfModules.begin(); iter != listOfModules.end(); ++iter)
{
if (_wcsicmp(iter->szModule, filename) == 0)
{
break;
}
}
if (iter == listOfModules.end())
{
return 0;
} else
{
return (unsigned long)iter->modBaseAddr;
}
}
void Output(std::list<unsigned long> codeAddresses, std::list<MODULEENTRY32> modulesList)
{
std::wstring path = theRootPath;
path += std::wstring(L"\\Dump.txt");
FILE* file = NULL;
errno_t err = _wfopen_s(&file, path.c_str(), L"w");
fwprintf(file, L"Start modules dump\n");
std::list<MODULEENTRY32>::iterator iterModules;
for (iterModules = modulesList.begin(); iterModules != modulesList.end(); ++iterModules)
{
unsigned long modBaseStartAddr = (unsigned long)iterModules->modBaseAddr;
unsigned long modBaseEndAddr = modBaseStartAddr + (unsigned long)iterModules->modBaseSize;
const wchar_t* szModule = iterModules->szModule;
wprintf(L"0x%08X - 0x%08X %s\n", modBaseStartAddr, modBaseEndAddr, szModule);
fwprintf(file, L"0x%08X - 0x%08X %s\n", modBaseStartAddr, modBaseEndAddr, szModule);
}
fwprintf(file, L"End modules dump\n");
fwprintf(file, L"Start stack dump\n");
std::list<unsigned long>::iterator iterCodeAddresses;
for (iterCodeAddresses = codeAddresses.begin(); iterCodeAddresses != codeAddresses.end(); ++iterCodeAddresses)
{
wprintf(L"0x%08X\n", *iterCodeAddresses);
fwprintf(file, L"0x%08X\n", *iterCodeAddresses);
}
fwprintf(file, L"End stack dump\n");
fclose(file);
}
void AnalyzeCallStack(const BYTE* pFrame, const BYTE* pStack)
{
// Adjust pFrame to nearest upper 64K boundary (so we look further back in time)
BYTE* pStackBase = (BYTE*)(((unsigned long)pFrame & 0xFFFF0000) + 0x0000FFFF);
// Method 1 using frames
std::list<unsigned long> frames = findFrames((unsigned long)pFrame, (unsigned long)pStack);
std::list<unsigned long> codeAddresses = findCodeAddress(frames);
std::list<MODULEENTRY32> modulesList = GetModulesList();
Output(codeAddresses, modulesList);
// Method 2 walking complete stack
// std::list<unsigned long> codeAddresses2 = findCodeAddress((unsigned long)pFrame, (unsigned long)pStackBase);
// Output(codeAddresses2, modulesList);
unsigned long a1 = GetLoadAddress(L"kernel32.dll");
unsigned long a2 = GetLoadAddressEx(L"kernel32.dll", modulesList);
unsigned long a3 = GetLoadAddressEx(L"ntdll.dll", modulesList);
}