安全矩阵

 找回密码
 立即注册
搜索
查看: 2631|回复: 0

实战|使用Windows API绕过进程保护

[复制链接]

855

主题

862

帖子

2940

积分

金牌会员

Rank: 6Rank: 6

积分
2940
发表于 2021-11-19 10:29:50 | 显示全部楼层 |阅读模式
原文链接:实战|使用Windows API绕过进程保护

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

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

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

img

img
这个并不是说权限不够的问题,即便是system权限也不行。而是由于在底层,杀死进程的API已经被hook了,这里应该是内核hook,当想要结束的进程为核心进程时,就直接返回一个无法终止进程的弹窗。本文就如何实现一个进程保护功能进行探究,驱动就不写了,就写一个用户层的。
实现原理windows提供了一个可以杀死其他进程的API:TerminateProcess。如果是结束本身进程为:ExitProcess。
  1. BOOL TerminateProcess(
  2. [in] HANDLE hProcess,
  3. [in] UINT   uExitCode
  4. );
复制代码

通过命令taskkill或者通过任务管理器等GUI工具去结束进程,本质上都是调用的TerminateProcess,有些可能是调用更为底层的ZwTerminateProcess,但是本质都差不多,只是看该进程用的什么API,这里为了方便就使用TerminateProcess进行演示。通过hook TerminateProcess让执行该函数时直接返回没有权限。
获取API地址并创建新的hook函数
  1. static BOOL(WINAPI* OldTerminateProcess)(
  2.     HANDLE hProcess,
  3.     UINT   uExitCode) = TerminateProcess;
  4. BOOL WINAPI New_TerminateProcess(
  5. _In_ HANDLE hProcess,
  6. _In_ UINT uExitCode
  7. ) {
  8.     unhookTerminateProcess();
  9. MessageBox(NULL,L"该进程受保护!", L"Access Denied",NULL);
  10.     hookTerminateProcess();
  11. returnfalse;
  12. }
复制代码

这里是64位的进程,要改变的硬编码是12个字节,而且不需要去算偏移。
  1. 48 b8 _dwNewAddress(0x1122334455667788)
  2. ff e0
  3. mov rax, _dwNewAddress(0x1122334455667788)
  4. jmp rax
复制代码

将原来函数的调转地址改为我们自己函数的跳转地址
  1. void hookTerminateProcess() {
  2.     DWORD dwTerminateProcessOldProtect;
  3.     BYTE pData[12] = { 0x48,0xb8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xFF,0xE0};
  4.     ULONGLONG ullNewFuncAddr = (ULONGLONG)New_TerminateProcess;
  5. //保存本来的字节
  6. ::RtlCopyMemory(g_OldTerminateProcess, OldTerminateProcess, sizeof(pData));
  7. //更改权限
  8. VirtualProtect(OldTerminateProcess, 12, PAGE_EXECUTE_READWRITE, &dwTerminateProcessOldProtect);
  9. //更改原来的机器码,改为我们自己函数的地址
  10. ::RtlCopyMemory(&pData[2], &ullNewFuncAddr, sizeof(ullNewFuncAddr));
  11. ::RtlCopyMemory(OldTerminateProcess, pData, sizeof(pData));
  12. //恢复属性
  13. VirtualProtect(OldTerminateProcess, 12, dwTerminateProcessOldProtect, &dwTerminateProcessOldProtect);
  14. }
复制代码

恢复硬编码,还原调转地址。
  1. void unhookTerminateProcess() {
  2.     DWORD dwOldProtect = NULL;
  3. //改为可写属性
  4. VirtualProtect(OldTerminateProcess, 12, PAGE_EXECUTE_READWRITE, &dwOldProtect);
  5. //还原原来的硬编码
  6. RtlCopyMemory(OldTerminateProcess, g_OldTerminateProcess, sizeof(g_OldTerminateProcess));
  7. //还原属性
  8. VirtualProtect(OldTerminateProcess, 12, dwOldProtect, &dwOldProtect);
  9. }
复制代码

最方便的就是写成一个dll,这样注入到任务管理器并起一个线程跑就行了。
  1. BOOL APIENTRY DllMain( HMODULE hModule,
  2.                        DWORD  ul_reason_for_call,
  3.                        LPVOID lpReserved
  4. )
  5. {
  6. switch(ul_reason_for_call)
  7. {
  8. case DLL_PROCESS_ATTACH:
  9.         hookTerminateProcess();
  10. break;
  11. case DLL_THREAD_ATTACH:
  12. break;
  13. case DLL_THREAD_DETACH:
  14. break;
  15. case DLL_PROCESS_DETACH:
  16.         unhookTerminateProcess();
  17. break;
  18. }
  19. return TRUE;
  20. }
