4-特征修改
一、前言
参考文章:免杀与 golang 实现 - 先知社区
在 混淆加密
那一小节的开头我提过,AV/EDR会对木马文件进行特征扫描与匹配,为了规避静态查杀,除了混淆加密外,另一种办法就是本节将介绍的 特征修改
特征修改包括:
改函数名、改变量名、改包名、改头文件名、改代码逻辑等(常用于源码二开,最有效)
更改默认端口、修改https特征证书、修改http服务流量特征、域前置、修改密钥、修改默认的硬编码数字等(常用于具有流量通信的工具二开)
时间戳
换编译器
修改Visual Studio的的编译默认选项
资源修改和加签名
文件的熵值
在本小节中我只介绍第3点到第7点,至于第1点和第2点,只有具体二开某个工具的时候才会详细介绍。
注意:并不是说用了这些技术就可以提高免杀性,这个要看具体AV/EDR而言的,有时会你用了这些技术反而增加被杀的可能。这句话对后面各章节将要提到的各种免杀技术同样适用,所以我们的目标就是以最少的技术达到免杀效果。
二、修改时间戳
个别杀毒喜欢给最近创建的文件标记为可疑,我们可以修改时间戳提升一点点免杀性,可以使用工具修改一下程序的时间戳
项目地址:GitHub - sorabug/ChangeTimestamp: 一键修改exe、dll的编译时间、创建时间、修改时间和访问时间
该项目需要自己编译源码
点击ChangeTimestamp.sln,会自动打开VS

可以选择自己电脑上的NET Framework环境,我选择的是4.8

编译
看了一下源码,很简单,无后门,放心编译,点击生成即可

使用方法
.\ChangeTimestamp <filepath> <timestamp>
例子
.\ChangeTimestamp .\beacon.exe 2008-8-8 8:12:31


三、换编译器
大部分黑客早期都一直在使用VS默认的编译器(MSVC)去编写恶意代码导致就连正常的编译的都会报毒了,说的就是你,某数字杀毒软件,真tmd睿智,编写个helloworld程序都给我报毒。
所以就可以换一些相对冷门的编译器,如Clang、Intel C++ Compiler、MinGW,使用它们编译程序后报毒率大大降低。
它们的特点,详见:几种广泛使用的 C++ 编译器-CSDN博客
这里我将详细介绍Clang和MinGW,当然Intel C++ Compiler也挺好的,可以与VS很好的适配,只是需要的空间更大而已。
如何给VS安装Intel C++编译器 官网:Get the Intel® oneAPI Base Toolkit
安装教程:如何给VS安装Intel C++编译器-腾讯云开发者社区-腾讯云
3.1 Clang
Clang 是 LLVM 项目的一部分,旨在提供一个更现代、更快的编译体验。Clang是 前端
,前端主要负责词法和语法分析,将源代码转化为抽象语法树;LLVM是 后端
,后端则是将已经优化的中间代码转化为针对各自平台的机器代码。clang+llvm构成一个完整的编译器,它们统称为LLVM。
项目地址:GitHub - llvm/llvm-project:LLVM 项目是模块化和可重用的编译器和工具链技术的集合。
使用VS2022可以很方便的下载Clang
点击工具-获取工具和功能

在单个组件中搜索"clang",将两个选项都都勾选,安装即可。
这里我已经安装了

切换编译器

接下来就可以愉快的编译程序了。
3.2 MinGW
MinGW 的目标之一是提供一个轻量级的开发环境,使开发者能够在 Windows 上编写和编译原生的 C/C++ 程序,而无需依赖于大型的集成开发环境(IDE)
MinGW不是很依赖IDE,要与VS适配就要配置CMake,比较麻烦。详见:Visual Studio 2022使用MinGW来编译调试C/C++程序_cmakepresets.json-CSDN博客
如果是想要用MinGW来编译和调式程序的话,可以使用Clion这个IDE。默认安装的C++编译器就是MinGW,各种环境都配好了,开箱即用。越到后面,比如说 第三章-防御规避
,就越不推荐使用Clion了,因为会遇到各种各样的问题,比如说找不到库文件。
新建项目

直接运行main.cpp

