不乱于心,不困于情。
不畏将来,不念过往。如此,安好。

使用Windows API绕过进程保护

前言

最近在研究某数字杀软的时候看到有个配置选项:

这个自我保护实际上是加载360SelfProtection.sys驱动(看这名字应该还有360SelfProtection_win10.sys文件),在0环通过hook等手段保护注册表项,重要进程进程等。

比如这里要结束某核心进程,会显示无法结束,拒绝访问。

这个并不是说权限不够的问题,即便是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 e0mov 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; } }

直接注入,看到线程创建成功就表示已经成功了。

用procexp也可以看一眼。

比如现在我们想要结束QQ进程。

会发现无法结束。

这里由于还没有写判断规则,这个任务管理器目前无法结束任何进程,也就是所有进程都无法结束,这显然用户体验是不好的。

那么如果要选择性保护进程,又应该怎么做呢。注意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换成DetourDetachDetourDetach((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; } }

转自:奇安信攻防社区

赞(0) 打赏
未经允许不得转载:seo优化_前端开发_渗透技术 » 使用Windows API绕过进程保护

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