// ------------------------------------- //
// 您如果要使用本文件，请不要删除本说明  //
// ------------------------------------- //
//             HOOKAPI v1.6              //
//   Copyright 2002 编程沙龙 Paladin     //
//       www.ProgramSalon.com            //
// ------------------------------------- //
// 
//  HookAPI.cpp
//  通过取得函数地址，并在函数代码前插入call myfunc来实现调用myfunc的程序
//  Last Modified: 2003.05.07, by Paladin
//

#include <windows.h>
#include <time.h>
#include <stdio.h>
#include "ApiInfo.h"
#include "HookApi.h"
#include "util.h"

#ifdef WINNT
#include "injlib.h"
#endif

#ifdef WIN95
#include "ring0.h"
#endif

#pragma check_stack(off)

#ifdef WIN95

#pragma code_seg("_INIT")
#pragma comment(linker,"/SECTION:.bss,RWS /SECTION:.data,RWS /SECTION:.rdata,RWS /SECTION:.text,RWS /SECTION:_INIT,RWS ")
#pragma comment(linker,"/BASE:0xBFF70000")

#endif


#define CALL_BYTES_SIZE		5		// CALL 0xnnnnnnnn		size=5

CAPIInfo g_api_info;

char g_szDllPath[128];

static int g_demo_count =0;
#ifdef WINNT
extern BOOL ObtainSeDebugPrivilege();
#endif


////////////////////////
CHookAPI::CHookAPI()
{
	g_szDllPath[0] =0;

	//WriteLog("debug: hooked");
	m_hMyDll =NULL;
	HMODULE hMod =GetModuleHandle(HOOKAPI_DLL_NAME);
	if(hMod)
	{
		char fname[128];
		GetModuleFileName(hMod, fname, sizeof(fname));
		GetFilePath(fname, g_szDllPath);
		WriteProfileString("HookAPI", "DLL_PATH", g_szDllPath);
	}
	else GetProfileString("HookAPI", "DLL_PATH", "", g_szDllPath, sizeof(g_szDllPath));

	LoadMyDll();

	char mod_name[256], mod_name2[100];
	GetModuleFileName(NULL, mod_name, sizeof(mod_name));
	GetProfileString("HookAPI", "exe_name", "", mod_name2, sizeof(mod_name2));
	strupr(mod_name);
	strupr(mod_name2);

#ifdef WINNT
	if(mod_name2[0] ==0 || strstr(mod_name, mod_name2) ==NULL)
#endif
	{
		Init();
		HookAllAPI();
	}
}

CHookAPI::~CHookAPI()
{
	UnhookAllAPI();
	FreeMyDll();
}

int CHookAPI::LoadMyDll()
{
	if(GetModuleHandle(MY_DLL_NAME)) return 0;

	char fdll[128];
	wsprintf(fdll, "%s\\%s", g_szDllPath, MY_DLL_NAME);
	if((m_hMyDll =LoadLibrary(fdll)) ==NULL)
	{
		WriteLog("LoadMyDll %s failed!", fdll);
		return -1;
	}

	return 0;
}

void CHookAPI::FreeMyDll()
{
	if(m_hMyDll) FreeLibrary(m_hMyDll);

	m_hMyDll =NULL;
}