在一个项目中创建多个main函数
在实际开发中,多个main函数的程序主要用于测试和调式目的。为了在一个项目中创建多个main函数,有两种办法可以达到这个目的,两种方法都需要利用CMake
(1)修改入口点,可以创建多个main文件
①右键项目,点击新建一个文件,命名为 main1.cpp

②配置CMake中的入口点,将main.cpp
修改为main1.cpp

③运行

(2)宏定义,一个main文件中包含多个main函数
①编写宏定义
#include <iostream>
#ifdef MAIN
int main()
{
std::cout << "Hello, World! main" << std::endl; return 0;
}
#endif
#ifdef MAIN1
int main()
{
std::cout << "Hello, World! main1" << std::endl; return 0;
}
#endif

②配置CMake
cmake_minimum_required(VERSION 3.28)
project(Test)
set(CMAKE_CXX_STANDARD 17)
add_executable(Test main.cpp)
add_definitions(-DMAIN1)
其中-D表示这是一个宏定义,MAIN1表示宏定义的名称
③运行

3.3 思路
我提供一些思路仅供参考使用。
如果不想安装CLion,但是安装了Clang和MinGW,可以在VS上用MSVC来调式(毕竟是VS自带的,适配性更好),然后调式完了再换Clang和MinGW进行编译即可。
如果只安装CLion,不想安装VS也可以,毕竟CLion也非常强大而且跨平台,再搭配
在一个项目中创建多个main函数
就可以在一个项目方便创建多个main函数,而不覆盖之前编写的代码,也省去了创建多个项目。关于这一点还是Golang好啊,一个项目创建多个目录,每个目录可以有一个main函数,这个是Go语言特性决定的。
四、修改Visual Studio的编译选项设置
有一定的免杀效果,这些选项看个人选择来设置


/MD
类型表示运行时库不集成,生成的文件小,程序运行时动态加载对应的 dll
;/MT
类型表示运行时库集成,生成的文件较大,在链接时将 C/C++
运行时库集成到程序中。
MT
和 MD
的选择,详见:Visual Studio中MD与MT的区别及运行库类型选择 - 知乎




五、资源修改和加签名
卡巴斯基和某数字杀毒软件具有启发式查杀的功能,对于较小的程序很容易检测出异常。所谓启发式查杀其实是利用病毒程序与正常程序启动或运行时,所做的行为不同来判断是否为病毒程序,多数结合人工智能手段进行判断,一般误判可能性较大
某数字杀毒软件常见的QVM编号
HEUR/Malware.QVM06.Gen 一般情况下加数字签名可过
HEUR/Malware.QVM07.Gen 一般情况下换资源
HEUR/Malware.QVM13.Gen 加壳了
HEUR/Malware.QVM19.Gen 杀壳 (lzz221089提供 )
HEUR/Malware.QVM20.Gen 改变了入口点
HEUR/Malware.QVM27.Gen 输入表
HEUR/Malware.QVM18.Gen 加花
HEUR/Malware.QVM05.Gen 加资源,改入口点
QVM07加资源一般加到2M会报QVM06
再加数字签名,然后再慢慢减资源,这个方法对大部分木马有效果。
QVM06 加数字签名
QVM12杀壳
QVM13杀壳
QVM27杀输入表
QVM19 加aspack
QVM20就加大体积/加aspack压缩
5.1 Ting_More
使用Ting_More工具进行全自动添加签名和添加资源 工具下载:Ting_More.zip_免费高速下载|百度网盘-分享无限制
.\Ting_More.exe -t .\beacon.exe

其结果输出到了output文件夹中

5.2 ResourceHacker
如果想自己替换资源,可以使用ResourceHacker工具
官网地址:Resource Hacker
如果你下载了Ting_More这个工具,它就自带了ResourceHacker这个工具。使用方法如下

选择一个.ico文件,然后替换

保存

5.3 使用VS添加资源

选择一个.ico文件即可


5.4 Signtool-GUI
如果想自己添加数字签名可以使用微软的SignTool工具,但是SignTool操作太过复杂,可以使用大佬开发的Signtool-GUI,图形化,方便操作。
工具下载: Signtool-GUI Ver1.0 【数字签名制作及签名工具】 - 吾爱破解 - 52pojie.cn
点击生成证书

给目标程序签名

