一、背景
最近想写一个杀毒软件,可是别人在任务管理器里轻轻松松就把我的进程结束了,我希望把我的杀毒软件做成360那样,一旦结束就提示“拒绝访问”,而且不管你用任务管理器,还是taskkill,进程都无法结束。
于是,我在网上搜集了大量的资料,很多资料都说要写驱动,可我是一名小白,根本不会写驱动,正准备去学的时候,突然发现写驱动需要数字签名,还要花250美金,也就是1750人民币左右!我可不想花这么多钱。
我一开始误以为写驱动是为了在Ring0运行,从而让进程杀不掉,但是杀毒软件这个进程其实还是在Ring3运行的,所以不是通过提高权限来防止别人终止程序的。后来听说驱动是通过注册回调来实现的,而注册回调实际上和HOOK差不多。我学过Inline hook,于是想用Inline hook实现进程防终止。不过,Inline hook需要DLL注入,也就是说,只有注入DLL的进程才会被HOOK。网上大部分人都是把DLL写好之后,注入任务管理器,可是我用taskkill依然可以结束进程。也就是说,我必须想出一个方法,这个方法能HOOK所有程序,或者说把DLL注入所有程序。经过一番苦思冥想,我终于想出了解决办法。
二、思路
关于Inline hook,网上有很多博客,这里就不说了。我要HOOK住OpenProcess,当检测到要Open的进程是受保护的进程时,就返回拒绝访问。
我现在要解决的问题是,如何将DLL注入所有程序。
注意,这里的“注入所有程序”指的是:不管你怎么运行新进程,这个进程都会被注入我们的DLL,而不是将DLL注入所有现有的系统进程。比如说,你想打开任务管理器结束进程,而任务管理器一打开就要被注入DLL,导致你无法注入。
我想了两个思路,但是只有一个成功,不过我还是都分享出来吧。
思路一
既然要注入所有进程,一个很自然的想法是:写一个死循环,不停地枚举系统中所有进程,对于每一个进程,枚举它加载的DLL,如果没有我们的DLL,就立刻往里面注入DLL。这样,你打开任务管理器的时候,因为没有我们的DLL,所以会立刻被注入DLL。
事实也是如此。用任务管理器无法结束进程。但是用taskkill可以结束。
为什么呢?
原来,这个方法有一个缺陷:那些已经注入DLL的进程,在枚举的时候会不停地枚举,导致做了很多无用功。而且,像taskkill这样的进程,一启动就会立刻调用OpenProcess,如果枚举的时候刚好错过了taskkill这个进程,还没有再次枚举到的时候,taskkill已经调用了OpenProcess,结果是:出现了漏网之鱼!
于是,我只好乖乖地使用第二种方法。
思路二
思路一失败了。我突然想到了一种方法可以解决思路一的缺陷(做了很多无用功):那些“老进程”(系统中正在运行的进程)第一次循环时就会被注入DLL,然后需要注入DLL的,只是那些“新进程”(刚启动,没有注入DLL的进程)。而第一种方法为了捕获到“新进程”,会反反复复地枚举“老进程”,做了很多无用功,耗费了宝贵的时间。
那么,一个新的思路就出现了:只要检测到新进程启动,就注入DLL。这样,就不用枚举老进程了。
问题来了:怎么检测新进程启动?难道要修改操作系统的代码吗???
其实不用。搜集了一些资料以后,我发现:大部分用户进程是通过explorer.exe调用CreateProcessW产生的。那么,我们只需要再HOOK住CreateProcessW就可以了。
因此,我写了两个DLL(其实写一个也行)。一个负责HOOK OpenProcess。另一个负责HOOK CreateProcessW,一旦调用CreateProcessW创建子进程,就立刻往新的进程里注入这两个DLL。为什么要同时注入两个呢?因为这样操作之后,子进程的CreateProcessW也会被HOOK,这样子进程的子进程也会被注入DLL,以此类推,所有explorer.exe的子进程都会被注入DLL。
最后,将两个DLL同时注入explorer.exe即可。
三、代码实现
部分代码参考:[Win32] API Hook(1)在32位系统上的实现
HOOK OpenProcess DLL:
代码中的notepad.exe要换成自己要保护的进程。
(processapi.h是我自己写的头文件,待会会展示)
#include <stdio.h>
#include <windows.h>
#include "processapi.h"
unsigned char code[5];
unsigned char oldcode[5];
FARPROC addr;
HANDLE WINAPI MyOpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId){
HANDLE handle;
char exe[256];
GetExeName(dwProcessId,exe);
if (stricmp(exe,"notepad.exe") == 0){
SetLastError(5);
return NULL;
}
DWORD old;
if (VirtualProtectEx(GetCurrentProcess(), (void*)addr, 5, PAGE_EXECUTE_READWRITE, &old)){
WriteProcessMemory(GetCurrentProcess(), (void*)addr, oldcode, 5, NULL);
VirtualProtectEx(GetCurrentProcess(), (void*)addr, 5, old, &old);
}
handle = OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
if (VirtualProtectEx(GetCurrentProcess(), (void*)addr, 5, PAGE_EXECUTE_READWRITE, &old)){
WriteProcessMemory(GetCurrentProcess(), (void*)addr, code, 5, NULL);
VirtualProtectEx(GetCurrentProcess(), (void*)addr, 5, old, &old);
}
return handle;
}
BOOL WINAPI DllMain(HMODULE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
addr = 0;
HMODULE hdll; hdll = LoadLibrary(TEXT("Kernel32.dll"));
addr = GetProcAddress(hdll, "OpenProcess");
if (addr){
code[0] = 0xe9;
DWORD a = (DWORD)MyOpenProcess - (DWORD)addr - 5;
RtlMoveMemory(code + 1, &a, 4);
DWORD old;
if (VirtualProtectEx(GetCurrentProcess(), (void*)addr, 5, PAGE_EXECUTE_READWRITE, &old)){
RtlMoveMemory(oldcode, (void*)addr, 5);
WriteProcessMemory(GetCurrentProcess(), (void*)addr, code, 5, NULL);
VirtualProtectEx(GetCurrentProcess(), (void*)addr, 5, old, &old);
}
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
HOOK CreateProcess DLL:
代码中的K:\\tools\\进程防终止\\OpenProcess\\DLL.dll需要换成HOOK OpenProcess的DLL的路径,K:\\tools\\进程防终止\\CreateProcess\\DLL.dll需要换成HOOK CreateProcess的DLL的路径。
(injectdll.h也是我写的头文件)
#include <stdio.h>
#include <windows.h>
#include "injectdll.h"
unsigned char code[5];
unsigned char oldcode[5];
FARPROC addr;
WINBOOL WINAPI MyCP(LPCWSTR p1, LPWSTR p2, LPSECURITY_ATTRIBUTES p3,
LPSECURITY_ATTRIBUTES p4, WINBOOL p5, DWORD p6, LPVOID p7,
LPCWSTR p8, LPSTARTUPINFOW p9, LPPROCESS_INFORMATION p10){
BOOL ret;
DWORD old;
if (VirtualProtectEx(GetCurrentProcess(), (void*)addr, 5, PAGE_EXECUTE_READWRITE, &old)){
WriteProcessMemory(GetCurrentProcess(), (void*)addr, oldcode, 5, NULL);
VirtualProtectEx(GetCurrentProcess(), (void*)addr, 5, old, &old);
}
//MessageBox(NULL,"CreateProcessW被HOOK了!","HaHaHa",MB_SYSTEMMODAL);
ret = CreateProcessW(p1,p2,p3,p4,p5,p6,p7,p8,p9,p10);
InjectDLL(p10 -> dwProcessId,"K:\\tools\\进程防终止\\OpenProcess\\DLL.dll");
InjectDLL(p10 -> dwProcessId,"K:\\tools\\进程防终止\\CreateProcess\\DLL.dll");
if (VirtualProtectEx(GetCurrentProcess(), (void*)addr, 5, PAGE_EXECUTE_READWRITE, &old)){
WriteProcessMemory(GetCurrentProcess(), (void*)addr, code, 5, NULL);
VirtualProtectEx(GetCurrentProcess(), (void*)addr, 5, old, &old);
}
return ret;
}
BOOL WINAPI DllMain(HMODULE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
addr = 0;
HMODULE hdll; hdll = LoadLibrary(TEXT("Kernel32.dll"));
addr = GetProcAddress(hdll, "CreateProcessW");
if (addr){
code[0] = 0xe9;
DWORD a = (DWORD)MyCP - (DWORD)addr - 5;
RtlMoveMemory(code + 1, &a, 4);
DWORD old;
if (VirtualProtectEx(GetCurrentProcess(), (void*)addr, 5, PAGE_EXECUTE_READWRITE, &old)){
RtlMoveMemory(oldcode, (void*)addr, 5);
WriteProcessMemory(GetCurrentProcess(), (void*)addr, code, 5, NULL);
VirtualProtectEx(GetCurrentProcess(), (void*)addr, 5, old, &old);
}
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
main.cpp:(这段代码负责将DLL注入explorer.exe)
#include <windows.h>
#include <stdio.h>
#include "processapi.h"
#include "injectdll.h"
int main(){
EnablePrivilege(SE_DEBUG_NAME,TRUE);
InjectDLL(GetPID("explorer.exe"),"K:\\tools\\进程防终止\\CreateProcess\\DLL.dll");
return 0;
}
processapi.h:
#ifndef _PROCESS_API_5216_
#define _PROCESS_API_5216_
#include <windows.h>
#include <tlhelp32.h>
BOOL EnablePrivilege(LPCSTR Name,BOOL fEnable)
{
//Enabling the debug privilege allows the application to see
//information about service application
BOOL fOk = FALSE; //Assume function fails
HANDLE hToken;
//Try to open this process's acess token
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
{
//Attempt to modify the "Debug" privilege
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
LookupPrivilegeValue(NULL, Name, &tp.Privileges[0].Luid);
tp.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0;
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
fOk = (GetLastError() == 0);
CloseHandle(hToken);
}
return fOk;
}
int GetPID(const char *szExeName){
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
PROCESSENTRY32 pe = {sizeof(pe)};
Process32First(hSnap,&pe);
do{
if (stricmp(pe.szExeFile,szExeName) == 0)return pe.th32ProcessID;
}while (Process32Next(hSnap,&pe));
return 1;
}
BOOL GetExeName(int pid,char *szExeName){
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
PROCESSENTRY32 pe = {sizeof(pe)};
Process32First(hSnap,&pe);
do{
if (pid == pe.th32ProcessID){
strcpy(szExeName,pe.szExeFile);
return TRUE;
}
}while (Process32Next(hSnap,&pe));
return FALSE;
}
#endif
injectdll.h:
#ifndef _INJECT_DLL_5216_
#define _INJECT_DLL_5216_
#include <windows.h>
int InjectDLL(int pid,const char dllName[]){
HANDLE ProcessHandle;
LPVOID remotebuffer;
BOOL write;
ProcessHandle = OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid);
if (ProcessHandle == NULL)return 1;
int len = strlen(dllName);
remotebuffer = VirtualAllocEx(ProcessHandle,NULL,len,MEM_COMMIT,PAGE_READWRITE);
write = WriteProcessMemory(ProcessHandle,remotebuffer,(LPVOID)dllName,len,NULL);
if (write == 0)return 2;
PTHREAD_START_ROUTINE threatStartRoutineAddress = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryA");
HANDLE hThread = CreateRemoteThread(ProcessHandle, NULL, 0, threatStartRoutineAddress, remotebuffer, 0, NULL);
if (hThread == 0)return 3;
CloseHandle(ProcessHandle);
CloseHandle(hThread);
return 0;
}
#endif
四、注意事项
这种方法在32位Win7成功了,但是64位系统没有测试过。