// Init hooked api info
int CHookAPI::Init()
{
	char cur_mod[200];
	MYAPIINFO *pMyAPIInfo;
	tagGetMyAPIInfo GetMyAPIInfo;
	APIFUNC old_api, my_api;
	HMODULE hmod;
	HINSTANCE hMyDll;

	// 得到要截获的api信息
	if(m_hMyDll ==NULL)
	{
		hMyDll =GetModuleHandle(MY_DLL_NAME);
		if(hMyDll ==NULL)
		{
			WriteLog("Init()::m_hMyDll ==NULL");
			return false;
		}
	}
	else
		hMyDll =m_hMyDll;

	if((GetMyAPIInfo=(tagGetMyAPIInfo)GetProcAddress(hMyDll, "GetMyAPIInfo")) ==NULL)
	{
		WriteLog("GetProcAddress GetMyAPIInfo of %s failed!", MY_DLL_NAME);
		return false;
	}
	pMyAPIInfo =GetMyAPIInfo();
	if(pMyAPIInfo ==NULL)
	{
		WriteLog("Init()::GetMyAPIInfo failed!");
		return false;
	}

	int count =0;
	//int f_hook_process =false;

	// 分析并保存函数和参数
	GetModuleFileName(NULL, cur_mod, sizeof(cur_mod));
	APIINFO *pinfo;

	//WriteLog("debug: get api info...");
	while (pMyAPIInfo[count].module_name !=NULL)
	{
		if((hmod =GetModuleHandle(pMyAPIInfo[count].module_name)) ==NULL)
		{
			WriteLog("Error Get module %s, so not hook this module's api", pMyAPIInfo[count].module_name);
			count++;
			continue;
		}
		if((old_api =(APIFUNC)GetProcAddress(hmod, pMyAPIInfo[count].api_name)) ==NULL)
		{
			WriteLog("Error Get ProcAddress %s of module %s, so not hook this module's api", pMyAPIInfo[count].api_name, pMyAPIInfo[count].module_name);
			count++;
			continue;
		}
		//WriteLog("debug: GetProcAddress %s %s =%x", pMyAPIInfo[count].module_name, pMyAPIInfo[count].api_name, old_api);

		if((my_api =(APIFUNC)GetProcAddress(hMyDll, pMyAPIInfo[count].my_api_name)) ==NULL)
		{
			WriteLog("failed to GetrProcAddress:%s", pMyAPIInfo[count].my_api_name);
			return false;
		}

		//WriteLog("debug: add api info:%s", pMyAPIInfo[count].api_name);
		if((pinfo =g_api_info.Add(pMyAPIInfo[count].module_name, pMyAPIInfo[count].api_name,
			pMyAPIInfo[count].my_api_name, pMyAPIInfo[count].param_count,
			old_api, my_api, pMyAPIInfo[count].friend_my_api_name,
			pMyAPIInfo[count].start_pos)) ==NULL)
			break;

		count++;
	}

#ifdef WINNT
	if((hmod =GetModuleHandle("Kernel32.dll")) ==NULL)
	{
		WriteLog("Error Get module Kernel32.dll, can not hook CreateprocessA");
		return -1;
	}
	if((old_api =(APIFUNC)GetProcAddress(hmod, "CreateProcessA")) ==NULL)
	{
		WriteLog("Error Get ProcAddress CreateProcessA of module Kernel32.dll");
		return -1;
	}
	if((pinfo =g_api_info.Add("Kernel32.dll", "CreateProcessA", "myCreateprocessA2003", 10, old_api,
		(APIFUNC)myCreateProcessA2003)) ==NULL)
	{
		WriteLog("Error add APIINFO of CreateProcessA");
		return -1;
	}

	if((old_api =(APIFUNC)GetProcAddress(hmod, "CreateProcessW")) ==NULL)
	{
		WriteLog("Error Get ProcAddress CreateProcessW of module Kernel32.dll");
		return -1;
	}
	if((pinfo =g_api_info.Add("Kernel32.dll", "CreateProcessW", "myCreateprocessW2003", 10, old_api,
		(APIFUNC)myCreateProcessW2003)) ==NULL)
	{
		WriteLog("Error add APIINFO of CreateProcessW");
		return -1;
	}
#endif

	//WriteLog("debug: Init ok!");

	return 0;
}

