home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World 1999 October
/
PCWorld_1999-10_cd1.bin
/
Hardware
/
Drivers
/
APISpy
/
APISpy32.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1999-07-25
|
19KB
|
882 lines
// ----------------------------------- //
// APISpy32 v2.0 //
// Copyright 1999 Yariv Kaplan //
// WWW.INTERNALS.COM //
// ----------------------------------- //
#include <windows.h>
#include "APISpy32.h"
#include "LinkList.h"
#include "LogAPI.h"
#pragma check_stack(off)
#ifdef WIN95
#pragma comment(linker, "/section:.data,RWS /section:.idata,RWS /section:.bss,RWS")
#pragma comment(linker, "/base:0xBFF70000")
#include "Ring0.h"
#endif
PSTR TrimString(PSTR pszStr);
char *GetParamFormat(tagParamType ParamType);
char *GetParamName(tagParamType ParamType);
tagParamType GetParamType(PSTR pszParameter);
DWORD GetParamMask(tagParamType ParamType);
void FormatString(PSTR pszSrcStr, PSTR pszDstStr, DWORD dwMaxSize);
tagParamSpec ParamSpec[] = {{"INT", PARAM_INT, "%d", 0xFFFFFFFF},
{"DWORD", PARAM_DWORD, "%u", 0xFFFFFFFF},
{"WORD", PARAM_WORD, "%u", 0x0000FFFF},
{"BYTE", PARAM_BYTE, "%u", 0x000000FF},
{"PSTR", PARAM_PSTR, "%#x", 0xFFFFFFFF},
{"PVOID", PARAM_PVOID, "%#x", 0xFFFFFFFF},
{"PINT", PARAM_PINT, "%#x", 0xFFFFFFFF},
{"PDWORD", PARAM_PDWORD, "%#x", 0xFFFFFFFF},
{"PWORD", PARAM_PWORD, "%#x", 0xFFFFFFFF},
{"PBYTE", PARAM_PBYTE, "%#x", 0xFFFFFFFF},
{"HANDLE", PARAM_HANDLE, "%#x", 0xFFFFFFFF},
{"HWND", PARAM_HWND, "%#x", 0xFFFFFFFF},
{"BOOL", PARAM_BOOL, "%u", 0xFFFFFFFF},
{"PWSTR", PARAM_PWSTR, "%#x", 0xFFFFFFFF},
{"UNKNOWN",PARAM_UNKNOWN,"%u", 0xFFFFFFFF}};
#ifdef WIN95
__declspec(naked) void Ring0ModifyPageProtection()
{
_asm
{
Mov EAX, ECX
Shr EAX, 22
Test DWORD PTR [0FFBFE000h + EAX * 4], 1
Jz Fail
Mov EAX, ECX
Shr EAX, 12
Mov EBX, EAX
Mov EAX, DWORD PTR [0FF800000h + EAX * 4]
Test EAX, 1
Jz Fail
Mov EAX, 1
Cmp EDX, PAGE_READWRITE
Je PageReadWrite
And DWORD PTR [0FF800000h + EBX * 4], 0xFFFFFFFD
Jmp Done
PageReadWrite:
Or DWORD PTR [0FF800000h + EBX * 4], 2
Jmp Done
Fail:
Xor EAX, EAX
Done:
Retf
}
}
bool CallRing0(PVOID pvRing0FuncAddr, PVOID pvAddr, DWORD dwPageProtection)
{
GDT_DESCRIPTOR *pGDTDescriptor;
GDTR gdtr;
bool Result;
_asm Sgdt [gdtr]
// Skip the null descriptor
pGDTDescriptor = (GDT_DESCRIPTOR *)(gdtr.dwGDTBase + 8);
// Search for a free GDT descriptor
for (WORD wGDTIndex = 1; wGDTIndex < (gdtr.wGDTLimit / 8); wGDTIndex++)
{
if (pGDTDescriptor->Type == 0 &&
pGDTDescriptor->System == 0 &&
pGDTDescriptor->DPL == 0 &&
pGDTDescriptor->Present == 0)
{
// Found one !
// Now we need to transform this descriptor into a callgate.
// Note that we're using selector 0x28 since it corresponds
// to a ring 0 segment which spans the entire linear address
// space of the processor (0-4GB).
CALLGATE_DESCRIPTOR *pCallgate;
pCallgate = (CALLGATE_DESCRIPTOR *) pGDTDescriptor;
pCallgate->Offset_0_15 = LOWORD(pvRing0FuncAddr);
pCallgate->Selector = 0x28;
pCallgate->ParamCount = 0;
pCallgate->Unused = 0;
pCallgate->Type = 0xc;
pCallgate->System = 0;
pCallgate->DPL = 3;
pCallgate->Present = 1;
pCallgate->Offset_16_31 = HIWORD(pvRing0FuncAddr);
// Prepare the far call parameters
WORD CallgateAddr[3];
CallgateAddr[0] = 0x0;
CallgateAddr[1] = 0x0;
CallgateAddr[2] = (wGDTIndex << 3) | 3;
// Please fasten your seat belts!
// We're about to make a hyperspace jump into RING 0.
_asm
{
Mov ECX, [pvAddr]
Mov EDX, [dwPageProtection]
Cli
Call FWORD PTR [CallgateAddr]
Sti
Mov DWORD PTR [Result], EAX
}
// Now free the GDT descriptor
memset(pGDTDescriptor, 0, 8);
return Result;
}
// Advance to the next GDT descriptor
pGDTDescriptor++;
}
// Whoops, the GDT is full
return false;
}
bool RemovePageProtection(PVOID pvAddr)
{
return CallRing0((PVOID)Ring0ModifyPageProtection, pvAddr, PAGE_READWRITE);
}
bool SetPageProtection(PVOID pvAddr)
{
return CallRing0((PVOID)Ring0ModifyPageProtection, pvAddr, PAGE_READONLY);
}
#endif
void APILogFunction(tagAPIInfo *pAPIInfo, PSTR pszLogString, ...)
{
va_list Marker;
DWORD dwParamValue;
char szParamStr[MAX_TEXT_LEN + 6];
char szUnicodeStr[MAX_TEXT_LEN + 1];
char cIndex;
va_start(Marker, pszLogString);
strcpy(pszLogString, pAPIInfo->szAPIName);
strcat(pszLogString, "(");
for (cIndex = 0; cIndex < pAPIInfo->ParamCount; cIndex++)
{
dwParamValue = va_arg(Marker, DWORD);
dwParamValue &= GetParamMask(pAPIInfo->ParamList[cIndex]);
strcat(pszLogString, GetParamName(pAPIInfo->ParamList[cIndex]));
strcat(pszLogString, ":");
wsprintf(szParamStr, GetParamFormat(pAPIInfo->ParamList[cIndex]), dwParamValue);
strcat(pszLogString, szParamStr);
switch (pAPIInfo->ParamList[cIndex])
{
case PARAM_PSTR:
strcat(pszLogString, ":");
FormatString((PSTR)dwParamValue, szParamStr, MAX_TEXT_LEN);
strcat(pszLogString, szParamStr);
break;
case PARAM_PINT:
strcat(pszLogString, ":");
if (IsBadReadPtr((PVOID)dwParamValue, 4) == 0)
{
wsprintf(szParamStr, "%d", *(int *)dwParamValue);
strcat(pszLogString, szParamStr);
}
else strcat(pszLogString, "?");
break;
case PARAM_PDWORD:
strcat(pszLogString, ":");
if (IsBadReadPtr((PVOID)dwParamValue, 4) == 0)
{
wsprintf(szParamStr, "%u", *(DWORD *)dwParamValue);
strcat(pszLogString, szParamStr);
}
else strcat(pszLogString, "?");
break;
case PARAM_PWORD:
strcat(pszLogString, ":");
if (IsBadReadPtr((PVOID)dwParamValue, 2) == 0)
{
wsprintf(szParamStr, "%u", LOWORD(*(WORD *)dwParamValue));
strcat(pszLogString, szParamStr);
}
else strcat(pszLogString, "?");
break;
case PARAM_PBYTE:
strcat(pszLogString, ":");
if (IsBadReadPtr((PVOID)dwParamValue, 1) == 0)
{
wsprintf(szParamStr, "%u", LOBYTE(*(BYTE *)dwParamValue));
strcat(pszLogString, szParamStr);
}
else strcat(pszLogString, "?");
break;
case PARAM_PWSTR:
strcat(pszLogString, ":");
if (IsBadReadPtr((PVOID)dwParamValue, 2) == 0 &&
IsBadStringPtrW((LPCWSTR)dwParamValue, MAX_TEXT_LEN) == 0)
{
WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)dwParamValue, MAX_TEXT_LEN, szUnicodeStr, sizeof(szUnicodeStr), NULL, NULL);
szUnicodeStr[MAX_TEXT_LEN] = '\0';
FormatString(szUnicodeStr, szParamStr, MAX_TEXT_LEN);
strcat(pszLogString, szParamStr);
}
else strcat(pszLogString, "?");
break;
}
strcat(pszLogString, ", ");
}
if (pAPIInfo->ParamCount > 0)
pszLogString[strlen(pszLogString) - 2] = ')';
else
strcat(pszLogString, ")");
va_end(Marker);
}
bool ParseAPIFile(PSTR pszFileName)
{
HANDLE hFile;
HANDLE hMap;
DWORD dwIndex;
PSTR pszParameterList;
PSTR pszParameter;
PSTR pszModuleName;
PSTR pszAPIName;
PCHAR pcFile;
DWORD dwFileSize;
char cParamIndex;
tagAPIInfo *pAPIInfo;
char szAPIDefinition[1024];
hFile = CreateFile(pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (hFile == INVALID_HANDLE_VALUE)
return false;
dwFileSize = GetFileSize(hFile, NULL);
if (dwFileSize == 0xFFFFFFFF)
return false;
hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
if (hMap == NULL)
{
CloseHandle(hFile);
return false;
}
pcFile = (PCHAR)MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
if (pcFile == NULL)
{
CloseHandle(hMap);
CloseHandle(hFile);
return false;
}
while (dwFileSize)
{
for (dwIndex = 0; *pcFile != 13 && dwFileSize; dwIndex++)
{
szAPIDefinition[dwIndex] = *pcFile;
pcFile++;
dwFileSize--;
}
szAPIDefinition[dwIndex] = '\0';
if (szAPIDefinition[0] != '\0')
{
pszModuleName = szAPIDefinition;
pszAPIName = strchr(pszModuleName, ':');
if (pszAPIName != NULL)
{
pszAPIName[0] = '\0';
pszAPIName++;
pszParameterList = strchr(pszAPIName, '(');
if (pszParameterList != NULL)
{
pszParameterList[0] = '\0';
pszParameterList++;
pAPIInfo = HookAPIFunction(pszModuleName, pszAPIName, APILogFunction);
if (pAPIInfo != NULL)
{
#ifdef WINNT
pAPIInfo->szAPIName = (PSTR)malloc(strlen(pszAPIName) + 1);
#endif
if (pAPIInfo->szAPIName == NULL)
{
UnmapViewOfFile(pcFile);
CloseHandle(hMap);
CloseHandle(hFile);
return false;
}
strcpy(pAPIInfo->szAPIName, pszAPIName);
#ifdef WIN95
pAPIInfo->hMutex = CreateMutex(NULL, FALSE, pszAPIName);
#endif
#ifdef WINNT
InitializeCriticalSection(&pAPIInfo->CriticalSection);
#endif
cParamIndex = 0;
pszParameter = strtok(pszParameterList, ",;)");
while (pszParameter != NULL)
{
pszParameter = TrimString(pszParameter);
pAPIInfo->ParamList[cParamIndex] = GetParamType(pszParameter);
cParamIndex++;
if (cParamIndex == MAX_PARAM)
break;
pszParameter = strtok(NULL, ",;)");
}
if (cParamIndex == MAX_PARAM)
UnhookAPIFunction(pAPIInfo);
else
pAPIInfo->ParamCount = cParamIndex;
}
}
}
}
if (dwFileSize == 0)
break;
pcFile += 2;
dwFileSize -= 2;
}
UnmapViewOfFile(pcFile);
CloseHandle(hMap);
CloseHandle(hFile);
return true;
}
void APIHandler()
{
PBYTE pbAPI;
PDWORD pdwAPI;
tagAPIInfo *pAPIInfo;
PBYTE pbAfterCall;
PDWORD pdwParam;
DWORD dwParamSize;
PDWORD pdwESP;
void *pvReturnAddr;
DWORD dwReturnValue;
char szLogString[2048];
#ifdef WIN95
HANDLE hMutex;
#endif
_asm
{
Mov EAX, [EBP + 4]
Mov [pbAfterCall], EAX
Mov EAX, [EBP + 8]
Mov [pvReturnAddr], EAX
Lea EAX, [EBP + 12]
Mov [pdwParam], EAX
}
pAPIInfo = Head;
while (pAPIInfo != NULL)
{
if ((pbAfterCall - 5) == (PBYTE)pAPIInfo->APIAddress)
{
#ifdef WIN95
hMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, pAPIInfo->szAPIName);
if (hMutex)
WaitForSingleObject(hMutex, INFINITE);
#endif
#ifdef WINNT
EnterCriticalSection(&pAPIInfo->CriticalSection);
#endif
memcpy(pAPIInfo->APIAddress, pAPIInfo->Opcodes, 5);
break;
}
pAPIInfo = pAPIInfo->Next;
}
dwParamSize = pAPIInfo->ParamCount * 4;
_asm
{
Sub ESP, [dwParamSize]
Mov [pdwESP], ESP
}
memcpy(pdwESP, pdwParam, dwParamSize);
pAPIInfo->APIEnterHandler(pAPIInfo, szLogString);
pAPIInfo->APIAddress();
_asm
{
Push EAX
Mov [dwReturnValue], EAX
}
AddLogEntry(GetCurrentProcessId(), szLogString, dwReturnValue, (PBYTE)pvReturnAddr - 5);
pbAPI = (PBYTE)pAPIInfo->APIAddress;
pbAPI[0] = 0xE8;
pdwAPI = (DWORD *)&pbAPI[1];
pdwAPI[0] = (DWORD)APIHandler - (DWORD)pbAPI - 5;
#ifdef WIN95
if (hMutex)
{
ReleaseMutex(hMutex);
CloseHandle(hMutex);
}
#endif
#ifdef WINNT
LeaveCriticalSection(&pAPIInfo->CriticalSection);
#endif
_asm
{
Pop EAX
Mov ECX, [dwParamSize]
Mov EDX, [pvReturnAddr]
Pop EDI
Pop ESI
Pop EBX
Mov ESP, EBP
Pop EBP
Add ESP, 8
Add ESP, ECX
Push EDX
Ret
}
}
bool RemoveProtection(PVOID pvAddress, tagAPIInfo *pAPIInfo)
{
#ifdef WIN95
bool Result;
Result = RemovePageProtection(pvAddress);
if (Result == false)
return false;
#endif
#ifdef WINNT
MEMORY_BASIC_INFORMATION mbi;
DWORD dwProtectionFlags;
DWORD dwScratch;
BOOL Result;
// Get page protection of API
VirtualQuery(pvAddress, &mbi, sizeof(mbi));
dwProtectionFlags = mbi.Protect;
pAPIInfo->dwOldProtectionFlags = dwProtectionFlags;
// Remove page protection from API
dwProtectionFlags &= ~PAGE_READONLY;
dwProtectionFlags &= ~PAGE_EXECUTE_READ;
dwProtectionFlags |= PAGE_READWRITE;
Result = VirtualProtect(pvAddress, 4096, dwProtectionFlags, &dwScratch);
if (Result == FALSE)
return false;
#endif
return true;
}
tagAPIInfo *HookAPIFunction(PSTR pszModuleName,
PSTR pszAPIName,
tagHandlerAddr APIEnterHandler)
{
HMODULE hModule;
tagAPIInfo *pAPIInfo;
tagAPIInfo *pTempAPIInfo;
PBYTE pbAPI;
PDWORD pdwAPI;
bool Result;
hModule = GetModuleHandle(pszModuleName);
if (hModule == NULL)
return NULL;
pbAPI = (PBYTE)GetProcAddress(hModule, pszAPIName);
if (pbAPI == NULL)
return NULL;
// Is it already hooked ?
pTempAPIInfo = Head;
while (pTempAPIInfo != NULL)
{
if (pTempAPIInfo->APIAddress == (tagAPIAddr)pbAPI)
return NULL;
pTempAPIInfo = pTempAPIInfo->Next;
}
// No, so add a new item
pAPIInfo = AddItem();
if (pAPIInfo == NULL)
return NULL;
pAPIInfo->APIAddress = NULL;
Result = RemoveProtection(pbAPI, pAPIInfo);
if (Result == false)
{
#ifdef WINNT
RemoveItem(pAPIInfo);
#endif
return NULL;
}
// Save first 5 bytes of API
memcpy(pAPIInfo->Opcodes, pbAPI, 5);
pAPIInfo->APIAddress = (tagAPIAddr)pbAPI;
pAPIInfo->APIEnterHandler = APIEnterHandler;
// Write a call to the hook function
pbAPI[0] = 0xE8;
pdwAPI = (DWORD *)&pbAPI[1];
pdwAPI[0] = (DWORD)APIHandler - (DWORD)pbAPI - 5;
return pAPIInfo;
}
void UnhookAPIFunction(tagAPIInfo *pAPIInfo)
{
bool Result;
Result = RemoveProtection(pAPIInfo->APIAddress, pAPIInfo);
if (Result == true)
memcpy(pAPIInfo->APIAddress, pAPIInfo->Opcodes, 5);
#ifdef WIN95
SetPageProtection(pAPIInfo->APIAddress);
if (pAPIInfo->hMutex)
CloseHandle(pAPIInfo->hMutex);
#endif
#ifdef WINNT
DWORD dwScratch;
DeleteCriticalSection(&pAPIInfo->CriticalSection);
VirtualProtect(pAPIInfo->APIAddress, 4096, pAPIInfo->dwOldProtectionFlags, &dwScratch);
free(pAPIInfo->szAPIName);
RemoveItem(pAPIInfo);
#endif
}
void UnhookAllAPIFunctions()
{
tagAPIInfo *pAPIInfo;
tagAPIInfo *pTempAPIInfo;
pAPIInfo = Head;
while (pAPIInfo != NULL)
{
pTempAPIInfo = pAPIInfo->Next;
UnhookAPIFunction(pAPIInfo);
pAPIInfo = pTempAPIInfo;
}
}
PSTR TrimString(PSTR pszStr)
{
PCHAR pcBlankChar;
pcBlankChar = pszStr + strlen(pszStr) - 1;
while (*pcBlankChar == ' ')
{
pcBlankChar[0] = '\0';
pcBlankChar--;
}
while (*pszStr == ' ') pszStr++;
return pszStr;
}
char *GetParamFormat(tagParamType ParamType)
{
char cIndex;
for (cIndex = 0; ParamSpec[cIndex].ParamType != PARAM_UNKNOWN; cIndex++)
if (ParamSpec[cIndex].ParamType == ParamType)
return ParamSpec[cIndex].ParamFormat;
return ParamSpec[cIndex].ParamFormat;
}
char *GetParamName(tagParamType ParamType)
{
char cIndex;
for (cIndex = 0; ParamSpec[cIndex].ParamType != PARAM_UNKNOWN; cIndex++)
if (ParamSpec[cIndex].ParamType == ParamType)
return ParamSpec[cIndex].ParamName;
return ParamSpec[cIndex].ParamName;
}
tagParamType GetParamType(PSTR pszParameter)
{
char cIndex;
strupr(pszParameter);
for (cIndex = 0; ParamSpec[cIndex].ParamType != PARAM_UNKNOWN; cIndex++)
if (strcmp(pszParameter, ParamSpec[cIndex].ParamName) == 0)
return ParamSpec[cIndex].ParamType;
return PARAM_UNKNOWN;
}
DWORD GetParamMask(tagParamType ParamType)
{
char cIndex;
for (cIndex = 0; ParamSpec[cIndex].ParamType != PARAM_UNKNOWN; cIndex++)
if (ParamSpec[cIndex].ParamType == ParamType)
return ParamSpec[cIndex].dwParamMask;
return ParamSpec[cIndex].dwParamMask;
}
void FormatString(PSTR pszSrcStr, PSTR pszDstStr, DWORD dwMaxSize)
{
DWORD dwDstSize = 0;
if (IsBadStringPtr(pszSrcStr, dwMaxSize) != 0)
{
*pszDstStr++ = '?';
*pszDstStr = '\0';
return;
}
*pszDstStr++ = '"';
while (*pszSrcStr != '\0')
{
if (*pszSrcStr == '\n')
{
*pszDstStr++ = '\\';
*pszDstStr = 'n';
}
else if (*pszSrcStr == '\t')
{
*pszDstStr++ = '\\';
*pszDstStr = 't';
}
else if (*pszSrcStr == '\r')
{
*pszDstStr++ = '\\';
*pszDstStr = 'r';
}
else *pszDstStr = *pszSrcStr;
dwDstSize++;
pszSrcStr++;
pszDstStr++;
if (dwDstSize == dwMaxSize)
{
*pszDstStr++ = '.';
*pszDstStr++ = '.';
*pszDstStr++ = '.';
break;
}
}
*pszDstStr++ = '"';
*pszDstStr = '\0';
}
BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD dwReason, PVOID pvReserved)
{
DWORD dwResult;
char szAPIFileName[MAX_PATH];
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
dwResult = GetWindowsDirectory(szAPIFileName, MAX_PATH);
if (dwResult == 0) return false;
strcat(szAPIFileName, "\\APISpy32.api");
DisableThreadLibraryCalls(hInstDLL);
ParseAPIFile(szAPIFileName);
break;
case DLL_PROCESS_DETACH:
UnhookAllAPIFunctions();
break;
}
return TRUE;
}