|
原文链接:实战|使用Windows API绕过进程保护
前言最近在研究某数字杀软的时候看到有个配置选项:
 
img
这个自我保护实际上是加载360SelfProtection.sys驱动(看这名字应该还有360SelfProtection_win10.sys文件),在0环通过hook等手段保护注册表项,重要进程进程等。

img
比如这里要结束某核心进程,会显示无法结束,拒绝访问。
 
img
 
img
这个并不是说权限不够的问题,即便是system权限也不行。而是由于在底层,杀死进程的API已经被hook了,这里应该是内核hook,当想要结束的进程为核心进程时,就直接返回一个无法终止进程的弹窗。本文就如何实现一个进程保护功能进行探究,驱动就不写了,就写一个用户层的。
实现原理windows提供了一个可以杀死其他进程的API:TerminateProcess。如果是结束本身进程为:ExitProcess。
- BOOL TerminateProcess(
- [in] HANDLE hProcess,
- [in] UINT uExitCode
- );
复制代码
通过命令taskkill或者通过任务管理器等GUI工具去结束进程,本质上都是调用的TerminateProcess,有些可能是调用更为底层的ZwTerminateProcess,但是本质都差不多,只是看该进程用的什么API,这里为了方便就使用TerminateProcess进行演示。通过hook TerminateProcess让执行该函数时直接返回没有权限。
获取API地址并创建新的hook函数
- static BOOL(WINAPI* OldTerminateProcess)(
- HANDLE hProcess,
- UINT uExitCode) = TerminateProcess;
- BOOL WINAPI New_TerminateProcess(
- _In_ HANDLE hProcess,
- _In_ UINT uExitCode
- ) {
- unhookTerminateProcess();
- MessageBox(NULL,L"该进程受保护!", L"Access Denied",NULL);
- hookTerminateProcess();
- returnfalse;
- }
复制代码
这里是64位的进程,要改变的硬编码是12个字节,而且不需要去算偏移。- 48 b8 _dwNewAddress(0x1122334455667788)
- ff e0
- mov rax, _dwNewAddress(0x1122334455667788)
- jmp rax
复制代码
将原来函数的调转地址改为我们自己函数的跳转地址- void hookTerminateProcess() {
- DWORD dwTerminateProcessOldProtect;
- BYTE pData[12] = { 0x48,0xb8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xFF,0xE0};
- ULONGLONG ullNewFuncAddr = (ULONGLONG)New_TerminateProcess;
- //保存本来的字节
- ::RtlCopyMemory(g_OldTerminateProcess, OldTerminateProcess, sizeof(pData));
- //更改权限
- VirtualProtect(OldTerminateProcess, 12, PAGE_EXECUTE_READWRITE, &dwTerminateProcessOldProtect);
- //更改原来的机器码,改为我们自己函数的地址
- ::RtlCopyMemory(&pData[2], &ullNewFuncAddr, sizeof(ullNewFuncAddr));
- ::RtlCopyMemory(OldTerminateProcess, pData, sizeof(pData));
- //恢复属性
- VirtualProtect(OldTerminateProcess, 12, dwTerminateProcessOldProtect, &dwTerminateProcessOldProtect);
- }
复制代码
恢复硬编码,还原调转地址。- void unhookTerminateProcess() {
- DWORD dwOldProtect = NULL;
- //改为可写属性
- VirtualProtect(OldTerminateProcess, 12, PAGE_EXECUTE_READWRITE, &dwOldProtect);
- //还原原来的硬编码
- RtlCopyMemory(OldTerminateProcess, g_OldTerminateProcess, sizeof(g_OldTerminateProcess));
- //还原属性
- VirtualProtect(OldTerminateProcess, 12, dwOldProtect, &dwOldProtect);
- }
复制代码
最方便的就是写成一个dll,这样注入到任务管理器并起一个线程跑就行了。- BOOL APIENTRY DllMain( HMODULE hModule,
- DWORD ul_reason_for_call,
- LPVOID lpReserved
- )
- {
- switch(ul_reason_for_call)
- {
- case DLL_PROCESS_ATTACH:
- hookTerminateProcess();
- break;
- case DLL_THREAD_ATTACH:
- break;
- case DLL_THREAD_DETACH:
- break;
- case DLL_PROCESS_DETACH:
- unhookTerminateProcess();
- break;
- }
- return TRUE;
- }
复制代码
注入这里也是为了练手,稍微写了一个注入程序,为了以后的方法写成了突破session 0的注入。- #include<iostream>
- #include<windows.h>
- #include"tchar.h"
- #include<TlHelp32.h>
- usingnamespace std;
- BOOL EnbalePrivileges(HANDLE hProcess, LPCWSTR pszPrivilegesName)
- {
- HANDLE hToken = NULL;
- LUID luidValue = { 0};
- TOKEN_PRIVILEGES tokenPrivileges = { 0};
- BOOL bRet = FALSE;
- DWORD dwRet = 0;
- // 打开进程令牌并获取进程令牌句柄
- bRet = ::OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken);
- if(FALSE == bRet)
- {
- printf("[!] Open CurrentProcessToken Error,Error is:%d\n",GetLastError());
- return FALSE;
- }
- else
- {
- printf("[*] Open CurrentProcessToken Successfully!,TokenHandle is:%d\n", hToken);
- }
- // 获取本地系统的 pszPrivilegesName 特权的LUID值
- bRet = ::LookupPrivilegeValue(NULL, pszPrivilegesName, &luidValue);
- if(FALSE == bRet)
- {
- printf("[!] LookupPrivilegeValue Error,Error is:%d\n", GetLastError());
- return FALSE;
- }
- else
- {
- printf("[*] LookupPrivilegeValue Successfully!\n");
- }
- // 设置提升权限信息
- tokenPrivileges.PrivilegeCount= 1;
- tokenPrivileges.Privileges[0].Luid= luidValue;
- tokenPrivileges.Privileges[0].Attributes= SE_PRIVILEGE_ENABLED;
- // 提升进程令牌访问权限
- bRet = ::AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, 0, NULL, NULL);
- if(FALSE == bRet)
- {
- printf("[!] AdjustTokenPrivileges Error,Error is:%d\n", GetLastError());
- return FALSE;
- }
- else
- {
- // 根据错误码判断是否特权都设置成功
- dwRet = ::GetLastError();
- if(ERROR_SUCCESS == dwRet)
- {
- printf("[√] ALL_ASSIGNED!\n");
- return TRUE;
- }
- elseif(ERROR_NOT_ALL_ASSIGNED == dwRet)
- {
- printf("[!] ERROR:NOT_ALL_ASSIGNED,Error is %d\n", dwRet);
- return FALSE;
- }
- }
- return FALSE;
- }
- DWORD EnumModules(DWORD hPid, LPCSTR hMoudlePath)
- {
- WCHAR szBuffer[MAX_PATH] = { 0};
- mbstowcs(szBuffer, hMoudlePath, MAX_PATH);
- //通过pid列出所有的Modules
- HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
- MODULEENTRY32 me32;
- //给进程所引用的模块信息设定一个快照
- hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, hPid);
- if(hModuleSnap == INVALID_HANDLE_VALUE)
- {
- printf("[!] Error:Enum modules failed to detect if there is an injected DLL module,error is %d\n",GetLastError());
- }
- me32.dwSize = sizeof(MODULEENTRY32);
- if(!Module32First(hModuleSnap, &me32))
- {
- printf("[!] Enum Error!\n");
- CloseHandle(hModuleSnap);
- }
- do
- {
- if(!memcmp(me32.szExePath, szBuffer,MAX_PATH))
- return1;
- } while(Module32Next(hModuleSnap, &me32));
- CloseHandle(hModuleSnap);
- return0;
- }
- DWORD _InjectThread(DWORD _Pid, LPCSTR psDllPath)
- {
- FILE* fp;
- fp = fopen(psDllPath, "r");
- if(!fp)
- {
- printf("[!] Error:DLL path not found\nPlease check that your path is correct or absolute\n");
- return FALSE;
- }
- fclose(fp);
- printf("****************************************************************************\n");
- HANDLE hprocess = NULL;
- HANDLE hThread = NULL;
- DWORD _SIZE = 0;
- LPVOID pAlloc = NULL;
- FARPROC pThreadFunction = NULL;
- DWORD ZwRet= 0;
- hprocess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, _Pid);
- if(hprocess == NULL)
- {
- printf("[!] OpenProcess Error,Error is:%d\n", GetLastError());
- return FALSE;
- }
- else
- {
- printf("[*] OpenProcess Successfully!\n");
- }
- _SIZE = strlen(psDllPath)+1;
- pAlloc = ::VirtualAllocEx(hprocess, NULL, _SIZE, MEM_COMMIT, PAGE_READWRITE);
- if(pAlloc == NULL)
- {
- printf("[!] VirtualAllocEx Error,Error is:%d\n", GetLastError());
- return FALSE;
- }
- else
- {
- printf("[*] VirtualAllocEx Successfully!\n");
- }
- BOOL x = ::WriteProcessMemory(hprocess, pAlloc, psDllPath, _SIZE, NULL);
- if(FALSE == x)
- {
- printf("[!] WriteMemory Error,Error is:%d\n", GetLastError());
- return FALSE;
- }
- else
- {
- printf("[*] WriteMemory Successfully!\n");
- }
- HMODULE hNtdll = LoadLibrary(L"ntdll.dll");
- if(hNtdll == NULL)
- {
- printf("[!] LoadNTdll Error,Error is:%d\n", GetLastError());
- return FALSE;
- }
- else
- {
- printf("[*] Load ntdll.dll Successfully!\n");
- }
- pThreadFunction = ::GetProcAddress(::GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");
- if(pThreadFunction == NULL)
- {
- printf("[!] Get LoadLibraryA Address Error,Error is:%d\n", GetLastError());
- return FALSE;
- }
- else
- {
- printf("[*] Get LoadLibraryA Address Successfully! Address is %x\n", pThreadFunction);
- }
- #ifdef _WIN64
- typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
- PHANDLE ThreadHandle,
- ACCESS_MASK DesiredAccess,
- LPVOID ObjectAttributes,
- HANDLE ProcessHandle,
- LPTHREAD_START_ROUTINE lpStartAddress,
- LPVOID lpParameter,
- ULONG CreateThreadFlags,
- SIZE_T ZeroBits,
- SIZE_T StackSize,
- SIZE_T MaximumStackSize,
- LPVOID pUnkown
- );
- #else
- typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
- PHANDLE ThreadHandle,
- ACCESS_MASK DesiredAccess,
- LPVOID ObjectAttributes,
- HANDLE ProcessHandle,
- LPTHREAD_START_ROUTINE lpStartAddress,
- LPVOID lpParameter,
- BOOL CreateSuspended,
- DWORD dwStackSize,
- DWORD dw1,
- DWORD dw2,
- LPVOID pUnkown
- );
- #endif
- typedef_ZwCreateThreadEx ZwCreateThreadEx= NULL;
- ZwCreateThreadEx= (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdll, "ZwCreateThreadEx");
- if(ZwCreateThreadEx== NULL)
- {
- printf("[!] Get ZwCreateThreadEx Address Error,Error is:%d\n", GetLastError());
- return FALSE;
- }
- else
- {
- printf("[*] Get ZwCreateThreadEx Address Successfully! Address is %x\n", ZwCreateThreadEx);
- }
- HANDLE hRemoteThread;
- ZwRet= ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hprocess,
- (LPTHREAD_START_ROUTINE)pThreadFunction, pAlloc, 0, 0, 0, 0, NULL);
- if(hRemoteThread == NULL)
- {
- printf("[!] Creat RemoteThread Error,Error is:%d\n", GetLastError());
- CloseHandle(hprocess);
- return FALSE;
- }
- printf("[*] Please wait for a moment in the process of injection:\n");
- for(int m = 0;m<5;m++)
- {
- if(EnumModules(_Pid, psDllPath))
- {
- printf("[√] Creat RemoteThread Successfully! RemoteThread Id is %x\n", hRemoteThread);
- VirtualFreeEx(hprocess, pAlloc, 0, MEM_RELEASE);
- CloseHandle(hRemoteThread);
- CloseHandle(hprocess);
- FreeLibrary(hNtdll);
- return TRUE;
- }
- Sleep(2000);
- }
- printf("[!] DLL injection failed!\nNotice:Please check that your path is absolute or correct\n");
- VirtualFreeEx(hprocess, pAlloc, 0, MEM_RELEASE);
- CloseHandle(hRemoteThread);
- CloseHandle(hprocess);
- FreeLibrary(hNtdll);
- return FALSE;
- }
- int main(int argc, char* argv[])
- {
- if(argc == 3)
- {
- EnbalePrivileges(GetCurrentProcess(), SE_DEBUG_NAME);
- DWORD dwPid;
- sscanf(argv[1],"%d", &dwPid);
- _InjectThread(dwPid, argv[2]);
- return1;
- }
- else
- {
- printf("[!] You passed in the wrong number of parameters!Please pass in two parameters.\n");
- printf("[!] Notice:\n[!] The first parameter is the PID of the target process\n[!] The second parameter is the location of the injected DLL,Please enter the absolute path!");
- return0;
- }
- }
复制代码
直接注入,看到线程创建成功就表示已经成功了。 
img
用procexp也可以看一眼。
 
