linux程序无源码调试的方法
序
过段时间可能要逆向linux上的cm, 先将调试方法做个实验, 将菜谱备好.
远程调试有2个方法:
*GDB在linux服务器上直接调试无源码的cm,这个调试起来比较繁琐,即使没有源码,也是按照C语言代码行进行的单步。寄存器值变的太突然,不好理解。
*在windows上用IDA6.8pro远程启动(或附加)调试linux服务器上的cm。这个可以直接单步反汇编代码, 可以通过单步,理解寄存器值的变化。
因为是做实验,我采用的是直接由调试器启动cm.
demo
在本地写好,上传到服务器,编译后,用Makefile中写好的命令,将输出之外文件(main.cpp, Makefile)都删掉, 模拟cm没有源码的实际场景。
demo实现1+..+N的值打印
// @file main.cpp
// @brief test gdb debug the elf that no source
// if debian OS not install gdb, please run below command to install gdb
// apt-get install gdb
// run below command to rebuild project
// make rebuild
// run below command to only stay output file
// make only_stay_output
// ================================================================================
// gdb
// ================================================================================
// now can use gdb to debug ./test_gdb_no_source_debug without source code
// gdb ./test_gdb_no_source_debug
// gdb documentation below
// http://www.gnu.org/software/gdb/documentation/
// 如果命令只记得一部分, 用tab key 补齐命令
// view main funtion addr, because main was export on this Makefile
// (gdb) info line main
// Line 14 of "main.cpp" starts at address 0x4006f6 <main(int, char**)> and ends at 0x400705 <main(int, char**)+15>.
// view main's dasm code
// (gdb) disassemble 0x4006f6
/**
(gdb) disassemble 0x4006f6
Dump of assembler code for function main(int, char**):
0x00000000004006f6 <+0>: push %rbp
0x00000000004006f7 <+1>: mov %rsp,%rbp
0x00000000004006fa <+4>: sub $0x20,%rsp
0x00000000004006fe <+8>: mov %edi,-0x14(%rbp)
0x0000000000400701 <+11>: mov %rsi,-0x20(%rbp)
0x0000000000400705 <+15>: movq $0x0,-0x8(%rbp)
0x000000000040070d <+23>: movl $0x0,-0xc(%rbp)
0x0000000000400714 <+30>: mov $0x0,%edi
0x0000000000400719 <+35>: callq 0x4005f0 <time@plt>
0x000000000040071e <+40>: mov %eax,%edi
0x0000000000400720 <+42>: callq 0x4005e0 <srand@plt>
0x0000000000400725 <+47>: movl $0xa,-0xc(%rbp)
0x000000000040072c <+54>: mov -0xc(%rbp),%eax
0x000000000040072f <+57>: mov %eax,%edi
0x0000000000400731 <+59>: callq 0x400768 <Add1ToN(int)>
0x0000000000400736 <+64>: mov %rax,-0x8(%rbp)
0x000000000040073a <+68>: mov -0x8(%rbp),%rdx
0x000000000040073e <+72>: mov -0xc(%rbp),%eax
0x0000000000400741 <+75>: mov %eax,%esi
0x0000000000400743 <+77>: mov $0x400824,%edi
0x0000000000400748 <+82>: mov $0x0,%eax
0x000000000040074d <+87>: callq 0x400590 <printf@plt>
0x0000000000400752 <+92>: mov $0x400837,%edi
0x0000000000400757 <+97>: callq 0x4005b0 <puts@plt>
0x000000000040075c <+102>: callq 0x4005d0 <getchar@plt>
0x0000000000400761 <+107>: mov $0x0,%eax
0x0000000000400766 <+112>: leaveq
0x0000000000400767 <+113>: retq
End of assembler dump.
*/
// set bp on main function's first code line
// (gdb) break main
// set bp on addr
// (gdb) break *xxxxxxxx
// e.g. (gdb) break *0x00000000004006F6
// view bp was set
// (gdb) info breakpoints
// run until break on gdb
// (gdb) run
// when break on gdb, view main's parameter
/**
(gdb) info args
argc = 1
argv = 0x7fffffffebc8
*/
// view current dasm code
// (gdb) disassemble
// step one dasm code line
// (gdb) next
// view register
// (gdb) info registers eax
// (gdb) info registers
// (gdb) info all-registers
// set register
// (gdb) set $rax = 1
// (gdb) set $rax += 1
// (gdb) set $rax -= 1
// view stack
// (gdb) x/8x $rsp
// execute C code line
// (gdb) next
// (gdb) step 1
// quit gdb
// (gdb)quit
// 常见的就这么多了,如果还需要,就再去找
// ================================================================================
// DDD
// ================================================================================
// add one line append to /etc/apt/sources.list
// deb http://ftp.hk.debian.org/debian sid main
// do below
// apt-get update
// do below
// apt-get install ddd
// 更换介质:请把标有
// “Debian GNU/Linux 7.5.0 _Wheezy_ - Official amd64 DVD Binary-1 20140426-13:37
// 升级的还挺多的,下次玩ddd, 这次就用gdb
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
long Add1ToN(int iN);
#define MY_DEBUG
#define NUM_N 10
int main(int argc, char* argv[])
{
long lSum = 0;
int iN = 0;
srand((int)time(NULL));
#ifdef MY_DEBUG
iN = NUM_N;
#else
iN = rand();
#endif
lSum = Add1ToN(iN);
printf("Add1ToN(%d) = %ld\n", iN, lSum);
printf("END, press any key to quit\n");
getchar();
return 0;
}
long Add1ToN(int iN)
{
long lRc = 1;
if (iN > 1) {
do {
lRc += iN;
} while (--iN > 1);
}
return lRc;
}
# ==============================================================================
# makefile
# lostspeed 2017-06-24
# ==============================================================================
CC = g++
CFLAGS = --std=c++11 \
-Wall \
-g
BIN = test_gdb_no_source_debug
INC = -I.
LIBS = -lstdc++ \
-pthread
LIBPATH = /usr/local/lib
SUBDIR = $(shell ls -d */)
ROOTSRC = $(wildcard *.cpp)
ROOTOBJ = $(ROOTSRC:.cpp=.o)
SUBSRC = $(shell find $(SUBDIR) -name '*.cpp')
SUBOBJ = $(SUBSRC:.cpp=.o)
help:
clear
@echo make help
@echo command list:
@echo make clean
@echo make all
@echo make rebuild
@echo make only_stay_output
clean:
clear
@echo make clean
rm -f $(BIN) $(ROOTOBJ) $(SUBOBJ)
ls -l
all:$(BIN)
@echo make all
ls -l
$(BIN) : $(ROOTOBJ) $(SUBOBJ)
$(CC) $(CFLAGS) -o $@ $^ -L$(LIBPATH) $(LIBS)
.cpp.o:
$(CC) -c $(CFLAGS) $^ -o $@ $(INC)
rebuild:
make clean
make all
only_stay_output:
rm *.o
rm *.cpp
rm ./Makefile
ls -l
GDB
上传工程到实验目录 : main.cpp , Makefile
编译程序 :make rebuild
只保留输出文件在实验目录 : make only_stay_output
开始实验时,目录下只有输出文件./test_gdb_no_source_debug
开始用GDB无源码调试demo
先确定main函数位置,也可以用IDA静态分析要下断点的地址。
(gdb) info line main
Line 14 of "main.cpp" starts at address 0x4006f6 <main(int, char**)> and ends at 0x400705 <main(int, char**)+15>.
从main函数开始反汇编,看看应该在哪个地址下断点
(gdb) disassemble 0x4006f6
Dump of assembler code for function main(int, char**):
0x00000000004006f6 <+0>: push %rbp
0x00000000004006f7 <+1>: mov %rsp,%rbp
0x00000000004006fa <+4>: sub $0x20,%rsp
0x00000000004006fe <+8>: mov %edi,-0x14(%rbp)
0x0000000000400701 <+11>: mov %rsi,-0x20(%rbp)
0x0000000000400705 <+15>: movq $0x0,-0x8(%rbp)
0x000000000040070d <+23>: movl $0x0,-0xc(%rbp)
0x0000000000400714 <+30>: mov $0x0,%edi
0x0000000000400719 <+35>: callq 0x4005f0 <time@plt>
0x000000000040071e <+40>: mov %eax,%edi
0x0000000000400720 <+42>: callq 0x4005e0 <srand@plt>
0x0000000000400725 <+47>: movl $0xa,-0xc(%rbp)
0x000000000040072c <+54>: mov -0xc(%rbp),%eax
0x000000000040072f <+57>: mov %eax,%edi
0x0000000000400731 <+59>: callq 0x400768 <Add1ToN(int)>
0x0000000000400736 <+64>: mov %rax,-0x8(%rbp)
0x000000000040073a <+68>: mov -0x8(%rbp),%rdx
0x000000000040073e <+72>: mov -0xc(%rbp),%eax
0x0000000000400741 <+75>: mov %eax,%esi
0x0000000000400743 <+77>: mov $0x400824,%edi
0x0000000000400748 <+82>: mov $0x0,%eax
0x000000000040074d <+87>: callq 0x400590 <printf@plt>
0x0000000000400752 <+92>: mov $0x400837,%edi
0x0000000000400757 <+97>: callq 0x4005b0 <puts@plt>
0x000000000040075c <+102>: callq 0x4005d0 <getchar@plt>
0x0000000000400761 <+107>: mov $0x0,%eax
0x0000000000400766 <+112>: leaveq
0x0000000000400767 <+113>: retq
End of assembler dump.
GDB反汇编出来是AT&T语法,和intel反汇编语法稍有不同(e.g. src,dst位置是倒的; 立即数有$前缀;寄存器值有%前缀;不用[reg+offset],而是用offset(%reg)…), 和intel反汇编语法虽然不同,但是可以看得懂。
挑出要下断点的地址,开始下断点
// set bp on addr
// (gdb) break *xxxxxxxx
// e.g. (gdb) break *0x00000000004006F6
对地址下断点,地址有*前缀。
(gdb) break *0x00000000004006F6
查看下好的断点列表
// view bp was set
// (gdb) info breakpoints
(gdb) info breakpoints
断点下好后,就可以跑了,直到断下为止
// run until break on gdb
// (gdb) run
(gdb) run
当程序断住后,可以查看程序的信息和反汇编代码了。
查看main函数入参
(gdb) info args
argc = 1
argv = 0x7fffffffebc8
查看当前的反汇编
(gdb) disassemble
查看寄存器
// view register
// (gdb) info registers eax
// (gdb) info registers
// (gdb) info all-registers
修改寄存器
// set register
// (gdb) set $rax = 1
// (gdb) set $rax += 1
// (gdb) set $rax -= 1
查看栈
// view stack
// (gdb) x/8x $rsp
单步或步入
// execute C code line
// (gdb) next
// (gdb) step 1
程序调试完成,就可以退出GDB
// quit gdb
// (gdb)quit
很少玩x64调试,一直用OD玩x86,当断住时,想查看寄存器,各种不舒服。
以后多玩玩。
还可以用图形前端ddd, 不过apt-get时,看到要升级很多软件,怕实际调试时,有个GDB用就不错了。就没有继续做ddd的实验。
GDB调试器的使用资料
- gnu官方资料 http://www.gnu.org/software/gdb/documentation/
- CSDN上有好心人上传了”The Art of Debugging with GDB”。
IDA
在linux服务器上运行inux_serverx64
linux_serverx64位置 =>D:\Program Files (x86)\IDA 6.8\dbgsrv\linux_serverx64
将linux_serverx64上传到服务器任意目录运行,linux_serverx64开始等IDA来连接。
运行IDA连接服务器
先用IDA将demo分析好,形成test_gdb_no_source_debug.i64
打开IDA Pro (64-bit) => go
这时服务器上的demo已经在内存里加载好了
结合静态分析找好的地址,下断点,就可以开始调试了。
在IDA中调试demo时,用的是intel语法。
用着IDA调试倒是很方便,不过查看变量时,也是各种不舒服。
更多推荐
所有评论(0)