gdb是支持脚本的,不过他支持的是命令序列。
网上的这样的文章也不多。我在这里就来个gdb脚本反反调试的实验。
实际上要实现起来也不是很难。ok!我们在原来的基础上多加几次反调试的语句。但并没有增加技术上难度。
我在这里体现的不是反调试的方法而只是如何利用shell script和gdb script 配合进行调试。学习进行中.....
首先还是给出被测试程序的源代码。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int get_name_by_pid(pid_t pid, char* name)
{
int fd;
char buf[1024] = {0};
snprintf(buf, 1024, "/proc/%d/cmdline", pid);
if ((fd = open(buf, O_RDONLY)) == -1)
return -1;
read(fd, buf, 1024);
strncpy(name, buf, 1023);
return 0;
}
int check_user()
{
char *password="123",user[5]="";
printf("password:");
scanf("%s",user);
if(!strcmp(password,user))
{printf("success!\n");return 0;}
else
{printf("Error!\n");return -1;}
}
int main(void)
{
char name[1024];
pid_t ppid = getppid();
if (get_name_by_pid(ppid, name))
return -1;
if (strcmp(name,"bash")==0||strcmp(name,"init")==0)
{
pid_t ppid = getppid();
if (get_name_by_pid(ppid, name))
return -1;
if (strcmp(name,"bash")==0||strcmp(name,"init")==0)
{
check_user();
}
else if (strcmp(name,"gdb")==0 || strcmp(name,"strace")==0 || strcmp(name,"ltrace") == 0)
printf("Traced!\n");
else
printf("Unknown! Maybe traced!\n");
}
else if (strcmp(name,"gdb")==0 || strcmp(name,"strace")==0 || strcmp(name,"ltrace") == 0)
printf("Traced!\n");
else
printf("Unknown! Maybe traced!\n");
return 0;
}
源码没有很大变化,只是在检测getppid次数上增加一次。用来说明GDB script的自动调试方法
gdb 的脚本的使用方法也很简单直接在gdb的命令行中输入source filename
脚本的内容
echo ----------welcome useing MyScript--------------\n
echo --------------Gdb Script ----------------------\n
echo -----------------------------------Beijihu-----\n
r
b getppid
commands
rep
end
define rep
next
shell echo `ps -A |grep "bash"` >./gdb.Temp
shell echo 'print $eax=arr' >./anti.temp
shell sed -i s@arr@`awk -F' ' '{ print $1}' ./gdb.Temp`@g ./anti.temp
source ./anti.temp
shell rm ./*.temp
c
end
详细解释一下具体命令的含义:
---------------------------------beijihu's split-line------------------------------------------------------
r #第一次运行。这个上次说为什么不知道这样做的原因大概是因为第一次运行需要加载器解决程序的共享函数库的重定向的问题
b getppid #在 getppid 函数调用下断
commands #定义 getppid 断点的调用命令序列
rep #自定义的命令
end
define rep #定义一个命令序列
next #步过,相当于OD的F8
shell echo `ps -A |grep "bash"` >./gdb.temp
#Shell 命令
shell echo 'print $eax=arr' >./anti.temp
#shell 命令临时 建立一个gbd script
shell sed -i s@arr@`awk -F' ' '{ print $1}' ./gdb.temp`@g ./anti.temp
#shell 命令 置换arr为bash的pid
source ./anti.temp
#执行临时建的gdb脚本。实际上就是一个 print $eax=pid
shell rm ./*.temp
#清理现场
c #继续运行
end #命令序列结束
---------------------------------beijihu's split-line------------------------------------------------------
脚本调用...
(gdb) source anti.gdb
运行脚本后以下输出
----------welcome useing MyScript--------------
--------------Gdb Script ----------------------
-----------------------------------Beijihu-----
(no debugging symbols found)
(no debugging symbols found)
(no debugging symbols found)
Traced!
Program exited normally.
Breakpoint 1 at 0xcc6880
(gdb) r
常用的gdb命令
backtrace 显示程序中的当前位置和表示如何到达当前位置的栈跟踪(同义词:where)
breakpoint 在程序中设置一个断点
cd 改变当前工作目录
clear 删除刚才停止处的断点
commands 命中断点时,列出将要执行的命令
continue 从断点开始继续执行
delete 删除一个断点或监测点;也可与其他命令一起使用
display 程序停止时显示变量和表达时
down 下移栈帧,使得另一个函数成为当前函数
frame 选择下一条continue命令的帧
info 显示与该程序有关的各种信息
jump 在源程序中的另一点开始运行
kill 异常终止在gdb 控制下运行的程序
list 列出相应于正在执行的程序的原文件内容
next 执行下一个源程序行,从而执行其整体中的一个函数
print 显示变量或表达式的值
pwd 显示当前工作目录
pype 显示一个数据结构(如一个结构或C++类)的内容
quit 退出gdb
reverse-search 在源文件中反向搜索正规表达式
run 执行该程序
search 在源文件中搜索正规表达式
set variable 给变量赋值
signal 将一个信号发送到正在运行的进程
step 执行下一个源程序行,必要时进入下一个函数
undisplay display 命令的反命令,不要显示表达式
until 结束当前循环
up 上移栈帧,使另一函数成为当前函数
watch 在程序中设置一个监测点(即数据断点)
whatis 显示变量或函数类型
在使用gdb 调试 程序 的时候,有时候需要设定多个断点,重复执行某些操作,而这些操作写起来比较麻烦,这个时候就应该想起来用gdb命令 脚本了,它能够很好的完成这些工作。
以设置多个断点为例,我写的命令脚本为
---------------------------------------------------
#filename: .gdbinit
#gdb will read it when starting
file test_gdbscript
set args hello
b main
b foo
r
---------------------------------------------------
有两种方式来使用这个脚本:
1)启动gdb时候
gdb在启动的时候,会在当前目录 下查找".gdbinit"这个文件 ,并把它的内容作为gdb命令进行解释,所以如果我把脚本命名为".gdbinit",这样在启动的时候就会处理这些命令。
2)gdb运行期间
可以使用 source script-file 来解释gdb命令脚本script-file
在.gdbinit里面设置了断点,但是如果这些断点在某些动态库里 ,gdb会报错:
Make breakpoint pending on future shared library load? (y or [n]) [answered N; input not from terminal ]
可以这样解决这个问题
"set breakpoint pending on"
(gdb) help set breakpoint pending
Set debugger's behavior regarding pending breakpoints.
If on, an unrecognized breakpoint location will cause gdb to create a
pending breakpoint. If off, an unrecognized breakpoint location results in
an error. If auto, an unrecognized breakpoint location results in a
user-query to see if a pending breakpoint should be created.