img
比如现在我们想要结束QQ进程。

img
会发现无法结束。
 
img
这里由于还没有写判断规则,这个任务管理器目前无法结束任何进程,也就是所有进程都无法结束,这显然用户体验是不好的。
那么如果要选择性保护进程,又应该怎么做呢。注意TerminateProcess的第一个参数,传入的是一个句柄,这个句柄需要从openprocess的返回值获得,所以我们还需要知道打开进程的句柄。
同一进程多次使用openprocess获取的句柄是不一样的。
inline hook的稳定性还是差了点,很容易让进程崩溃。根据实验:任务管理器会不断地调用openprocess这个api,不管有没有操作都会一直调用。这里就用微软更加稳定的detours库。
- voidHook()
- {
- DetourTransactionBegin();
- DetourUpdateThread(GetCurrentThread());
- // 参数一是原函数地址,参数二是新函数地址
- DetourAttach((PVOID*)&OldOpenProcess, New_OpenProcess);
- DetourAttach((PVOID*)&OldTerminateProcess, New_TerminateProcess);
- DetourTransactionCommit();
- }
- voidUnHook()
- {
- DetourTransactionBegin();
- DetourUpdateThread(GetCurrentThread());
- // 和Hook完全一样,不同的只是将DetourAttach换成DetourDetach
- DetourDetach((PVOID*)&OldTerminateProcess, New_TerminateProcess);
- DetourDetach((PVOID*)&OldOpenProcess, New_OpenProcess);
- DetourTransactionCommit();
- }
复制代码
我们自己的函数如下编写,就是做一些简单的判断,比如这里要保护的进程id为5568 :- HANDLE g_handle = NULL;
- HANDLE WINAPI New_OpenProcess(DWORD dwDesiredAccess,BOOL bInheritHandle,DWORD dwProcessId) {
- if(dwProcessId == 5568) {
- g_handle = OldOpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
- return g_handle;
- }
- else
- {
- HANDLE hHandle = OldOpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
- return hHandle;
- }
- }
- BOOL WINAPI New_TerminateProcess(
- _In_ HANDLE hProcess,
- _In_ UINT uExitCode
- ) {
- if(g_handle == hProcess)
- {
- MessageBox(NULL, L"该进程受保护!", L"Access Denied", NULL);
- g_handle = NULL;
- returnfalse;
- }
- else
- {
- OldTerminateProcess(hProcess, uExitCode);
- returntrue;
- }
- }
复制代码
 
|
|