2015-04-02 22:29:41 +08:00
|
|
|
// ntinspect.cc
|
|
|
|
// 4/20/2014 jichi
|
|
|
|
#include "ntdll/ntdll.h"
|
|
|
|
#include "ntinspect/ntinspect.h"
|
|
|
|
|
2016-01-05 23:01:17 +08:00
|
|
|
// https://social.msdn.microsoft.com/Forums/vstudio/en-US/4cb11cd3-8ce0-49d7-9dda-d62e9ae0180b/how-to-get-current-module-handle?forum=vcgeneral
|
|
|
|
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
|
|
|
|
|
2015-04-02 22:29:41 +08:00
|
|
|
//#ifdef _MSC_VER
|
|
|
|
//# pragma warning(disable:4018) // C4018: signed/unsigned mismatch
|
|
|
|
//#endif // _MSC_VER
|
|
|
|
|
|
|
|
namespace { // unnamed
|
2016-01-05 23:01:17 +08:00
|
|
|
|
2015-04-02 22:29:41 +08:00
|
|
|
// Replacement of wcscpy_s which is not available on Windows XP's msvcrt
|
|
|
|
// http://sakuradite.com/topic/247
|
|
|
|
errno_t wcscpy_safe(wchar_t *buffer, size_t bufferSize, const wchar_t *source)
|
|
|
|
{
|
|
|
|
size_t len = min(bufferSize - 1, wcslen(source));
|
|
|
|
buffer[len] = 0;
|
|
|
|
if (len)
|
|
|
|
memcpy(buffer, source, len * 2);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} // unnamed namespace
|
|
|
|
|
|
|
|
NTINSPECT_BEGIN_NAMESPACE
|
|
|
|
|
2016-01-05 23:01:17 +08:00
|
|
|
// https://social.msdn.microsoft.com/Forums/vstudio/en-US/4cb11cd3-8ce0-49d7-9dda-d62e9ae0180b/how-to-get-current-module-handle?forum=vcgeneral
|
|
|
|
HMODULE getCurrentModuleHandle() { return (HMODULE)&__ImageBase; }
|
|
|
|
|
|
|
|
/** Memory range */
|
|
|
|
|
|
|
|
BOOL getProcessName(LPWSTR buffer, int bufferSize)
|
2015-04-02 22:29:41 +08:00
|
|
|
{
|
|
|
|
//assert(name);
|
|
|
|
PLDR_DATA_TABLE_ENTRY it;
|
|
|
|
__asm
|
|
|
|
{
|
|
|
|
mov eax,fs:[0x30]
|
|
|
|
mov eax,[eax+0xc]
|
|
|
|
mov eax,[eax+0xc]
|
|
|
|
mov it,eax
|
|
|
|
}
|
|
|
|
// jichi 6/4/2014: _s functions are not supported on Windows XP's msvcrt.dll
|
|
|
|
//return 0 == wcscpy_s(buffer, bufferSize, it->BaseDllName.Buffer);
|
|
|
|
return 0 == wcscpy_safe(buffer, bufferSize, it->BaseDllName.Buffer);
|
|
|
|
}
|
|
|
|
|
2016-01-05 23:01:17 +08:00
|
|
|
// See: ITH FillRange
|
2015-04-02 22:29:41 +08:00
|
|
|
BOOL getModuleMemoryRange(LPCWSTR moduleName, DWORD *lowerBound, DWORD *upperBound)
|
|
|
|
{
|
|
|
|
//assert(lower);
|
|
|
|
//assert(upper);
|
|
|
|
PLDR_DATA_TABLE_ENTRY it;
|
|
|
|
LIST_ENTRY *begin;
|
|
|
|
__asm
|
|
|
|
{
|
|
|
|
mov eax,fs:[0x30]
|
|
|
|
mov eax,[eax+0xc]
|
|
|
|
mov eax,[eax+0xc]
|
|
|
|
mov it,eax
|
|
|
|
mov begin,eax
|
|
|
|
}
|
|
|
|
|
|
|
|
while (it->SizeOfImage) {
|
|
|
|
if (_wcsicmp(it->BaseDllName.Buffer, moduleName) == 0) {
|
|
|
|
DWORD lower = (DWORD)it->DllBase;
|
|
|
|
if (lowerBound)
|
|
|
|
*lowerBound = lower;
|
|
|
|
|
|
|
|
if (upperBound) {
|
|
|
|
DWORD upper = lower;
|
|
|
|
MEMORY_BASIC_INFORMATION mbi = {};
|
|
|
|
DWORD size = 0;
|
|
|
|
do {
|
|
|
|
DWORD len;
|
|
|
|
// Nt function is needed instead of VirtualQuery, which only works for the current process
|
|
|
|
::NtQueryVirtualMemory(NtCurrentProcess(), (LPVOID)upper, MemoryBasicInformation, &mbi, sizeof(mbi), &len);
|
|
|
|
if (mbi.Protect & PAGE_NOACCESS) {
|
|
|
|
it->SizeOfImage = size;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
size += mbi.RegionSize;
|
|
|
|
upper += mbi.RegionSize;
|
|
|
|
} while (size < it->SizeOfImage);
|
|
|
|
|
|
|
|
*upperBound = upper;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
it = (PLDR_DATA_TABLE_ENTRY)it->InLoadOrderModuleList.Flink;
|
|
|
|
if (it->InLoadOrderModuleList.Flink == begin)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2016-01-05 23:01:17 +08:00
|
|
|
BOOL getProcessMemoryRange(DWORD *lowerBound, DWORD *upperBound)
|
2015-04-02 22:29:41 +08:00
|
|
|
{
|
|
|
|
WCHAR procName[MAX_PATH]; // cached
|
|
|
|
*lowerBound = 0;
|
|
|
|
*upperBound = 0;
|
2016-01-05 23:01:17 +08:00
|
|
|
return getProcessName(procName, MAX_PATH)
|
2015-04-02 22:29:41 +08:00
|
|
|
&& getModuleMemoryRange(procName, lowerBound, upperBound);
|
|
|
|
}
|
|
|
|
|
2016-01-05 23:01:17 +08:00
|
|
|
/** Module header */
|
|
|
|
|
|
|
|
// See: ITH AddAllModules
|
|
|
|
bool iterModule(const iter_module_fun_t &fun)
|
|
|
|
{
|
|
|
|
// Iterate loaded modules
|
|
|
|
PPEB ppeb;
|
|
|
|
__asm {
|
|
|
|
mov eax, fs:[0x30]
|
|
|
|
mov ppeb, eax
|
|
|
|
}
|
|
|
|
const DWORD start = *(DWORD *)&ppeb->Ldr->InLoadOrderModuleList;
|
|
|
|
for (auto it = (PLDR_DATA_TABLE_ENTRY)start;
|
|
|
|
it->SizeOfImage && *(DWORD *)it != start;
|
|
|
|
it = (PLDR_DATA_TABLE_ENTRY)it->InLoadOrderModuleList.Flink)
|
|
|
|
if (!fun((HMODULE)it->DllBase, it->BaseDllName.Buffer))
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// See: ITH AddAllModules
|
|
|
|
DWORD getExportFunction(LPCSTR funcName)
|
|
|
|
{
|
|
|
|
// Iterate loaded modules
|
|
|
|
PPEB ppeb;
|
|
|
|
__asm {
|
|
|
|
mov eax, fs:[0x30]
|
|
|
|
mov ppeb, eax
|
|
|
|
}
|
|
|
|
const DWORD start = *(DWORD *)&ppeb->Ldr->InLoadOrderModuleList;
|
|
|
|
for (auto it = (PLDR_DATA_TABLE_ENTRY)start;
|
|
|
|
it->SizeOfImage && *(DWORD *)it != start;
|
|
|
|
it = (PLDR_DATA_TABLE_ENTRY)it->InLoadOrderModuleList.Flink) {
|
|
|
|
//if (moduleName && ::wcscmp(moduleName, it->BaseDllName.Buffer)) // BaseDllName.Buffer == moduleName
|
|
|
|
// continue;
|
|
|
|
if (DWORD addr = getModuleExportFunction((HMODULE)it->DllBase, funcName))
|
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// See: ITH AddModule
|
|
|
|
DWORD getModuleExportFunction(HMODULE hModule, LPCSTR funcName)
|
|
|
|
{
|
|
|
|
if (!hModule)
|
|
|
|
return 0;
|
|
|
|
DWORD startAddress = (DWORD)hModule;
|
|
|
|
IMAGE_DOS_HEADER *DosHdr = (IMAGE_DOS_HEADER *)hModule;
|
|
|
|
if (IMAGE_DOS_SIGNATURE == DosHdr->e_magic) {
|
|
|
|
DWORD dwReadAddr = startAddress + DosHdr->e_lfanew;
|
|
|
|
IMAGE_NT_HEADERS *NtHdr = (IMAGE_NT_HEADERS *)dwReadAddr;
|
|
|
|
if (IMAGE_NT_SIGNATURE == NtHdr->Signature) {
|
|
|
|
DWORD dwExportAddr = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
|
|
|
|
if (dwExportAddr == 0)
|
|
|
|
return 0;
|
|
|
|
dwExportAddr += startAddress;
|
|
|
|
IMAGE_EXPORT_DIRECTORY *ExtDir = (IMAGE_EXPORT_DIRECTORY *)dwExportAddr;
|
|
|
|
dwExportAddr = startAddress + ExtDir->AddressOfNames;
|
|
|
|
for (UINT uj = 0; uj < ExtDir->NumberOfNames; uj++) {
|
|
|
|
DWORD dwFuncName = *(DWORD *)dwExportAddr;
|
|
|
|
LPCSTR pcFuncName = (LPCSTR)(startAddress + dwFuncName);
|
|
|
|
if (::strcmp(funcName, pcFuncName) == 0) {
|
|
|
|
char *pcFuncPtr = (char *)(startAddress + (DWORD)ExtDir->AddressOfNameOrdinals+(uj * sizeof(WORD)));
|
|
|
|
WORD word = *(WORD *)pcFuncPtr;
|
|
|
|
pcFuncPtr = (char *)(startAddress + (DWORD)ExtDir->AddressOfFunctions+(word * sizeof(DWORD)));
|
|
|
|
return startAddress + *(DWORD *)pcFuncPtr; // absolute address
|
|
|
|
}
|
|
|
|
dwExportAddr += sizeof(DWORD);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// See: ITH FindImportEntry
|
|
|
|
DWORD getModuleImportAddress(HMODULE hModule, DWORD exportAddress)
|
|
|
|
{
|
|
|
|
if (!hModule)
|
|
|
|
return 0;
|
|
|
|
DWORD startAddress = (DWORD)hModule;
|
|
|
|
IMAGE_DOS_HEADER *DosHdr = (IMAGE_DOS_HEADER *)hModule;
|
|
|
|
if (IMAGE_DOS_SIGNATURE == DosHdr->e_magic) {
|
|
|
|
IMAGE_NT_HEADERS *NtHdr = (IMAGE_NT_HEADERS *)(startAddress + DosHdr->e_lfanew);
|
|
|
|
if (IMAGE_NT_SIGNATURE == NtHdr->Signature) {
|
|
|
|
DWORD IAT = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress;
|
|
|
|
DWORD end = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size;
|
|
|
|
IAT += startAddress;
|
|
|
|
end += IAT;
|
|
|
|
for (DWORD pt = IAT; pt < end; pt += 4) {
|
|
|
|
DWORD addr = *(DWORD *)pt;
|
|
|
|
if (addr == (DWORD)exportAddress)
|
|
|
|
return pt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-04-02 22:29:41 +08:00
|
|
|
NTINSPECT_END_NAMESPACE
|
|
|
|
|
|
|
|
// EOF
|