文章目录
- 1.gdb基本命令
- 2.radare2基本指令
- 3.pwnTools使用
-
- 3.1.安装
- 3.2.基本模块
- 3.3.Tubes读写接口
- 3.4.汇编和反汇编
-
- 3.5.shellcode生成器
- 3.6.elf文件操作
- 3.7.ROP链生成器
- 3.8.实例1
- 3.9.实例2
- 3.10.实例3
- 3.11.实例4
- 3.12.实例4
- 4.64位程序注意
- 5.python二进制库ctypes
-
- 5.1.动态链接库方法
- 5.2.构建C数据类型
- 5.3.创建结构和组合
- 6.函数调用协议
- 7.PyDbg
-
- 7.1断点处理
- 7.2.异常处理
- 7.3.进程快照
- 7.4.找漏洞
- 8.绕过之ROP技术
- 9.绕过SafeSEH
-
- 9.1.原理
- 9.2.绕过方式
- 10.绕过GS技术
-
- 10.1.原理
- 10.2.绕过
- 11.绕过ASLR技术
-
- 11.1.原理
- 11.2.绕过
- 12.windbg使用
- 13.Immunity Debugger
-
- 13.1.PyCommand
- 13.2.PyHook
- 13.3.绕过windows的DEP
- 13.4.完成反调试机制
- 14.Hook
-
- 14.1. 用 PyDbg 实现 Soft Hooking Hooking
- 14.2.Hard Hooking
- 15.python库之zio的使用
-
- 15.1.简介
- 15.2.函数
- 16.FUZZ
-
- 16.1.SPIKE
-
- 16.1.1.简介
- 16.1.2.spk脚本原语
- 16.2.AFL
- 16.3.sulley
-
- 16.3.1.简介
- 16.3.2.Strings
- 16.3.3.Delimiters
- 16.3.4.Static and Random Primitives
- 16.3.5.Binary Data
- 16.3.6.Integers
- 16.3.7.Blocks and Groups
- 17.约束求解器Z3
-
- 17.1.简介
- 17.2.用Z解决软件注册码1
- 17.3.用Z解决软件注册码2
- 18.DLL and 代码注入
-
- 18.1.创建远程线程
- 18.2.DLL注入
- 18.3.代码注入
- 18.4.写一个纯python后门
-
- 18.4.1.文件隐藏
- 18.4.2.编写后门
- 18.5.打包成exe
- 19.Angr
-
- 19.1.简介
- 19.2.利用angr写下选择分支输入的脚本
- 20.信息泄露
-
- 20.1.例子1
- 20.2.例子2
- 21.IDAPython
-
- 21.1.基本函数
- 21.2.脚本例子
- 22.PyEmu
-
- 22.1.基本函数
1.gdb基本命令
2.radare2基本指令
常用命令:
信息搜集:
$ rabin2 -I ./program — 查看二进制信息
ii [q] – 查看导出表
?v sym.imp.func_name — 获取链接表中相应函数的地址(func_name@PLT)
?v reloc.func_name —获取全局偏移表中函数的地址(func_name@GOT)
ie [q] — 获取入口点地址
内存相关:
dmm — 列出模块 (库文件,内存中加载的二进制文件)
dmi [addr|libname] [symname] — 列出目标库的符号标志
搜索:
/?— 列出搜索子命令
/ string — 搜索内存/二进制文件字符串
/R [?] —搜索ROP gadgets
/R/ — 使用正则表达式搜索ROP gadgets
调试: ood [p] ——开始调试
dc — 继续执行
dcu addr – 继续执行直到达指定地址
dcr — 继续执行直至到达ret (使用步过step over)
dbt [?] —基于 dbg.btdepth 和 dbg.btalgo显示backtrace追踪函数
doo [args] — 重新打开调试模式
ds — 进入指令(step on)
dso — 步过(Step over)
aaa ——分析 fs ——检查分析后的符号 fs 分析符号;f ——查看具体的
s 函数 ——定位到指定函数
pdf@函数 ——列出指定函数的反汇编
axt @@ str.* 找到引用相应的字符串
ahi s 用于设置字符串特定的偏移地址(使用 ahi? 获得更多用法),@@它是一个迭代器,可以用来接受以后输入的多个参数。执行此命令后,图形视图将自动更新
V —使用视图模式p/P to在不同模式之间切换 dc
wopO eip
izzq~str 找到二进制字符串的地址 pd 1024~字符串地址 找到引用
ps @ addr 找到相应地址的字符串
afn name addr 命名地址的函数
afl 查看函数 izz 查看里面的字符串 ir 查看aslr函数偏移
/ /bin/sh搜索字符串
3.pwnTools使用
使用文档
3.1.安装
安装命令:pip install --upgrade pwntools
安装完毕后在python环境下只需使用 from pwn import * 即可导入
这会将大量的功能导入到全局命名空间,然后我们就可以直接使用单一的函数进行汇编、反汇编、pack,unpack等操作。
from pwn import *
3.2.基本模块
asm : 汇编与反汇编,支持x86/x64/arm/mips/powerpc等基本上所有的主流平台
dynelf : 用于远程符号泄漏,需要提供leak方法
elf : 对elf文件进行操作
gdb : 配合gdb进行调试
memleak : 用于内存泄漏
shellcraft : shellcode的生成器
tubes: 包括tubes.sock,tubes.process, tubes.ssh, tubes.serialtube,分别适用于不同场景的PIPE
utils : 一些实用的小功能,例如CRC计算,cyclic pattern等
3.3.Tubes读写接口
这是exploit最为基础的部分,对于一次攻击而言前提就是与目标服务器或者程序进行交互,这里就可以使用remote(address, port)产生一个远程的socket然后就可以读写了
con = remote('ip',port)
con.recvline()
con.revcuntil('str',drop=True)
con.send('str')
con.close()
con.recv()
con.recvall()
recvrepeat()
pwntools还有创建监听器的功能
l = listen()
r = remote('localhost',l.lport)
c = l.wait_for_connection()
r.send('hello')
c.recv()
通过pwnlib.tubes.process可以与进程进行交互,我们不单单可以通过编程的方式事先写好与进程交互的逻辑,还可以直接与进程交互
sh = process('/bin/sh')
sh.sendline('echo hello world;')
sh.recvline()
sh.interactive()
3.4.汇编与反汇编
使用arm进行汇编,使用disarm进行反汇编
asm('nop')
disarm('6a0258cd80ebf9'.decode('hex'))
3.5.shellcode生成器
使用shellcraft可以生成对应的架构的shellcode代码,直接使用链式调用的方法就可以得到
print shellcraft.i386.nop().strip('\n')
print shellcraft.i386.linux.sh()
print shellcraft.i386.linux.sh()
3.6.elf文件操作
在进行elf文件逆向的时候,总是需要对各个符号的地址进行分析,elf模块提供了一种便捷的方法能够迅速的得到文件内函数的地址,plt位置以及got表的位置 下图分别是打印文件装载的基地址、函数地址、GOT表的地址、PLT表的地址
e = ELF('/bin/cat') # 查看elf文件信息
hex(e.address) # 获得基地址
hex(e.symbols['write']) # 获得函数地址
hex(e.got['write']) # 获得在GOT表中的地址
hex(e.plt['write']) # 获得在PLT表中的地址
e.asm(address, assembly) # 在指定地址进行汇编
e.bss(offset) # 返回bss段的位置,offset是偏移值
e.checksec() # 对elf进行一些安全保护检查,例如NX,PIE等。
e.disasm(address, n_bytes) # 在指定位置进行n_bytes个字节的反汇编
e.offset_to_vaddr(offset) # 将文件中的偏移offset转换成虚拟地址VMA
e.vaddr_to_offset(address) # 与上面的函数作用相反
e.read(address, count) # 在address(VMA)位置读取count个字节
e.write(address, data) # 在address(VMA)位置写入data
e.section(name) # dump出指定section的数据
3.7.ROP链生成器
回顾一下ROP的原理,由于NX开启不能在栈上执行shellcode,我们可以在栈上布置一系列的返回地址与参数,这样可以进行多次的函数调用,通过函数尾部的ret语句控制程序的流程,而用程序中的一些pop/ret的代码块(称之为gadget)来平衡堆栈。其完成的事情无非就是放上/bin/sh,覆盖程序中某个函数的GOT为system的,然后ret到那个函数的plt就可以触发system(’/bin/sh’)。由于是利用ret指令的exploit,所以叫Return-Oriented Programming。
ROP对象实现了__getattr__的功能,可以直接通过func call的形式来添加函数,rop.read(0, elf.bss(0x80))实际相当于rop.call(‘read’, (0, elf.bss(0x80)))。通过多次添加函数调用,最后使用str将整个rop chain dump出来就可以了。
e = ELF('welpwn')
rop = ROP(elf)
rop.read(0,elf.bss(0x80))
str(rop)
rop.call(resolvable, arguments=()) # 添加一个调用,resolvable可以是一个符号,也可以是一个int型地址,注意后面的参数必须是元组否则会报错,即使只有一个参数也要写成元组的形式(在后面加上一个逗号)
rop.chain() # 返回当前的字节序列,即payload
rop.dump() # 直观地展示出当前的ropchain
rop.raw() # 在rop chain中加上一个整数或字符串
rop.search(move=0, regs=None, order=’size’) # 按特定条件搜索gadget,没仔细研究过
rop.unresolve(value)# 给出一个地址,反解析出符号
另外,对于整数的pack与数据的unpack,可以使用p32,p64,u32,u64这些函数,分别对应着32位和64位的整数
使用ROPgadgets寻找gadgets,用于构造ROP链条 ROPgadget --binary ./welpwn --only “opo|ret”
readelf -a write4 查看section信息
readelf -x .data write4 看.data里面是否有数据 在使用ROPgadget时通过–badbytes即可过滤掉包含坏字符的项
ROPgadget --binary badchars --badbytes “62|69|63|2f|20|66|6e|73”
ROPgadget --binary fluff -depth 20
readelf -x .data write4
3.8.实例1
3.9.实例2
3.10.实例3
3.11.实例4
3.12.实例4
4.64位程序注意
在64位程序中,我们先看RIP,发现它不包含我们前面随机生成的序列。在64位中,如果实际上无法跳转到该地址并执行,则不会向RIP中pop值。因此,如果在将其pop到RIP失败,该值会位于堆栈的顶部,因此需要从RSP获取值。
我们知道了可以泄露的函数 foothold_function() 。根据延时绑定技术,当我们在调用如 func@plt() 的时候,系统才会将真正的 func() 函数地址写入到GOT表的 func.got.plt 中,然后 func@plt()根据 func.got.plt 跳转到真正的 func() 函数上去。
5.python二进制库ctypes
5.1.动态链接库方法
ctypes 提供了三种方法调用动态链接库:cdll(), windll(), 和 oledll()。它们的不同之处就在 于,函数的调用方法和返回值。cdll() 加载的库,其导出的函数必须使用标准的 cdecl 调用 约定。windll()方法加载的库,其导出的函数必须使用 stdcall 调用约定(Win32 API 的原生约 定)。oledll()方法和 windll()类似,不过如果函数返回一个 HRESULT 错误代码,可以使用 COM 函数得到具体的错误信息
调用print函数
from ctypes import *
msvcrt = cdll.msvcrt
message_string = "Hello world\n"
msvcrt.printf("Testing: %s",message_string)
5.2.构造C数据类型
c_long()
5.3.创建结构体和联合
# 结构体
class beer_recipe(Structure):
_fields_ = [
("amt_barley", c_int),
("amt_water", c_int),
]
# 联合
class barley_amount(Union):
_fields_ = [
("barley_long", c_long),
("barley_int", c_int),
("barley_char", c_char * 8),
]
value = raw_input("Enter the amount of barley to put into the beer vat:
my_barley = barley_amount(int(value))
print "Barley amount as a long: %ld" % my_barley.barley_long
print "Barley amount as an int: %d" % my_barley.barley_long
print "Barley amount as a char: %s" % my_barley.barley_char
6.函数调用约定
(1)cdecl调用,常用于x86的c语言里,是从右到左依次压栈,并且负责函数堆栈平衡,返回值存储在EAX中 (2)stdcall,也是从右到左依次压栈,但是由函数自己负责堆栈平衡,返回值存储在EAX中
7.PyDbg
7.1断点处理
(1)print_loop.py
from ctypes import *
import time
msvcrt = cdll.msvcrt
count = 0
while 1:
msvcrt.printf("Loop iterations %d!\n" % count)
time.sleep(2)
count += 1
(2)print_random.py
from pydbg import *
from pydbg.defines import *
import struct
import random
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]
dbg.write_process_memory()
return DBG_CONTINUE
dbg = pydbg()
pid = raw_input("Enter the printf_loop.py PID:")
dbg.attach(int(pid))
printf_address = dbg.func_resolve("msvcrt","printf")
dbg.bp_set(printf_address,description="printf_address",handler=printf_randomizer)
dbg.run()
7.2.异常处理
(1)buffer_overflow.py
from ctypes import *
msvcrt = cdll.msvcrt
raw_input("Once the debugger is attached , press any key.")
buffer = c_char_p("AAAAAA")
overflow = "A"*1000
msvcrt.strcpy(buffer,overflow)
(2)access_violation_handler.py
from pydbg import *
from pydbg.defines import *
import utils
def check_accessv(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()
dbg.terminate_process()
return DBG_EXCEPTION_HANDLED
pid = raw_input("Enter the Process ID:")
dbg = pydbg()
dbg.attach(int(pid))
dbg.set_callback(EXCEPTION_ACCESS_VIOLATION,check_accessv)
dbg.run()
7.3.进程快照
#-*- coding:utf8 -*-
from pydbg import *
from pydbg.defines import *
import threading
import time
import sys
class snapshotter(object):
def __init__(self,path):
self.path = path
self.pid = None
self.dbg = None
self.running = True
pydbg_thread = threading.Thread(target=self.start_debugger)
pydbg_thread.setDaemon(0) # 创建的子线程不会被主线程回收
pydbg_thread.start()
while self.pid == None:
time.sleep(1)
monitor_thread = threading.Thread(target=self.monitor_debugger)
monitor_thread.setDaemon(0)
monitor_thread.start()
def start_debugger(self):
self.dbg = pydbg()
pid = self.dbg.load(self.path)
self.pid = pid
self.dbg.run()
self.running = True
def monitor_debugger(self):
while self.running == True:
input = raw_input("Enter:'snap','restore' or 'quit'")
input = input.lower().strip()
if input == "quit":
print "[*] Exiting the snapshotter"
self.running = False
self.dbg.terminate_process()
elif input == "snap":
print "[*] Suspending all threads"
self.dbg.suspend_all_threads()
print "[*] Obtaining snapshot"
self.dbg.process_snapshot()
print "[*] Resuming operation"
self.dbg.resume_all_threads()
elif input == "restore":
print "[*] Suspending all threads"
self.dbg.suspend_all_threads()
print "[*] Restoring snapshot"
self.dbg.process_restore()
print "[*] Resuming operation"
self.dbg.resume_all_threads()
7.4.找漏洞
#-*- coding:utf8 -*-
from ctypes import *
from 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]
while esp_offset<=20:
parameter = dbg.smart_deference(dbg.context.Esp+esp_offset)
print "[ESP + %d] => %s" % (esp_offset,parameter)
esp_offset += 4
dbg.suspend_all_threads()
dbg.process_snapshot()
dbg.resume_all_threads()
return DBG_CONTINUE
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()
dbg.terminate_process()
return DBG_EXCEPTION_HANDLED
def 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_CONTINUE
dbg = 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()
8.绕过之ROP技术
由于受到NX即不可执行内存技术的约束,导致在栈上的shellcode无法作为汇编代码执行,于是采用一条ROP链组成shellcode
(1)ret2win:如果有可以跳转得到flag的函数直接将该函数地址放入ROP (2)split:有system函数,且用izz搜索的到cat flag.txt字符串,将字符串压入edi,然后调用system函数 (3)write4:有system函数,没有搜索到cat flag.txt字符串,需要先将字符串存入.data段,然后在压入edi,调用system函数 (4)badchars:基本条件和write4一样,但是对拷贝的字符串有字符要求,先搜索没有badchars的ROPgadget,然后使用异或形成字符串cat flag.txt (5)pivot:该题栈空间被限制了,通过gdb调试后发现,溢出返回地址后面只有两个8字节,但是有两个fgets,于是可以跳转到第一个fgets所在的内存执行ROPgadget (6)fluff:如果ROPgadget不足,尝试将深度改为20搜索,并且利用组合进行ROP (7)ret2csu:如果没有ROPgadget,可以使用__lib_csu_init_函数,该函数是linux程序加载lib库的函数,几乎每个程序都有,里面有两段ROPgadget,比较通用。
9.绕过SafeSEH
9.1.原理
接下来我们来看一下操作系统在 SafeSEH 机制中发挥的重要作用。我们知道异常处理函数的调用是通过 RtlDispatchException()函数处理实现的,SafeSEH 机制也是从这里开始的。我们来看一下这里都有哪些保护措施。 (1)检查异常处理链是否位于当前程序的栈中,如果不在当前栈中,程序将终止异常处理函数的调用。 (2)检查异常处理函数指针是否指向当前程序的栈中。如果指向当前栈中,程序将终止异常处理函数的调用 (3)在前面两项检查都通过后,程序调用一个全新的函数 RtlIsValidHandler(),来对异常处 理函数的有效性进行验证,所以来详细介绍 RtlIsValidHandler()函数。 首先,该函数判断异常处理函数地址是不是在加载模块的内存空间,如果属于加载模块的 内存空间,校验函数将依次进行如下校验。 ① 判断程序是否设置了 IMAGE_DLLCHARACTERISTICS_NO_SEH 标识。如果设置了 这个标识,这个程序内的异常会被忽略。所以当这个标志被设置时,函数直接返回校验失败。 ② 检测程序是否包含安全 S.E.H 表。如果程序包含安全 S.E.H 表,则将当前的异常处理 函数地址与该表进行匹配,匹配成功则返回校验成功,匹配失败则返回校验失败。 ③ 判断程序是否设置 ILonly 标识。如果设置了这个标识,说明该程序只包含.NET 编译 人中间语言,函数直接返回校验失败。 ④ 判断异常处理函数地址是否位于不可执行页(non-executable page)上。当异常处理函 数地址位于不可执行页上时,校验函数将检测 DEP 是否开启,如果系统未开启 DEP 则返回校 验成功,否则程序抛出访问违例的异常。 如果异常处理函数的地址没有包含在加载模块的内存空间,校验函数将直接进行 DEP 相 关检测,函数依次进行如下校验。 ① 判断异常处理函数地址是否位于不可执行页(non-executable page)上。当异常处理函 数地址位于不可执行页上时,校验函数将检测 DEP 是否开启,如果系统未开启 DEP 则返回校 验成功,否则程序抛出访问违例的异常。 ② 判断系统是否允许跳转到加载模块的内存空间外执行,如果允许则返回校验成功,否 则返回校验失败。
9.2.绕过方式
首先为什么使用pop/pop/ret,因为调用异常处理函数的时候,pointer to next handler的是作为第三个参数传入的,一共有四个参数,从右往左压栈,所以刚好能ret到。
1.在Exploit 中不利用 SEH(而是通过覆盖返回地址的方法来利用,前提是模块没有GS保护)关于如何覆盖返回地址利用我在:软件漏洞挖掘系列教程第二篇:栈溢出覆盖返回地址实践 已经详细讲了。准确来说我觉得这不算是绕过,但是它往往是很成功的。【推荐指数:10 】
2.如果程序编译的时候没有启用 safeseh 并且至少存在一个没启用 safeseh 的加载模块(系统模块或程序 私有模块)。这样就可以用这些模块中的 pop/pop/ret 指令地址来绕过保护。前面那个test1.exe程序就有ntdll.dll和test1.exe这两个模块没有启用SafeSeh,所以我们仍然可以利用这两个模块的指令地址绕过SafeSeh。【推荐指数:8 】
3.如果只有应用程序没有启用 safeseh 保护机制,在特定条件下,你依然可以成功利用,应用程序被加载的地址有 NULL 字节,如果在程序中找到了 pop/pop/ret 指令,你可以使用这个地址(NULL 字节会是最后 一个字节),但是你不能把 shellcode 放在异常处理器之后(因为这样 shellcode 将不会被拷贝到内存中 – NULL 是字符串终止符)我们可以把shellcode放到Pointer to next SEH record的前面,在Pointer to next SEH record加一个跳到shellcode的跳转,所以我们可以直接忽视0x00截断问题。(如果nseh放jmp 地址放不下,要将shellcode往上放8个字节放jmp 地址,然后nseh放jmp 到上面8个字节的地址)【推荐指数:4 】
4.从堆中绕过SafeSeh。【推荐指数:1 】
5.利用加载模块外的地址绕过SafeSeh。【推荐指数:6 】
除了平时我们常见的PE文件模块(exe和dll)外,还有一些映射文件,我们可以通过Olldbg的View-memory查看程序的内存映射状态。例如下图 类型为Map的映射文件,SafeSeh是无视它们的。当异常处理函数指针指向这些地址范围时候,是不对其进行有效性验证的。所以我们可以通过在这些模块找到跳转指令就可以绕过SafeSeh。
6.利用Adobe Flash Player ActiveX控件绕过SafeSeh 【推荐指数:1 】
10.绕过GS技术
10.1.原理
针对缓冲区溢出覆盖函数返回地址这一特征,微软在编译程序时候使用了一个很酷的安全编译选项—GS。/GS 编译选项会在函数的开头和结尾添加代码来阻止对典型的栈溢出漏洞(字符串缓冲区)的利用。当应用程序启动时,程序的 cookie(4 字节(dword),无符号整型)被计算出来(伪随机数)并保存在 加载模块的.data 节中,在函数的开头这个 cookie 被拷贝到栈中,位于 EBP 和返回地址的正前方(位于返回地址和局部变量的中间)。
[局部变量][cookie][保存的EBP][保存的返回地址][参数]
在函数的结尾处,程序会把这个 cookie 和保存在.data 节中的 cookie 进行比较。 如果不相等,就说明进程栈被破坏,进程必须被终止。
2.编译选项 微软在VS2003以后默认启用了GS编译选项。本文使用VS2010。GS编译选项可以通过菜单栏中的项目—配置属性—C/C++ --代码生成—缓冲区安全检查设置开启GS或关闭GS。
10.2.绕过
1.通过同时替换栈中和.data 节中的cookie 来绕过。【不推荐】
2.利用未被保护的缓冲区来实现绕过
3.通过猜测/计算出cookie 来绕过【不推荐】
4.基于静态cookie的绕过【如果cookie每次都是相同的】
5.覆盖虚表指针 【推荐】 (1)覆盖栈中虚表指针,指向一个非ASLR模块指定地址 (2)在非ASLR指定地址中填入指向shellcode的地址
6.利用异常处理器绕过 【推荐】
11.绕过ASLR技术
11.1.原理
1.关于ASLR的一些基础知识
ASLR(Address space layout randomization)是一种针对缓冲区溢出的安全保护技术,通过对堆、栈、共享库映射等线性区布局的随机化,通过增加攻击者预测目的地址的难度,防止攻击者直接定位攻击代码位置,达到阻止溢出攻击的目的。
2.如何配置ASLR
ASLR的实现需要程序自身的支持和操作系统的双重支持。在Windows Vista后ASLR开始发挥作用。同时微软从VS2005 SP1开始加入/dyanmicbase链接选项帮助程序启用ASLR。
11.2.绕过
1.利用部分覆盖定位内存地址 首先,在开始之前。我想告诉你一件事:ASLR 只是随机了地址的一部分,如果你重启后观察加载的模块基地址你会注意到,当一个地址保存在内存中,例如:0x12