// hook one api
int CHookAPI::HookOneAPI(APIINFO *pinfo)
{
	//WriteLog("debug: Hook one api:%s, start_pos:%d", pinfo->api_name, pinfo->start_pos);

	if(pinfo->f_hooked)
		return 0;

	BYTE *papi =(BYTE *)pinfo->old_api + pinfo->start_pos;  // insert call xxxxxxxx from start_pos. lgd 2003.03.01

	//将函数所指内容改为可写
	if(!RemoveProtection(pinfo))
	{
		WriteLog("RemoveProtection failed! %s", pinfo->api_name);
		return -1;
	}

	// 保存源函数前5个字节，因为后面将覆盖此处
	memcpy(pinfo->save_bytes, papi, 5/*sizeof(pinfo->save_bytes)*/);

	//在源函数前插入call ProcessCall命令
	papi[0] =0xE8;
	*(DWORD *)&papi[1] =(DWORD)ProcessCall -(DWORD)papi -CALL_BYTES_SIZE;

	pinfo->f_hooked =true;

	WriteLog("debug: Hook one api ok:hmod=%x, %s-%x(%x,%x,%x,%x), start_pos:%d", 
		GetModuleHandle(pinfo->module_name), pinfo->api_name,
		papi, papi[0], papi[1], pinfo->save_bytes[0], pinfo->save_bytes[1], pinfo->start_pos);
		
	return 0;
}

int CHookAPI::UnhookOneAPI(APIINFO *pinfo)
{
	if(!pinfo->f_hooked) return 0;

	// ligang, 2003.04.19
	// check if already unhooked in the first hook for same old api like that CreateProcessW are hooked in DISS.dll and DISSinj.dll
	// or: unhook only once for api that hooked more than once.
	APIINFO *pinfo2 =g_api_info.m_pInfo;
	int f_already_unhooked =0;
	while(pinfo2)
	{
		if(pinfo2 ==pinfo)
			break;
		if(pinfo2->old_api ==pinfo->old_api)  // already unhooked
		{
			f_already_unhooked =true;
			break;
		}
		pinfo2 =pinfo2->pnext;
	}

	if(f_already_unhooked)  // only unhook first hook, because the next hook's save_bytes is not origion bytes of old api
	{
		pinfo->f_hooked =false;
		return 0;
	}

	BYTE *papi =(BYTE *)pinfo->old_api +pinfo->start_pos;

	//char *p =(char *)papi;
	//WriteLog("debug: unHook one api:%s(%x, %x,%x), start_pos:%d", pinfo->api_name, papi, papi[0], papi[1], pinfo->start_pos);

	if(RemoveProtection(pinfo))
		memcpy((PBYTE)papi, pinfo->save_bytes, CALL_BYTES_SIZE);  // 恢复原5个字节
	else WriteLog("UnhookAPIFunction %s RemoveProtection failed!", pinfo->save_bytes);

#ifdef WIN95
	SetPageProtection(papi);
#else
	DWORD dwScratch;
	VirtualProtect(papi, 20, pinfo->old_protection_flags, &dwScratch);
#endif
	pinfo->f_hooked =false;

	return 0;
}

int CHookAPI::HookAllAPI()
{
	APIINFO *pinfo =g_api_info.m_pInfo;

	while (pinfo !=NULL)
	{
		HookOneAPI(pinfo);
		pinfo =pinfo->pnext;
	}

	return 0;
}

void CHookAPI::UnhookAllAPI()
{
	APIINFO *pinfo =g_api_info.m_pInfo;
	//ObtainSeDebugPrivilege();

	while (pinfo !=NULL)
	{
		UnhookOneAPI(pinfo);
		pinfo =pinfo->pnext;
	}
}