没签成功,不懂为什么,算了换命令行来签吧。
signtool.exe sign /f xxx.pfx /t http://timestamp.digicert.com yyy.exe
例子
.\signtool.exe sign /f .\Cert_1735735852.pfx /t http://timestamp.digicert.com .\beacon.exe

5.5 SigThief
有些反病毒供应商优先考虑某些证书颁发机构,而不检查签名是否实际有效
工具与使用说明: GitHub - secretsquirrel/SigThief:窃取签名并一次制作一个无效签名
简单示例,更多示例详见上面给的链接
python .\sigthief.py -i C:\Windows\explorer.exe -t .\beacon.exe -o beacon_sig.exe


六、文件的熵值
熵也被用于检测PE文件病毒, 一般一些合法软件的熵值在 4.8 - 7.2 之间。使用混淆加密和加壳会增加程序的熵值
用Golang编译下面的代码,得到计算文件的熵值的工具。
package main
import (
"debug/pe"
"flag"
"fmt"
"io"
"log"
"math"
"os"
)
func calculateEntropy(buffer []byte) float64 {
l := float64(len(buffer))
m := map[byte]float64{}
for _, b := range buffer {
m[b]++
}
var hm float64
for _, c := range m {
hm += c * math.Log2(c)
}
return math.Log2(l) - hm/l
}
func calculateFileEntropy(filename string) {
fileBuffer, err := os.ReadFile(filename)
if err != nil {
log.Fatal(err)
}
fileEntropy := calculateEntropy(fileBuffer)
fmt.Printf("Entropy of \033[36m%s \033[0m as a whole file is: ", filename)
if fileEntropy >= 5.6 && fileEntropy <= 6.8 {
fmt.Printf("\033[32m%f\033[0m\n", fileEntropy) // Green - legitimate
} else if fileEntropy > 7.2 && fileEntropy <= 8.0 {
fmt.Printf("\033[31m%f\033[0m\n", fileEntropy) // Red - malicious
} else {
fmt.Printf("%f\n", fileEntropy)
}
}
func calculatePESectionEntropy(filename string) {
file, err := os.Open(filename)
if err != nil {
log.Fatal(err)
}
defer file.Close()
filePE, err := pe.NewFile(file)
if err != nil {
log.Fatal(err)
}
calculateFileEntropy(filename)
fmt.Printf("[i] Parsing \033[36m%s\033[0m 's PE Section Headers ...\n", filename)
colorIndex := 0
colors := []string{"\033[33m", "\033[32m", "\033[36m", "\033[35m", "\033[34m"}
for _, section := range filePE.Sections {
sectionName := string(section.Name[:])
sectionSize := section.Size
switch sectionName {
case ".text", ".data", ".rdata", ".pdata", ".xdata", ".CRT", ".rsrc", ".reloc":
sectionContent := make([]byte, sectionSize)
_, err := file.Seek(int64(section.Offset), 0)
if err != nil {
log.Fatal(err)
}
_, err = io.ReadFull(file, sectionContent)
if err != nil {
log.Fatal(err)
}
sectionEntropy := calculateEntropy(sectionContent)
color := colors[colorIndex]
colorIndex = (colorIndex + 1) % len(colors)
fmt.Printf("\t>>> %s%s%s Scored Entropy Of Value: %f\033[0m\n", color, "\""+sectionName+"\"", color, sectionEntropy)
}
}
}
func main() {
filename := flag.String("file", "", "File to calculate entropy")
flag.Parse()
if *filename == "" {
flag.Usage()
return
}
file, err := os.Open(*filename)
if err != nil {
log.Fatal(err)
}
defer file.Close()
_, err = pe.NewFile(file)
if err == nil {
calculatePESectionEntropy(*filename)
} else {
calculateFileEntropy(*filename)
}
}
正常文件

原始无阶段beacon.exe

避免提高熵值的方法:
加.ico图标
加签名
加版本信息
shellcode进行base64编码可以降熵,但是用AES、RSA、XOR加密后shellcode的熵值会增加。所以shellcode加密后再base64编码熵值会低一些
后面会介绍
保护
这一节,里面的介绍的保护手段会提高熵值,所以少用一些保护选项避免提高熵值,这也是我一直强调以最少的技术达到免杀效果的原因。