LD_PRELOAD
: hook动态链接库函数
LD_PRELOAD
是Linux
系统中的一个环境变量,利用它可以指定在程序运行前优先加载的动态链接库,实现在主程序和其他动态链接库的中间加载自定义的动态链接库,甚至覆盖正常的函数。一般而言,程序启动后会按一定顺序加载动态库:
- 加载
LD_PRELOAD
指定的动态库; - 加载文件
/etc/ld.so.preload
指定的动态库; - 搜索
LD_LIBRARY_PATH
指定的动态库路径; - 搜索路径
/lib64
下的动态库文件。
在对嵌入式设备进行仿真时,经常需要进行环境修复,比如劫持与NVRAM
相关的函数、hook
某些函数使得程序继续运行不崩溃等。以qemu user mode
为例,通过-E
选项指定LD_PRELOAD
环境变量,从而达到上述目的。
1 | sudo chroot . ./qemu-arm-static -E LD_PRELOAD='<custom_lib.so>' <binary_path> arg0 arg1 |
有时,使用LD_PRELOAD
环境变量可能会不起作用,可以采用另一种方式:修改/etc/ld.so.preload
配置文件,指定需要加载的自定义动态链接库。
通常,有2种常见的方式可以让
LD_PRELOAD
失效(上面提到的情况不属于这2种):
- 静态链接
- 设置文件的
setgid/setuid
标志:有SUID
权限的程序,系统会忽略LD_PRELOAD
环境变量
最后,推荐两个常用的用于hook
的第三方库,代码及实现比较优雅,可以直接拿来使用或者参考借鉴:
libnvram
:固件仿真框架Firmadyne
中提供的用于模拟NVRAM
行为的动态库,支持很多常见的api
,同时还会解析固件中自带的一些默认键值对;preeny
:支持很多常见的api
,包括socket
相关、fork()
、alarm()
、rand()
等。
相关链接
- libnvram
- preeny
gdb
命中断点后继续运行
在使用gdb
进行调试时,有时侯想让程序命中断点执行一些操作后继续运行,比如dump
指定内存地址处的内容、记录执行过的基本块地址等。在gdb
中,让程序命中断点执行一些操作后继续运行,常见的方式有三种:
define hook-stop
方式1
2
3
4
5
6gdb
b *0x12345678 # set breakpoint
define hook-stop
x/4wx $esp # custom gdb command
continue
endcommands
命令 +gdb.events
事件1
2
3
4
5
6
7
8
9
10
11
12
13
14# gdb_event.py
# !!! call gdb.execute('continue') in event functions will cause recursive call
def handle_stop_event(event):
if not isinstance(event, gdb.BreakpointEvent):
# do what you want
gdb.events.stop.connect(handle_stop_event)
# gdb
> b *0x12345678
> source gdb_event.py # run python script in gdb
> commands 1 # breakpoint num
continue # custom gdb command
end自定义
gdb.Breakpoint
1
2
3
4
5
6
7
8
9
10# custom_gdb.py
class MyBreakpoint(gdb.Breakpoint):
def stop(self):
# do what you want
return False # continue automatically
MyBreakpoint("*{:#x}".format(0x12345678))
# gdb
> source custom_gdb.py
其中,在hook-stop
中运行continue
命令似乎仅在第一次有效,后续命中断点后还是会停下来;而采用commands + gdb.events
方式有时在多线程中会报异常;采用自定义gdb.Breakpoint
方式是比较推荐的。
另外,如果只是想在命中断点后,打印指定内存地址处的内容,一种更好地方式是直接使用dprtinf
命令,其原理是设置断点(dprintf
类型),然后调用printf
输出,之后再继续运行行。
1 | dprintf location,template,expression[,expression…] |
相关链接
- How to continue the exection after hitting breakpoints in gdb?
- User-defined Command Hooks
- Events In Python
- Manipulating breakpoints using Python
- Dynamic Printf
- gdb events example
IDA
命令行运行idapython
脚本
在IDA
GUI
中可以通过执行idapython
脚本来完成一些特定的工作,如果需要对多个程序执行相同的操作,一种方式是在IDA
GUI
中逐个程序执行对应的脚本,另一种更优雅的方式则是通过IDA
命令行进行自动化批量分析。
以Windows
平台为例,针对单个程序,通过命令行自动执行idapython
脚本的步骤如下:
调用
idat.exe/idat64.exe
对程序进行初始分析,生成对应的idb
文件1
2
3
4
5
6processor type
x86/x86_64: metapc
arm: arm/armb
mips: mipsl/mipsb
PowerPC: ppcl/ppc
"<ida_path>" -A -B -p<processor_type> -o<idb_path> <binary_path>基于生成的
idb
文件,运行对应的自动化脚本1
"<ida_path>" -A -S"<script_path> <arg1> <arg2>" <idb_path>
其中,idapython
脚本中通过ARGV[i]
来获取传递的参数,同时最后通过调用idc.Exit(0)
退出。
1 | arg1 = ARGV[1] |
Linux
平台与Windows
平台类似,但存在细微差别:1) ida
可执行程序变为idal/idal64
;2) 在命令行参数最开始加上TVHEADLESS=1
,最后可加上 > /dev/null
。
1 | TVHEADLESS=1 "<ida_path>" -B -p"<processor_type>" -o"<idb_path>" "<binary_path>" > /dev/null |
附件下载
相关链接
- IDA Help: Command line switches