下面代码演示了向“记事本”程序(NOTEPAD.EXE)的进程地址空间中拦截API,大致原理如下:
1. 提升注入(注意和“被注入”的区别)程序的进程访问权限
2. 随便打开一个记事本文件,注意是用NOTEPAD.EXE打开的
3. 查找NOTEPAD.EXE对应的进程ID,即Process ID
4. 以所有权限(包括read/write)打开NOTEPAD.EXE的进程ID
5. 在NOTEPAD.EXE的进程ID地址空间中,申请一块内存
6. 向刚刚申请的内存中,写入我们想要写的任何代码(注意:注入的代码中全部都用指针的形式调用相关数据和函数)
7. 我们写的代码不会自动执行,当你编辑被拦截的记事本内容,然后按 Ctrl+S 保存后,会调用CreateFileW(...)这个API,这个API已经被我们做过手脚了,会跳转到我们写的任何函数处,并执行,执行完后,将正确的API前10个字节恢复成原来的样子,然后清理现场,不留痕迹,就ok啦
注意:该程序必须在Release版本下运行,如果是DeBug版本的话,因为编译器没做优化和加了一些debug代码,所以会内存读写报错
//
//拦截 CreateFileW(...) API的示例代码
//
//整理:过客
//邮箱:386520874@qq.com
//日期:2014.04.06
#include <stdio.h>
#include <windows.h>
#include <wchar.h>
#include <TlHelp32.h>
typedef struct _RemoteParam
{
DWORD dwCreateFile;
DWORD dwMessageBox;
DWORD dwGetCurrentProcess;
DWORD dwWriteProcessMemory;
unsigned char szOldCode[10];
DWORD FunAddr;
wchar_t info[260];
} RemoteParam, * PRemoteParam;
typedef HANDLE (__stdcall * PFN_CREATEFILE)(LPCWSTR,DWORD,DWORD,LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE);
typedef int (__stdcall * PFN_MESSAGEBOX)(HWND, LPCWSTR, LPCWSTR, DWORD);
typedef BOOL (__stdcall * PFN_WRITEPROCESSMEMORY)(HANDLE,LPVOID,LPCVOID,SIZE_T,SIZE_T*);
typedef HANDLE (__stdcall * PFN_GETCURRENTPROCESS)(void);
void HookCreateFile(LPVOID lParam)
{
RemoteParam* pRP = (RemoteParam*)lParam;
DWORD NextIpAddr = 0;
DWORD dwParamaAddr = 0;
HANDLE RetFpHdl = INVALID_HANDLE_VALUE;
LPCWSTR lpFileName;
DWORD dwDesiredAccess;
DWORD dwShareMode;
LPSECURITY_ATTRIBUTES lpSecurityAttributes;
DWORD dwCreationDisposition;
DWORD dwFlagsAndAttributes;
HANDLE hTemplateFile;
PFN_CREATEFILE pfnCreatefile = (PFN_CREATEFILE)pRP->dwCreateFile;
//下面的汇编代码是将Createfile(...)里面的7个参数和另外两个变量(dwParamaAddr和NextIpAddr)保存到变量中,以便后面调用
__asm
{
MOV EAX,[EBP+8]
MOV [dwParamaAddr], EAX
MOV EAX,[EBP+12]
MOV [NextIpAddr], EAX
MOV EAX,[EBP+16]
MOV [lpFileName], EAX
MOV EAX,[EBP+20]
MOV [dwDesiredAccess], EAX
MOV EAX,[EBP+24]
MOV [dwShareMode], EAX
MOV EAX,[EBP+28]
MOV [lpSecurityAttributes], EAX
MOV EAX,[EBP+32]
MOV [dwCreationDisposition], EAX
MOV EAX,[EBP+36]
MOV [dwFlagsAndAttributes], EAX
MOV EAX,[EBP+40]
MOV [hTemplateFile], EAX
}
PFN_MESSAGEBOX pfnMessageBox = (PFN_MESSAGEBOX)pRP->dwMessageBox;
int allowFlag = pfnMessageBox(NULL, lpFileName, pRP->info, MB_ICONWARNING | MB_YESNO);
PFN_GETCURRENTPROCESS pfnGetCurrentProcess = (PFN_GETCURRENTPROCESS)pRP->dwGetCurrentProcess;
PFN_WRITEPROCESSMEMORY pfnWriteProcessMemory = (PFN_WRITEPROCESSMEMORY)pRP->dwWriteProcessMemory;
pfnWriteProcessMemory(pfnGetCurrentProcess(), (LPVOID)pfnCreatefile, (LPCVOID)pRP->szOldCode, 10, NULL);
RetFpHdl = pfnCreatefile(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
//下面代码是善后工作,这个非常重要,如果没有的话,EIP将回不到调用Createfile(...)的下一条命令,这条命令在"notepad.exe"的名字空间
//而不是kernel32.dll的名字空间中,要切记。
__asm
{
POP EDI
POP ESI
POP EBX
MOV EDX, [NextIpAddr] //调用Createfile(...)这一命令的下一条命令地址,
MOV EAX, [RetFpHdl] //函数返回值要放到EAX寄存器里,这一句可省略,原因是pfnCreatefile(...)会自动将结果存入EAX
MOV ESP, EBP
POP EBP
MOV ESP, EBP
PUSH EDX
RET //ret指令用栈中的数据(即edx里面的地址),修改IP的内容,注意不修改CS的内容,retf才是
}
}
//提升进程访问权限
BOOL enableDebugPriv()
{
HANDLE hToken;
LUID sedebugnameValue;
TOKEN_PRIVILEGES tkp;
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
return FALSE;
}
if(!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue))
{
CloseHandle(hToken);
return FALSE;
}
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = sedebugnameValue;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; //特权启用
if(!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL)) //启用指定访问令牌的特权
{
CloseHandle(hToken);
return FALSE;
}
return TRUE;
}
//根据进程名称得到进程ID,如果有多个运行实例的话,返回第一个枚举到的进程的ID
DWORD processNameToId(LPCTSTR lpszProcessName)
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 pe;
pe.dwSize = sizeof(PROCESSENTRY32);
if(!Process32First(hSnapshot, &pe))
{
MessageBox(NULL, "The frist entry of the process list has not been copyied to the buffer", "Notice", MB_ICONINFORMATION | MB_OK);
return 0;
}
while(Process32Next(hSnapshot, &pe)) //循环查找下一个进程
{
if(!strcmp(lpszProcessName, pe.szExeFile)) //找到了
{
return pe.th32ProcessID;
}
}
return 0;
}
//==============================================
int main(int argc, char* argv[])
{
//提升进程访问权限
if(!enableDebugPriv())
{
printf("提升进程访问权限 Error!\n");
return -1;
}
//等待输入进程名称,注意大小写匹配
char szExeName[MAX_PATH] = { 0 };
// cout << "Please input the name of target process !" << endl;
// cin >> szExeName; //接收命令行输入的字符串
// cout << szExeName << endl;
// strcpy(szExeName,"notepad.exe");
// scanf("%s",szExeName);
sprintf(szExeName,"NOTEPAD.EXE");
DWORD dwProcessId = processNameToId(szExeName); //Name转换成ID
if(dwProcessId == 0)
{
MessageBox(NULL, "The target process have not been found !", "Notice", MB_ICONINFORMATION | MB_OK);
return -1;
}
HANDLE hTargetProcess = OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE|PROCESS_VM_READ, FALSE, dwProcessId);
if(hTargetProcess == NULL)
{
printf("OpenProcess Error!\n");
return -1;
}
//定义线程体的大小,实际分配的内存大小是页内存大小的整数倍
int size_Func=1024*8;
DWORD dwFunAddr = (DWORD)VirtualAllocEx(hTargetProcess, NULL, size_Func, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if((LPVOID)dwFunAddr == NULL)
{
printf("申请线程内存失败!\n");
CloseHandle(hTargetProcess);
return -1;
}
DWORD dwPramaAddr = (DWORD)VirtualAllocEx(hTargetProcess, NULL, sizeof(RemoteParam), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if((LPVOID)dwPramaAddr == NULL)
{
printf("申请参数内存失败!\n");
CloseHandle(hTargetProcess);
return -1;
}
printf("\n线程内存地址:%.8x\n参数内存地址:%.8x\n", dwFunAddr, dwPramaAddr);
RemoteParam RParam;
ZeroMemory(&RParam, sizeof(RParam));
HMODULE hKernel32 = LoadLibrary("kernel32.dll");
HMODULE hUser32 = LoadLibrary("user32.dll");
RParam.dwCreateFile = (DWORD)GetProcAddress(hKernel32, "CreateFileW");
RParam.dwGetCurrentProcess = (DWORD)GetProcAddress(hKernel32, "GetCurrentProcess");
RParam.dwWriteProcessMemory = (DWORD)GetProcAddress(hKernel32, "WriteProcessMemory");
RParam.dwMessageBox = (DWORD)GetProcAddress(hUser32, "MessageBoxW");
wchar_t str2[]={L"我拦截API成功了,哈哈^o^"};
::wmemcpy_s(RParam.info,sizeof(RParam.info),str2,sizeof(str2));
for(int i=15; i<31; i++){RParam.info[i]=L'\0';}
unsigned char oldcode[10];
unsigned char newcode[10];
int praadd = (int)dwPramaAddr;
int threadadd = (int)dwFunAddr;
newcode[4] = praadd>>24;
newcode[3] = (praadd<<8)>>24;
newcode[2] = (praadd<<16)>>24;
newcode[1] = (praadd<<24)>>24;
newcode[0] = 0x68; //0x68: push newcode[1..4],参数先压栈
int offsetaddr = threadadd - (int)RParam.dwCreateFile - 10 ;
newcode[9] = offsetaddr>>24;
newcode[8] = (offsetaddr<<8)>>24;
newcode[7] = (offsetaddr<<16)>>24;
newcode[6] = (offsetaddr<<24)>>24;
newcode[5] = 0xE8; //0xE8: call newcode[6..9],然后调用函数
printf("NewCode:");
for(int j = 0; j < 10; j++){printf("0x%.2x ",newcode[j]);}
printf("\n");
DWORD dwRead = 0;
if(!ReadProcessMemory(GetCurrentProcess(), (LPCVOID)RParam.dwCreateFile, oldcode, 10, &dwRead))
{
printf("read error");
CloseHandle(hTargetProcess);
FreeLibrary(hKernel32);
return -1;
}
strcat((char*)RParam.szOldCode, (char*)oldcode);
RParam.FunAddr = dwFunAddr;
printf("RParam.dwCreateFile:%.8x\nRParam.dwMessageBox:%.8x\nRParam.dwGetCurrentProcess:%.8x\nRParam.dwWriteProcessMemory:%.8x\nRParam.FunAddr:%.8x\n",
RParam.dwCreateFile, RParam.dwMessageBox, RParam.dwGetCurrentProcess, RParam.dwWriteProcessMemory, RParam.FunAddr);
//RParam.szOldCode[i]里面存放的是kernel32.dll里面的CreateFileW函数的前10个字节
printf("RParam.szOldCode:");
for(int i = 0; i< 10; i++){printf("0x%.2x ", RParam.szOldCode[i]);}
printf("\n");
DWORD dwWrite = 0;
if(!WriteProcessMemory(hTargetProcess, (LPVOID)dwFunAddr, (LPVOID)&HookCreateFile, size_Func, &dwWrite))
{
printf("WriteRemoteProcessesMemory Error 1 !\n");
CloseHandle(hTargetProcess);
FreeLibrary(hKernel32);
return -1;
}
if(!WriteProcessMemory(hTargetProcess, (LPVOID)dwPramaAddr, (LPVOID)&RParam, sizeof(RemoteParam), &dwWrite))
{
printf("WriteRemoteProcessesMemory Error 2 !\n");
CloseHandle(hTargetProcess);
FreeLibrary(hKernel32);
return -1;
}
//将dwPid所在的进程的CreateFile API函数入口处前10个字节替换掉,当dwPid进程一旦调用CreateFile,就会先读取这10个字节
//实际上这10个字节是调用dwFunAddr处的函数,这个函数就是HookCreateFile
if(!WriteProcessMemory(hTargetProcess, (LPVOID)RParam.dwCreateFile, (LPVOID)newcode, 10, &dwWrite))
{
printf("WriteRemoteProcessesMemory Error 3 !\n");
CloseHandle(hTargetProcess);
FreeLibrary(hKernel32);
return -1;
}
printf("\n已经将代码注入到目标进程(notepad.exe),祝好运!!!\n");
CloseHandle(hTargetProcess);
FreeLibrary(hKernel32);
system("pause");
return 0;
}
--------------------------------------------------------------
运行结果截图: