问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
关于进程保护的探究
渗透测试
最近在研究某数字杀软的时候看到有个配置选项:
前言 -- 最近在研究某数字杀软的时候看到有个配置选项: [![](https://shs3.b.qianxin.com/attack_forum/2021/10/attach-8ef9216d5dae8c8f13709b821d684450f237ac94.png)](https://shs3.b.qianxin.com/attack_forum/2021/10/attach-8ef9216d5dae8c8f13709b821d684450f237ac94.png) 这个自我保护实际上是加载360SelfProtection.sys驱动(看这名字应该还有360SelfProtection\_win10.sys文件),在0环通过hook等手段保护注册表项,重要进程进程等。 [![](https://shs3.b.qianxin.com/attack_forum/2021/10/attach-9290efc1d25b098c3a89424fe4955dd5d9d61d35.png)](https://shs3.b.qianxin.com/attack_forum/2021/10/attach-9290efc1d25b098c3a89424fe4955dd5d9d61d35.png) 比如这里要结束某核心进程,会显示无法结束,拒绝访问。 [![](https://shs3.b.qianxin.com/attack_forum/2021/10/attach-467ae61b31ae241b4af62ac1307c047e8e2df318.png)](https://shs3.b.qianxin.com/attack_forum/2021/10/attach-467ae61b31ae241b4af62ac1307c047e8e2df318.png) [![](https://shs3.b.qianxin.com/attack_forum/2021/10/attach-a33f76fe33dab7701bf02df2156801db5525da91.png)](https://shs3.b.qianxin.com/attack_forum/2021/10/attach-a33f76fe33dab7701bf02df2156801db5525da91.png) 这个并不是说权限不够的问题,即便是system权限也不行。而是由于在底层,杀死进程的API已经被hook了,这里应该是内核hook,当想要结束的进程为核心进程时,就直接返回一个无法终止进程的弹窗。本文就如何实现一个进程保护功能进行探究,驱动就不写了,就写一个用户层的。 实现原理 ---- windows提供了一个可以杀死其他进程的API:TerminateProcess。如果是结束本身进程为:ExitProcess。 ```c++ BOOL TerminateProcess( [in] HANDLE hProcess, [in] UINT uExitCode ); ``` 通过命令taskkill或者通过任务管理器等GUI工具去结束进程,本质上都是调用的TerminateProcess,有些可能是调用更为底层的ZwTerminateProcess,但是本质都差不多,只是看该进程用的什么API,这里为了方便就使用TerminateProcess进行演示。 通过hook TerminateProcess让执行该函数时直接返回没有权限。 获取API地址并创建新的hook函数 ```c++ 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(); return false; } ``` 这里是64位的进程,要改变的硬编码是12个字节,而且不需要去算偏移。 ```php 48 b8 _dwNewAddress(0x1122334455667788) ff e0 mov rax, _dwNewAddress(0x1122334455667788) jmp rax ``` 将原来函数的调转地址改为我们自己函数的跳转地址 ```c++ 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); } ``` 恢复硬编码,还原调转地址。 ```c++ 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,这样注入到任务管理器并起一个线程跑就行了。 ```c++ 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的注入。 ```c++ #include <iostream> #include <windows.h> #include "tchar.h" #include <TlHelp32.h> using namespace 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; } else if (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)) return 1; } while (Module32Next(hModuleSnap, &me32)); CloseHandle(hModuleSnap); return 0; } 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]); return 1; } 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!"); return 0; } } ``` 直接注入,看到线程创建成功就表示已经成功了。 [![](https://shs3.b.qianxin.com/attack_forum/2021/10/attach-d63db9863c4d9c717519fbf5c0c952bae2436fd4.png)](https://shs3.b.qianxin.com/attack_forum/2021/10/attach-d63db9863c4d9c717519fbf5c0c952bae2436fd4.png) 用procexp也可以看一眼。 [![](https://shs3.b.qianxin.com/attack_forum/2021/10/attach-d6701f406436c1cef18d472e3743946e7c02a6a6.png)](https://shs3.b.qianxin.com/attack_forum/2021/10/attach-d6701f406436c1cef18d472e3743946e7c02a6a6.png) 比如现在我们想要结束QQ进程。 [![](https://shs3.b.qianxin.com/attack_forum/2021/10/attach-8a7a4e54e23e5dccf520e8316ddde70571377059.png)](https://shs3.b.qianxin.com/attack_forum/2021/10/attach-8a7a4e54e23e5dccf520e8316ddde70571377059.png) 会发现无法结束。 [![](https://shs3.b.qianxin.com/attack_forum/2021/10/attach-61c713a5069606cb8278c858c5252fc58f0e0905.png)](https://shs3.b.qianxin.com/attack_forum/2021/10/attach-61c713a5069606cb8278c858c5252fc58f0e0905.png) 这里由于还没有写判断规则,这个任务管理器目前无法结束任何进程,也就是所有进程都无法结束,这显然用户体验是不好的。 那么如果要选择性保护进程,又应该怎么做呢。注意TerminateProcess的第一个参数,传入的是一个句柄,这个句柄需要从openprocess的返回值获得,所以我们还需要知道打开进程的句柄。 > 同一进程多次使用openprocess获取的句柄是不一样的。 inline hook的稳定性还是差了点,很容易让进程崩溃。根据实验:任务管理器会不断地调用openprocess这个api,不管有没有操作都会一直调用。这里就用微软更加稳定的detours库。 ```c++ void Hook() { DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); // 参数一是原函数地址,参数二是新函数地址 DetourAttach((PVOID*)&OldOpenProcess, New_OpenProcess); DetourAttach((PVOID*)&OldTerminateProcess, New_TerminateProcess); DetourTransactionCommit(); } void UnHook() { DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); // 和Hook完全一样,不同的只是将DetourAttach换成DetourDetach DetourDetach((PVOID*)&OldTerminateProcess, New_TerminateProcess); DetourDetach((PVOID*)&OldOpenProcess, New_OpenProcess); DetourTransactionCommit(); } ``` 我们自己的函数如下编写,就是做一些简单的判断,比如这里要保护的进程id为5568 : ```c++ 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; return false; } else { OldTerminateProcess(hProcess, uExitCode); return true; } } ``` 当我尝试终止5568进程时会拒绝执行。 [![](https://shs3.b.qianxin.com/attack_forum/2021/10/attach-87b72250b5df0e62af2c805bf805d7501d9351c3.png)](https://shs3.b.qianxin.com/attack_forum/2021/10/attach-87b72250b5df0e62af2c805bf805d7501d9351c3.png) 尝试终止其他的程序则成功终止。比如这里有个QQ进程。 [![](https://shs3.b.qianxin.com/attack_forum/2021/10/attach-51cb4333cf6e232a3411a7b6893d5e8786ef8b05.png)](https://shs3.b.qianxin.com/attack_forum/2021/10/attach-51cb4333cf6e232a3411a7b6893d5e8786ef8b05.png) 可直接终止进程(这里本来有个gif,社区不能上传) [![](https://shs3.b.qianxin.com/attack_forum/2021/10/attach-fdade6afb069ee237aa0faf267acd6446f8b7c6c.png)](https://shs3.b.qianxin.com/attack_forum/2021/10/attach-fdade6afb069ee237aa0faf267acd6446f8b7c6c.png) [![](https://shs3.b.qianxin.com/attack_forum/2021/10/attach-183ffcc1811f4b6ad20f9374acd7d9f5ad73d09b.png)](https://shs3.b.qianxin.com/attack_forum/2021/10/attach-183ffcc1811f4b6ad20f9374acd7d9f5ad73d09b.png)
发表于 2021-11-01 15:51:53
阅读 ( 7023 )
分类:
安全开发
1 推荐
收藏
0 条评论
请先
登录
后评论
ccYo1
13 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!