之前分享过
《未知网络服务分析之调试技巧》
http://scz.617.cn:8/unix/201812111322.txt
后来有一些小更新,本篇内容已合并至前文尾部。
在某Docker环境中用gdb调试某进程,该进程有网络通信操作,想用"catch syscall read/recvfrom"之类的断点寻找读取网络数据的代码所在,始终无法命中,但通过其他技术手段已经确认网络通信进行中。
目标进程稍显复杂,转用nc进行测试。在该Docker环境中启动"nc -4 -l 12345",gdb attach上去,"catch syscall read/recvfrom",远程访问12345/TCP,前述断点无法命中。"info proc mappings"确认nc和libc所在,IDA反汇编之,Rebase基址。在nc中注意到对__recvfrom_chk()、__read_chk()的调用,在libc中反汇编查看__read_chk(),最终会触发"syscall read"。在__read_chk()中syscall指令之后设普通断点,能命中,"db $rsi $rax"正是客户端发送的数据。
就该环境中的nc而言,"b *read"并不会命中,该版本nc直接调__read_chk(),没有调read(),而__read_chk()直接"syscall read",并不会调read(),二者关系如下:
read
__read_nocancel
sys_read // moe eax, 0
__read_chk
sys_read // xor eax, eax
本来用"catch syscall read"是想更底层些,避免libc或其他封装导致拦截不全,上面的read()、__read_chk()就是很好的示例。但前述测试过程表明此环境中gdb有BUG,"catch syscall read"应该命中,但未命中。若gdb无BUG,"catch syscall read"把这些乱七八糟的封装都覆盖了,谁知gdb有BUG,坑得我死去活来。
未去找BUG源头何在,记录在此以作提醒。就是说,当你碰上不可解释的现象时,还得考虑调试器BUG这种可能性,别较死劲儿。
对于"catch syscall read"有BUG的情况,若直接断read()、__read_chk()均无命中,只能手工去找对"syscall read"的其他封装函数,这不太容易。首先要确认目标环境中sys_read的系统调用号,比如:
$ grep -r __NR_read /usr/include
...
/usr/include/asm/unistd_64.h:#define __NR_read 0
...
然后确定将eax置0的代码,可能性太多,比如最直白的:
$ rasm2 -a x86 -b 64 -s intel -o 0 "mov eax,0;xor eax,eax;mov rax,0;xor rax,rax;syscall"
b80000000031c048c7c0000000004831c00f05
$ rasm2 -a x86 -b 64 -s intel -o 0 -D b80000000031c048c7c0000000004831c00f05
0x00000000 5 b800000000 mov eax, 0
0x00000005 2 31c0 xor eax, eax
0x00000007 7 48c7c000000000 mov rax, 0
0x0000000e 3 4831c0 xor rax, rax
0x00000011 2 0f05 syscall
最后在IDA中Alt-B搜:
b8 00 00 00 00 0f 05
31 c0 0f 05
48 c7 c0 00 00 00 00 0f 05
48 31 c0 0f 05
若eax置0与syscall不紧挨着,这样是搜不到的,当然一般会紧挨着。无论如何,事情到了这一步,全凭人品,没有定数。反过来这也说明"catch syscall"多么有用,若无BUG应多用之。
本文始发于微信公众号(青衣十三楼飞花堂):未知网络服务分析之调试技巧(续)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论