复制代码

注入这里也是为了练手,稍微写了一个注入程序,为了以后的方法写成了突破session 0的注入。
  1. #include<iostream>
  2. #include<windows.h>
  3. #include"tchar.h"
  4. #include<TlHelp32.h>
  5. usingnamespace std;
  6. BOOL EnbalePrivileges(HANDLE hProcess, LPCWSTR pszPrivilegesName)
  7. {
  8.     HANDLE hToken = NULL;
  9.     LUID luidValue = { 0};
  10.     TOKEN_PRIVILEGES tokenPrivileges = { 0};
  11.     BOOL bRet = FALSE;
  12.     DWORD dwRet = 0;
  13. // 打开进程令牌并获取进程令牌句柄
  14.     bRet = ::OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hToken);
  15. if(FALSE == bRet)
  16. {
  17.         printf("[!] Open CurrentProcessToken Error,Error is:%d\n",GetLastError());
  18. return FALSE;
  19. }
  20. else
  21. {
  22.         printf("[*] Open CurrentProcessToken Successfully!,TokenHandle is:%d\n", hToken);
  23. }
  24. // 获取本地系统的 pszPrivilegesName 特权的LUID值
  25.     bRet = ::LookupPrivilegeValue(NULL, pszPrivilegesName, &luidValue);
  26. if(FALSE == bRet)
  27. {
  28.         printf("[!] LookupPrivilegeValue Error,Error is:%d\n", GetLastError());
  29. return FALSE;
  30. }
  31. else
  32. {
  33.         printf("[*] LookupPrivilegeValue Successfully!\n");
  34. }
  35. // 设置提升权限信息
  36.     tokenPrivileges.PrivilegeCount= 1;
  37.     tokenPrivileges.Privileges[0].Luid= luidValue;
  38.     tokenPrivileges.Privileges[0].Attributes= SE_PRIVILEGE_ENABLED;
  39. // 提升进程令牌访问权限
  40.     bRet = ::AdjustTokenPrivileges(hToken, FALSE, &tokenPrivileges, 0, NULL, NULL);
  41. if(FALSE == bRet)
  42. {
  43.         printf("[!] AdjustTokenPrivileges Error,Error is:%d\n", GetLastError());
  44. return FALSE;
  45. }
  46. else
  47. {
  48. // 根据错误码判断是否特权都设置成功
  49.         dwRet = ::GetLastError();
  50. if(ERROR_SUCCESS == dwRet)
  51. {
  52.             printf("[√] ALL_ASSIGNED!\n");
  53. return TRUE;
  54. }
  55. elseif(ERROR_NOT_ALL_ASSIGNED == dwRet)
  56. {
  57.             printf("[!] ERROR:NOT_ALL_ASSIGNED,Error is %d\n", dwRet);
  58. return FALSE;
  59. }
  60. }
  61. return FALSE;
  62. }
  63. DWORD EnumModules(DWORD hPid, LPCSTR hMoudlePath)
  64. {
  65.     WCHAR szBuffer[MAX_PATH] = { 0};
  66.     mbstowcs(szBuffer, hMoudlePath, MAX_PATH);
  67. //通过pid列出所有的Modules
  68.     HANDLE hModuleSnap = INVALID_HANDLE_VALUE;
  69.     MODULEENTRY32    me32;
  70. //给进程所引用的模块信息设定一个快照
  71.     hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, hPid);
  72. if(hModuleSnap == INVALID_HANDLE_VALUE)
  73. {
  74.         printf("[!] Error:Enum modules failed to detect if there is an injected DLL module,error is %d\n",GetLastError());
  75. }
  76.     me32.dwSize = sizeof(MODULEENTRY32);
  77. if(!Module32First(hModuleSnap, &me32))
  78. {
  79.         printf("[!] Enum Error!\n");
  80. CloseHandle(hModuleSnap);
  81. }
  82. do
  83. {
  84. if(!memcmp(me32.szExePath, szBuffer,MAX_PATH))
  85. return1;           
  86. } while(Module32Next(hModuleSnap, &me32));
  87. CloseHandle(hModuleSnap);
  88. return0;
  89. }
  90. DWORD _InjectThread(DWORD _Pid, LPCSTR psDllPath)
  91. {
  92.     FILE* fp;
  93.     fp = fopen(psDllPath, "r");
  94. if(!fp)
  95. {
  96.         printf("[!] Error:DLL path not found\nPlease check that your path is correct or absolute\n");
  97. return FALSE;
  98. }
  99.     fclose(fp);
  100.     printf("****************************************************************************\n");
  101.     HANDLE hprocess = NULL;
  102.     HANDLE hThread = NULL;
  103.     DWORD _SIZE = 0;
  104.     LPVOID pAlloc = NULL;
  105.     FARPROC pThreadFunction = NULL;
  106.     DWORD ZwRet= 0;
  107.     hprocess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, _Pid);
  108. if(hprocess == NULL)
  109. {
  110. printf("[!] OpenProcess Error,Error is:%d\n", GetLastError());
  111. return FALSE;
  112. }
  113. else
  114. {
  115. printf("[*] OpenProcess Successfully!\n");
  116. }
  117. _SIZE = strlen(psDllPath)+1;
  118. pAlloc = ::VirtualAllocEx(hprocess, NULL, _SIZE, MEM_COMMIT, PAGE_READWRITE);
  119. if(pAlloc == NULL)
  120. {
  121. printf("[!] VirtualAllocEx Error,Error is:%d\n", GetLastError());
  122. return FALSE;
  123. }
  124. else
  125. {
  126. printf("[*] VirtualAllocEx Successfully!\n");
  127. }
  128. BOOL x = ::WriteProcessMemory(hprocess, pAlloc, psDllPath, _SIZE, NULL);
  129. if(FALSE == x)
  130. {
  131. printf("[!] WriteMemory Error,Error is:%d\n", GetLastError());
  132. return FALSE;
  133. }
  134. else
  135. {
  136. printf("[*] WriteMemory Successfully!\n");
  137. }
  138. HMODULE hNtdll = LoadLibrary(L"ntdll.dll");
  139. if(hNtdll == NULL)
  140. {
  141. printf("[!] LoadNTdll Error,Error is:%d\n", GetLastError());
  142. return FALSE;
  143. }
  144. else
  145. {
  146. printf("[*] Load ntdll.dll Successfully!\n");
  147. }
  148. pThreadFunction = ::GetProcAddress(::GetModuleHandle(L"kernel32.dll"), "LoadLibraryA");
  149. if(pThreadFunction == NULL)
  150. {
  151. printf("[!] Get LoadLibraryA Address Error,Error is:%d\n", GetLastError());
  152. return FALSE;
  153. }
  154. else
  155. {
  156. printf("[*] Get LoadLibraryA Address Successfully! Address is %x\n", pThreadFunction);
  157. }
  158. #ifdef _WIN64
  159. typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
  160. PHANDLE ThreadHandle,
  161. ACCESS_MASK DesiredAccess,
  162. LPVOID ObjectAttributes,
  163. HANDLE ProcessHandle,
  164. LPTHREAD_START_ROUTINE lpStartAddress,
  165. LPVOID lpParameter,
  166. ULONG CreateThreadFlags,
  167. SIZE_T ZeroBits,
  168. SIZE_T StackSize,
  169. SIZE_T MaximumStackSize,
  170. LPVOID pUnkown
  171. );
  172. #else
  173. typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
  174. PHANDLE ThreadHandle,
  175. ACCESS_MASK DesiredAccess,
  176. LPVOID ObjectAttributes,
  177. HANDLE ProcessHandle,
  178. LPTHREAD_START_ROUTINE lpStartAddress,
  179. LPVOID lpParameter,
  180. BOOL CreateSuspended,
  181. DWORD dwStackSize,
  182. DWORD dw1,
  183. DWORD dw2,
  184. LPVOID pUnkown
  185. );
  186. #endif
  187. typedef_ZwCreateThreadEx ZwCreateThreadEx= NULL;
  188. ZwCreateThreadEx= (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdll, "ZwCreateThreadEx");
  189. if(ZwCreateThreadEx== NULL)
  190. {
  191. printf("[!] Get ZwCreateThreadEx Address Error,Error is:%d\n", GetLastError());
  192. return FALSE;
  193. }
  194. else
  195. {
  196. printf("[*] Get ZwCreateThreadEx Address Successfully! Address is %x\n", ZwCreateThreadEx);
  197. }
  198. HANDLE hRemoteThread;
  199. ZwRet= ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hprocess,
  200. (LPTHREAD_START_ROUTINE)pThreadFunction, pAlloc, 0, 0, 0, 0, NULL);
  201. if(hRemoteThread == NULL)
  202. {
  203. printf("[!] Creat RemoteThread Error,Error is:%d\n", GetLastError());
  204. CloseHandle(hprocess);
  205. return FALSE;
  206. }
  207. printf("[*] Please wait for a moment in the process of injection:\n");
  208. for(int m = 0;m<5;m++)
  209. {
  210. if(EnumModules(_Pid, psDllPath))
  211. {
  212. printf("[√] Creat RemoteThread Successfully! RemoteThread Id is %x\n", hRemoteThread);
  213. VirtualFreeEx(hprocess, pAlloc, 0, MEM_RELEASE);
  214. CloseHandle(hRemoteThread);
  215. CloseHandle(hprocess);
  216. FreeLibrary(hNtdll);
  217. return TRUE;
  218. }
  219. Sleep(2000);
  220. }
  221. printf("[!] DLL injection failed!\nNotice:Please check that your path is absolute or correct\n");
  222. VirtualFreeEx(hprocess, pAlloc, 0, MEM_RELEASE);
  223. CloseHandle(hRemoteThread);
  224. CloseHandle(hprocess);
  225. FreeLibrary(hNtdll);
  226. return FALSE;
  227. }
  228. int main(int argc, char* argv[])
  229. {
  230. if(argc == 3)
  231. {
  232. EnbalePrivileges(GetCurrentProcess(), SE_DEBUG_NAME);
  233. DWORD dwPid;
  234. sscanf(argv[1],"%d", &dwPid);
  235. _InjectThread(dwPid, argv[2]);
  236. return1;
  237. }
  238. else
  239. {
  240. printf("[!] You passed in the wrong number of parameters!Please pass in two parameters.\n");
  241. 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!");
  242. return0;
  243. }
  244. }