// 主要函数ProcessCall，所有api函数的替换函数
void CHookAPI::ProcessCall()
{
	PBYTE pbAfterCall;
	PDWORD pdwParam;
	PDWORD pdwESP;
	DWORD dwParamSize;
	void *pvReturnAddr;
	DWORD dwReturnValue;
	BYTE cl_val;
	BYTE *papi;
	DWORD errcode =0;

#ifdef WINNT
	PROCESS_INFORMATION *pi;
	char fname[128];
#endif

	_asm
	{
		Mov [cl_val], CL   // 备份CL for xxTextOutx in WIN9X
		Mov EAX, [EBP + 4] //前面是被替换的call xxxxxxxx命令
		Mov [pbAfterCall], EAX

		Mov EAX, [EBP + 8]
		Mov [pvReturnAddr], EAX // 保存上次调用位置

		Lea EAX, [EBP + 12]
		Mov [pdwParam], EAX    //取参数
	}
	APIINFO *pinfo;

#ifdef DEMO_VERSION
	if(++g_demo_count > 200)
		MsgBox("This is demo version, max hook count: 20 exceed...");
#endif

	if((pinfo =g_api_info.FindByOldAPI((APIFUNC)(pbAfterCall-CALL_BYTES_SIZE))) ==NULL)
	{
		WriteLog("ProcessCall: can not found api");
		goto call_ret;
	}
	g_api_info.lock(pinfo);

	papi =(BYTE *)pinfo->old_api+pinfo->start_pos;

	dwParamSize =pinfo->param_count * 4;  // 32位地址使用4个字节

	errcode =0;
	
	if(!RemoveProtection(pinfo))   // add 2004.03.30 for some time other program changed api's write permission
	{
		WriteLog("RemoveProtection failed! %s", pinfo->api_name);
		goto call_ret;
	}

	memcpy(papi, pinfo->save_bytes, CALL_BYTES_SIZE);   //还原原api函数前5字节(恢复api原函数调用)

	if(pinfo->my_friend_api_name[0])			// 如果此api和其他api函数共用部分代码如98中的TextOutA和TextOutW，则先恢复用到的函数的被修改的内容
	{
		WriteLog("Restore my_friend_api_name:%s", pinfo->my_friend_api_name);
		RestoreAPICodes(pinfo->my_friend_api_name);
	}

	// 压参数入堆栈
	_asm
	{
		Sub ESP, [dwParamSize]
		Mov [pdwESP], ESP
	}

	memcpy(pdwESP, pdwParam, dwParamSize);

	_asm
	{
		Mov CL, [cl_val]
	}

	// 调用mydll里的替换函数
	pinfo->my_api();

	_asm
	{
		Push EAX
		Mov [dwReturnValue], EAX  //构造返回值
	}

	errcode =GetLastError();
//#endif
	//MsgBox(pAPIInfo->szAPIName);
#ifdef WINNT
	if(!strcmp(pinfo->api_name, "CreateProcessW") || !strcmp(pinfo->api_name, "CreateProcessA") )
	{
		pi =(PROCESS_INFORMATION *)pdwParam[9];
		if(pi->hProcess)
		{
			//if(g_process_to_hook.Find(process_name))
			wsprintf(fname, "%s\\DISS.dll", g_szDllPath);
			//WaitForSingleObject(pi->hProcess, 0);
			//SuspendThread(pi->hThread);
#ifdef WINNT
			ObtainSeDebugPrivilege();
#endif
			InjectLib(pi->hProcess, fname);  // hook 新进程
			//ResumeThread(pi->hThread);
		}
		else WriteLog("hProcess ==NULL");
	}
#endif

	if(pinfo->my_friend_api_name[0])	// 再恢复上面修改的内容
	{
		WriteLog("Restore my_friend_api_name...");
		RestoreAPICodes(pinfo->my_friend_api_name);
	}

	// 恢复 Call xxxx
	papi[0] =0xE8;
	*(DWORD *)&papi[1] =(DWORD)ProcessCall - (DWORD)papi - CALL_BYTES_SIZE;

	g_api_info.unlock(pinfo);


call_ret:
	// 恢复错误码
	SetLastError(errcode);

	// 构造虚函数返回

	_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  //堆栈指针加参数大小，因为ProcessCall没有参数，所以要如此
		Push EDX
		Ret
	}
}

int CHookAPI::RestoreAPICodes(char *my_api_name)
{
	APIINFO *pinfo =g_api_info.FindByMyAPIName(my_api_name);
	if(pinfo ==NULL || pinfo->f_hooked ==false) return 0;

	g_api_info.lock(pinfo);
	memcpy((PBYTE)pinfo->old_api+pinfo->start_pos, pinfo->save_bytes, CALL_BYTES_SIZE/*(5>bytes)?bytes:5*/);
	g_api_info.unlock(pinfo);

	return 0;
}