复制代码

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

img
用procexp也可以看一眼。

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

img
会发现无法结束。

img
这里由于还没有写判断规则,这个任务管理器目前无法结束任何进程,也就是所有进程都无法结束,这显然用户体验是不好的。
那么如果要选择性保护进程,又应该怎么做呢。注意TerminateProcess的第一个参数,传入的是一个句柄,这个句柄需要从openprocess的返回值获得,所以我们还需要知道打开进程的句柄。
同一进程多次使用openprocess获取的句柄是不一样的。
inline hook的稳定性还是差了点,很容易让进程崩溃。根据实验:任务管理器会不断地调用openprocess这个api,不管有没有操作都会一直调用。这里就用微软更加稳定的detours库。
  1. voidHook()
  2. {
  3. DetourTransactionBegin();
  4. DetourUpdateThread(GetCurrentThread());
  5. // 参数一是原函数地址,参数二是新函数地址
  6. DetourAttach((PVOID*)&OldOpenProcess, New_OpenProcess);
  7. DetourAttach((PVOID*)&OldTerminateProcess, New_TerminateProcess);
  8. DetourTransactionCommit();
  9. }
  10. voidUnHook()
  11. {
  12. DetourTransactionBegin();
  13. DetourUpdateThread(GetCurrentThread());
  14. // 和Hook完全一样,不同的只是将DetourAttach换成DetourDetach
  15. DetourDetach((PVOID*)&OldTerminateProcess, New_TerminateProcess);
  16. DetourDetach((PVOID*)&OldOpenProcess, New_OpenProcess);
  17. DetourTransactionCommit();
  18. }
复制代码

我们自己的函数如下编写,就是做一些简单的判断,比如这里要保护的进程id为5568 :
  1. HANDLE g_handle = NULL;
  2. HANDLE WINAPI New_OpenProcess(DWORD dwDesiredAccess,BOOL bInheritHandle,DWORD dwProcessId) {
  3. if(dwProcessId == 5568) {
  4.         g_handle = OldOpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
  5. return g_handle;
  6. }
  7. else
  8. {
  9.         HANDLE hHandle = OldOpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId);
  10. return hHandle;
  11. }
  12. }
  13. BOOL WINAPI New_TerminateProcess(
  14. _In_ HANDLE hProcess,
  15. _In_ UINT uExitCode
  16. ) {
  17. if(g_handle == hProcess)
  18. {
  19. MessageBox(NULL, L"该进程受保护!", L"Access Denied", NULL);
  20.         g_handle = NULL;
  21. returnfalse;
  22. }
  23. else
  24. {
  25. OldTerminateProcess(hProcess, uExitCode);
  26. returntrue;
  27. }
  28. }
复制代码





回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2025-4-23 03:18 , Processed in 0.017486 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表