int CHookAPI::ChangeAPICodes(char *my_api_name)
{
	APIINFO *pinfo =g_api_info.FindByMyAPIName(my_api_name);
	if(pinfo==NULL) return 0;
	
	BYTE *papi =(BYTE *)pinfo->old_api+pinfo->start_pos;

	papi[0] =0xE8;
	*(DWORD *)&papi[1] =(DWORD)ProcessCall -(DWORD)papi -CALL_BYTES_SIZE;

	return 0;
}

// 使某块有写保护的内存地址（代码段内地址）为可写
BOOL CHookAPI::RemoveProtection(APIINFO *pinfo)
{
	BYTE *papi =(BYTE *)pinfo->old_api+pinfo->start_pos;
	BOOL ret;

#ifdef WIN95

	ret =RemovePageProtection(papi);

#endif

#ifdef WINNT

	MEMORY_BASIC_INFORMATION mbi;
	DWORD dwProtectionFlags;
	DWORD dwScratch;

	VirtualQuery(papi, &mbi, sizeof(mbi));

	dwProtectionFlags =mbi.Protect;
	pinfo->old_protection_flags =dwProtectionFlags;

	dwProtectionFlags =PAGE_READWRITE;

	ret =VirtualProtect(papi, 20, dwProtectionFlags, &dwScratch);

#endif

	if(ret ==FALSE)
	{
		char err[256];
		GetErrString(err, sizeof(err), GetLastError());
		WriteLog("Error VirtualProtect:%s", err);
		//WriteLog("VirtualProtect:%x,%x,%x", pvAddress, dwProtectionFlags, dwScratch);
		return false;
	}

	return ret;
}

#ifdef WINNT

// to hook new process
DWORD WINAPI CHookAPI::myCreateProcessW2003(
	LPCWSTR lpApplicationName,
	LPWSTR lpCommandLine, 
	LPSECURITY_ATTRIBUTES lpProcessAttributes,
	LPSECURITY_ATTRIBUTES lpThreadAttributes,
	BOOL bInheritHandles,
	DWORD dwCreationFlags,
	LPVOID lpEnvironment,
	LPCWSTR lpCurrentDirectory,
	LPSTARTUPINFOW lpStartupInfo,
	LPPROCESS_INFORMATION lpProcessInformation
)
{
	//WriteLog("HookAPI:CreateProcessW");
	DWORD aaa= CreateProcessW(lpApplicationName,
		lpCommandLine, lpProcessAttributes,
		lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment,
		lpCurrentDirectory, lpStartupInfo, lpProcessInformation);
	char fname[400];
	int len =WideCharToMultiByte( CP_ACP, 0, lpCommandLine, -1, fname, sizeof(fname),NULL,NULL);
	fname[len] =0;
	WriteLog("HookAPI:CreateProcessW:%s", fname);
	return aaa;

}

DWORD WINAPI CHookAPI::myCreateProcessA2003(
	LPCSTR lpApplicationName,
	LPSTR lpCommandLine, 
	LPSECURITY_ATTRIBUTES lpProcessAttributes,
	LPSECURITY_ATTRIBUTES lpThreadAttributes,
	BOOL bInheritHandles,
	DWORD dwCreationFlags,
	LPVOID lpEnvironment,
	LPCSTR lpCurrentDirectory,
	LPSTARTUPINFO lpStartupInfo,
	LPPROCESS_INFORMATION lpProcessInformation
)
{
	DWORD aaa= CreateProcessA(lpApplicationName,
		lpCommandLine, lpProcessAttributes,
		lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment,
		lpCurrentDirectory, lpStartupInfo, lpProcessInformation);
	WriteLog("HookAPI:CreateProcessA:%s", lpCommandLine);
	return aaa;
}
#endif
