1.GDB调试程序的源码原理核心技术-ptrace系统调用与使用示例
2.Hikari源码分析 - AntiDebug
3.APP安全加固怎么做?加固技术、加固方法、源码原理加固方案
4.C语言调试的源码原理作用C语言调试器是如何工作的
5.手把手教你写一个GDB(基本功能~)
6.程序调试利器GDB – 使用指南
GDB调试程序的核心技术-ptrace系统调用与使用示例
当程序遭遇bug时,GDB调试是源码原理常用手段。本文并非详述GDB的源码原理使用,而是源码原理冒险岛gm小助手源码聚焦于其核心技术——ptrace系统调用。Linux的源码原理ptrace功能强大,允许深入调试进程。源码原理首先,源码原理我们来理解ptrace系统调用,源码原理它是源码原理Linux内核提供的进程调试工具。
系统调用ptrace的源码原理定义如下,它提供了多种调试手段,源码原理例如追踪进程的源码原理寄存器值。深入学习需要了解进程调度、源码原理内存管理和信号处理等基础知识。
通过实例,我们可以看到如何使用ptrace获取子进程的寄存器值,这涉及进程的追踪和操作请求,如PTRACE_TRACEME和PTRACE_GETREGS。在X CPU架构下,Linux内核的ptrace实现位于特定的源代码文件中。
具体操作如PTRACE_TRACEME让进程进入追踪模式,当进程执行exec()函数时,会暂停并发送SIGCHLD信号给父进程,随后父进程可以开始调试。获取内存数据(如PTRACE_PEEKTEXT和PTRACE_PEEKDATA)涉及内存映射和访问过程。
此外,单步调试(PTRACE_SINGLESTEP)则通过设置CPU的Trap Flag实现,每执行一条指令都会暂停并触发SIGTRAP信号。在单步模式下,父进程可以在信号处理中进行各种操作,包括继续执行被调试进程。
总的来说,ptrace是GDB调试的强大工具,它通过系统调用来实现对进程的深入监控和干预。尽管本文未能详尽阐述所有功能,但提供了对ptrace核心功能的初步了解,其余内容则需要读者进一步阅读源代码来深入研究。
Hikari源码分析 - AntiDebug
一、框架分析 针对PASS的具体实现进行深入分析。该PASS旨在提升编译后程序的抵抗调试能力,其核心逻辑包括两个主要方面: 链接预编译的反调试IR代码 特定于平台的内联汇编注入 针对Darwin操作系统上的AArch架构,若未找到ADBCallBack和InitADB函数,PASS会尝试直接注入内联汇编代码。该代码片段可能利用系统调用,如ptrace,zookeeper 事务 源码来检测是否处于调试环境。 此外,配置允许用户指定预编译反调试IR文件的路径和函数混淆概率。 具体实现包括: 检查预编译IR路径,构建默认路径并链接预编译的IR文件。 修改ADBCallBack和InitADB函数属性,确保它们在编译和链接阶段表现出反调试行为。 初始化标志和目标三元组信息,准备为每个模块提供初始化和链接预编译IR的过程。 模块处理和函数处理涉及应用概率值来决定是否对模块和函数应用反调试混淆。 预编译的反调试IR文件包含了一系列用于反调试的函数和结构,如检测调试器的代码、修改执行路径以规避调试跟踪、以及插桩代码以检测异常行为。 通过LLVM工具链中的llvm-dis工具,可以将.bc文件转换为可读的LLVM IR文件。该文件结构包含多个结构体定义、全局声明、函数实现和属性。 函数ADBCallBack简单地终止程序并执行无法到达的指令。函数InitADB执行系统调用和检查来检测调试状态,可能涉及进程信息查询、动态库加载、系统调用、内存分配、异常端口检查等操作。 系统调用声明确保了程序能调用各种底层函数进行操作,如sysctl、dlopen、dlsym、task_get_exception_ports、isatty、ioctl等。 总结,通过在编译器优化阶段插入反调试逻辑,相较于源代码实现,基于LLVM Pass的AntiDebug方法提供了更好的隐蔽性、可移植性、灵活性、维护性和混淆程度。然而,这种方法需要对LLVM框架有深入理解,可能增加构建和调试复杂度。APP安全加固怎么做?加固技术、加固方法、加固方案
本文将深入探讨如何对APP进行安全加固,dsy指标源码以确保其在复杂的移动环境中免受威胁。首先,我们需了解安全检测的重要内容,包括源码和运行环境的保护措施。 Android安全加固技术主要包括:源码加固:如使用dex文件加壳保护和函数抽取加密,SO库加固则涉及文件加壳、深度混淆和数据隐藏等。
资源文件加固:包括音视频加密、配置文件和数据库的加密。
运行环境加固:如完整性保护(签名、防二次打包)、防调试(双向ptrace、反IDAPro)和防篡改(防破解分析)等。
iOS加固技术则侧重于高级混淆、字符串加密、指令多样化和安全防护SDK等。 为了全面防护,建议采用一个综合的安全加固方案,涵盖静态层面(防逆向、签名保护)、动态层面(防调试)和数据层面(数据防泄漏)的保护,同时关注业务场景的加固,如安全键盘、防界面劫持和通信协议加密。 在整个APP生命周期中,安全加固应从需求、设计、实现和交付阶段逐一考虑。例如,在需求阶段明确安全需求,设计阶段使用安全SDK,实现阶段进行安全编码培训和检测,交付阶段进行渠道监测和威胁感知。安全检测应涵盖事前检测、事中加固和事后评估。 通过全面的策略和工具,如静态代码扫描工具,可以有效提升APP的安全性。记住,软件安全是持续的过程,持续关注和优化是关键。更多深入内容,欢迎关注我的专栏:性能测试
安全测试
软件测试CNAS认证
功能测试
新兴技术测试
让我们一起探索和提升APP的安全防护能力。(谢绝转载,持续关注获取更多信息)C语言调试的作用C语言调试器是如何工作的
C语言调试的作用,C语言调试器是中大期货 源码如何工作的很多人还不知道,现在让我们一起来看看吧!
C语言调试器是如何工作的
当你用GDB 的时候,可以看到它完全控制了应用程序进程。当你在程序运行的时候用 Ctrl + C,程序的运行就能够终止,而GDB能展示它的当前地址、堆栈跟踪信息之类的内容。你知道C语言调试器是如何工作的吗?下面是小编为大家带来的关于C语言调试器是如何工作的的知识,欢迎阅读。
但是它们怎么不工作呢?
开始,让我们先研究它怎样才会不工作。它不能通过阅读和分析程序的二进制信息来模拟程序的运行。它其实能做,而那应该能起作用(Valgrind 内存调试器就是这样工作的),但是这样的话会很慢。Valgrind会让程序慢倍,但是GDB不会。它的工作机制与Qemu虚拟机一样。
所以到底是怎么回事?黑魔法?……不,如果那样的话就太简单了。
另一种猜想?……?破解!是的,这里正是这样的。操作系统内核也提供了一些帮助。
首先,关于Linux的进程机制需要了解一件事:父进程可以获得子进程的附加信息,也能够ptrace它们。并且你可以猜到的是,调试器是被调试的进程的父进程(或者它会变成父进程,在Linux中进程可以将一个进程变为自己子进程:-))
Linux Ptrace API
Linux Ptrace API 允许一个(调试器)进程来获取低等级的其他(被调试的)进程的信息。特别的,这个调试器可以:
读写被调试进程的内存 :PTRACE_PEEKTEXT、PTRACE_PEEKUSER、PTRACE_POKE……
读写被调试进程的CPU寄存器 PTRACE_GETREGSET、PTRACE_SETREGS
因系统活动而被提醒:PTRACE_O_TRACEEXEC, PTRACE_O_TRACECLONE, PTRACE_O_EXITKILL, PTRACE_SYSCALL(你可以通过这些标识区分exec syscall、clone、exit以及其他系统调用)
控制它的执行:PTRACE_SINGLESTEP、PTRACE_KILL、PTRACE_INTERRUPT、PTRACE_CONT (注意,CPU在这里是单步执行)
修改它的信号处理:PTRACE_GETSIGINFO、PTRACE_SETSIGINFO
Ptrace是如何实现的?
Ptrace的实现不在本文讨论的范围内,所以我不想进一步讨论,只是简单地解释它是如何工作的(我不是内核专家,如果我说错了请一定指出来,并原谅我过分简化:-))
Ptrace 是Linux内核的一部分,所以它能够获取进程所有内核级信息:
读写数据?跟庄必胜 源码Linux有copy_to/from_user。
获取CPU寄存器?用copy_regset_to/from_user很轻松(这里没有什么复杂的,因为CPU寄存器在进程未被调度时保存在Linux的struct task_struct *调度结构中)。
修改信号处理?更新域last_siginfo
单步执行?在处理器出发执行前,设置进程task结构的right flag(ARM、x)
Ptrace是在很多计划的操作中被Hooked(搜索 ptrace_event函数),所以它可以在被询问时(PTRACE_O_TRACEEXEC选项和与它相关的),向调试器发出一个SIGTRAP信号。
没有Ptrace的系统会怎么样呢?
这个解释超出了特定的Linux本地调试,但是对于大部分其他环境是合理的。要了解GDB在不同目标平台请求的内容,你可以看一下它在目标栈里面的操作。
在这个目标接口里,你可以看到所有C调试需要的高级操作:
struct target_ops
{
struct target_ops *beneath;
/* To the target under this one. */
const char *to_shortname;
/* Name this target type */
const char *to_longname;
/* Name for printing */
const char *to_doc;
/* Documentation. Does not include trailing
newline, and starts with a one-line descrip-
tion (probably similar to to_longname). */
void (*to_attach) (struct target_ops *ops, const char *, int);
void (*to_fetch_registers) (struct target_ops *, struct regcache *, int);
void (*to_store_registers) (struct target_ops *, struct regcache *, int);
int (*to__breakpoint) (struct target_ops *, struct gdbarch *,
struct bp_target_info *);
int (*to__watchpoint) (struct target_ops *,
CORE_ADDR, int, int, struct expression *);
}
普通的GDB调用这些函数,然后目标相关的组件再实现它们。(概念上)这是一个栈,或者一个金字塔:栈顶的是非常通用的,比如:
系统特定的Linux
本地或远程调试
调试方式特定的(ptrace、ttrace)
指令集特定的(Linux ARM、Linux x)
那个远程目标很有趣,因为它通过一个连接协议(TCP/IP、串行端口)把两台“电脑”间的执行栈分离开来。
那个远程的部分可以是运行在另一台Linux机器上的'gdbserver。但是它也可以是一个硬件调试端口的界面(JTAG) 或者一个虚拟的机器管理程序(比如 Qemu),并能够代替内核和ptrace的功能。那个远程根调试器会查询管理程序的结构,或者直接地查询处理器硬件寄存器来代替对OS内核结构的查询。
想要深层次学习这个远程协议,Embecosm 写了一篇一个关于不同信息的详细指南。Gdbserver的事件处理循环在这,而也可以在这里找到Qemu gdb-server stub 。
总结一下
我们能看到ptrace的API提供了这里所有底层机制被要求实现的调试器:
获取exec系统调用并从调用的地方阻止它执行
查询CPU的寄存器来获得处理器当前指令以及栈的地址
获取clone或fork事件来检测新线程
查看并改变数据地址读取并改变内存的变量
但是这就是一个调试器的全部工作吗?不,这只是那些非常低级的部分……它还会处理符号。这是,链接源程序和二进制文件。被忽视可能也是最重要的的一件事:断点!我会首先解释一下断点是如何工作的,因为这部分内容非常有趣且需要技巧,然后回到符号处理。
断点不是Ptrace API的一部分
就像我们之前看到的那样,断点不是ptrace API的一部分。但是我们可以改动内存并获取被调试的程序信号。你看不到其中的相关之处?这是因为断点的实现比较需要技巧并且还要一点hack!让我们来检验一下如何在一个指定的地址设置一个断点。
1、这个调试器读取(ptrace追踪)存在地址里的二进制指令,并保存在它自己的数据结构中。
2、它在这个位置写入一个不合法的指令。不管这个指令是啥,只要它是不合法的。
3、当被调试的程序运行到这个不合法的指令时(或者更准确地说,处理器将内存中的内容设置好时)它不会继续运行(因为它是不合法的)。
4、在现代多任务系统中,一个不合法的指令不会使整个系统崩溃掉,但是会通过引发一个中断(或错误)把控制权交回给系统内核。
5、这个中断被Linux翻译成一个SIGTRAP信号,然后被发送到处理器……或者发给它的父进程,就像调试器希望的那样。
6、调试器获得信号并查看被调试的程序指令指针的值(换言之,是陷入 trap发生的地方)。如果这个IP地址是在断点列表中,那么就是一个调试器的断点(否则就是一个进程中的错误,只需要传过信号并让它崩溃)。
7、现在,那个被调试的程序已经停在了断点,调试器可以让用户来做任何他/她想要做的事,等待时机合适继续执行。
8、为了要继续执行,这个调试器需要 1、写入正确的指令来回到被调试的程序的内存; 2、单步执行(继续执行单个CPU指令,伴随着ptrace 单步执行); 3、把非法指令写回去(使得这个执行过程下一次可以再次停止) ;4、让这个执行正常运行
很整洁,是不是?作为一个旁观的评论,你可以注意到,如果不是所有线程同时停止的话这个算法是不会工作的(因为运行的线程可能会在合法的指令出现时传出断点)。我不会详细讨论GDB是如何解决这个问题的,但在这篇论文里已经说得很详细了:使用GDB不间断调试多线程程序。简要地说,他们把指令写到内存中的其他地方,然后把那个指令的指针指向那个地址并单步执行处理器。但是问题在于一些指令是和地址相关的,比如跳转和条件跳转……
处理符号和调试信息
现在,让我们回到信号和调试信息处理。我没有详细地学习这部分,所以只是大体地说一说。
首先,我们是否可以不使用调试信息和信号地址来调试呢?答案是可以。因为正如我们看到过的那样,所有的低级指令是对CPU寄存器和内存地址来操作的,不是源程序层面的信息。因此,这个到源程序的链接只是为了方便用户。没有调试信息的时候,你看程序的方式就像是处理器(和内核)看到的一样:二进制(汇编)指令和内存字节。GDB不需要进一步的信息来把二进制信息翻译成CPU指令:
(gdb) x/x $pc # heXadecimal representation
0xc: 0x 0x 0xf 0xfd
0xc: 0xa8ec 0x8b 0x8be 0x
0xc: 0x 0x
(gdb) x/i $pc # Instruction representation
=> 0xc: push %r
0xc: push %r
0xc: push %r
0xc: push %r
0xc: mov %rsi,%r
0xc6b: push %rbp
0xc6c: mov %edi,%ebp
0xc6e: push %rbx
0xc6f: sub $0x3a8,%rsp
0xc: mov (%rsi),%rdi
现在,如果我们加上调试信息,GDB能够把符号名称和地址配对:
(gdb) $pc
$1 = (void (*)()) 0xc
你可以通过 nm -a $file 来获取ELF二进制的符号列表:
nm -a /usr/lib/debug/usr/bin/ls.debug | grep " main"
c T main
GDB还会能够展示堆栈跟踪信息(稍后会详细说),但是只有感兴趣的那部分:
(gdb) where
#0 write ()
#1 0xde3 in _IO_new_file_write ()
#2 0xde4c in new_do_write ()
#3 _IO_new_do_write ()
#4 0xd in _IO_new_file_overflow ()
#5 0xbb in print_current_files ()
#6 0xb in main ()
我们现在有了PC地址和相应的函数,就是这样。在一个函数中,你将需要对着汇编来调试!
现在让我们加入调试信息:就是DWARF规范下的gcc -g选项。我不是特别熟悉这个规范,但我知道它提供的:
地址到代码行和行到地址的配对
数据类型的定义,包括typedef和structure
本地变量和函数参数以及它们的类型
$ dwarfdump /usr/lib/debug/usr/bin/ls.debug | grep ce4
0xce4 [, 0] NS
$ addr2line -e /usr/lib/debug/usr/bin/ls.debug 0xce4
/usr/src/debug/coreutils-8./src/ls.c:
试一试dwarfdump来查看二进制文件里嵌入的信息。addr2line也能用到这些信息:
很多源代码层的调试命令会依赖于这些信息,比如next命令,这会在下一行的地址设置一个断点,那个print命令会依赖于变量的类型来输出(char、int、float,而不是二进制或十六进制)。
最后总结
我们已经见过调试器内部的好多方面了,所以我只会最后说几点:
这个堆栈跟踪信息也是通过当前的帧是向上“解开(unwinded)”的($sp和$bp/#fp),每个堆栈帧处理一次。函数的名称和参数以及本地变量名可以在调试信息中找到。
监视点(<code>watchpoints)是通过处理器的帮助(如果有)实现的:在寄存器里标记哪些地址应该被监控,然后它会在那内存被读写的时候引发一个异常。如果不支持这项功能,或者你请求的断点超过了处理器所支持的……那么调试器就会回到“手动”监视:一个指令一个指令地执行这个程序,并检查是否当前的操作到达了一个监视点的地址。是的,这很慢!
反向调试也可以这样进行,记录每个操作的效果,并反向执行。
条件断点是正常的断点,除非在内部,调试器在将控制权交给用户前检查当前的情况。如果当前的情况不满足,程序将会默默地继续运行。
还可以玩gdb gdb,或者更好的(好多了)gdb --pid $(pid of gdb),因为把两个调试器放到同一个终端里是疯狂的:-)。还可以调试系统:
qemu-system-i -gdb tcp::
gdb --pid $(pidof qemu-system-i)
gdb /boot/vmlinuz --exec "target remote localhost:"
手把手教你写一个GDB(基本功能~)
本文将为你详细解读如何使用GDB进行Linux内核的基本调试,从入门到实践,一步步教你掌握这一强大的工具。首先,GDB中的ptrace系统调用是关键,它允许我们控制和观察其他进程的执行。 通过GDB,你可以:调试可执行文件,理解程序的运行流程和逻辑。
创建被调试子进程,并加载你想要监控的程序。
当程序暂停时,利用GDB的命令行发送调试指令,深入检查变量值、调用栈等信息。
深入理解Linux内核架构和工作原理是学习GDB的基础,这包括理解五大模块和整体设计。相关学习资源丰富,例如《Linux内核五大模块内核源码详解》和《全网独一无二Linux内核Makefile系统文件详解》等书籍可以提供深入的知识支持。 同时,加入我们的技术交流群,可以获取更多学习资料,包括书籍、视频和面试题等福利。前名加入者还有额外的奖励,是提升内核调试技能的宝贵途径。 学习Linux内核和GDB的旅程并不孤单,一步步实践,你将能够掌握这个强大的调试工具,为你的编程和内核研究打开新世界的大门。程序调试利器GDB – 使用指南
GDB,GNU Debugger的强大工具,可在程序运行时深入分析其行为。它支持多种编程语言,如C、C++、D、Go、Object-C等,并对无源码程序和第三方库问题有卓越的调试能力。使用GDB,你能够:检查崩溃原因、实时监控变量、设置中断点和追踪内存变化,尤其在测试、集成和发布阶段,GDB的价值超越了日志记录。 尽管GDB在内存泄露和性能优化辅助上有限,但它并非用于这些问题的检测工具。GDB也无法运行编译有问题的程序或解决编译问题。以下是GDB的使用步骤和注意事项: 安装:在Debian系统上,可通过apt-get;源代码安装时,需从官网下载并编译。 在Docker容器中,可能需要特权模式以修改ptrace权限和设置核心文件生成位置。 生成调试符号表:编译时使用-ggdb选项,保持符号表与二进制文件对应。 恢复会话:使用screen保持调试会话的连续性,即使终端关闭也能恢复。 启动方式多样:包括加载程序、附加到进程、分析core文件等。 实战应用:通过Hello World示例,学习如何设置断点,排查Segmentation Fault和程序阻塞问题。 利用GDB深入问题:如数据篡改、内存重复释放的定位和修复。 总结来说,GDB是解决复杂编程问题的强大武器,但要充分利用其优势,还需结合对其他库的深入理解。GDB的工作原理及skyeye远程调试
GDB,即GNU调试器,是UNIX及类UNIX系统中一款强大的命令行调试工具。GDB的工作原理主要基于ptrace系统调用,通过该系统调用,GDB能够观察和控制目标进程的执行,检查和修改其核心映像以及寄存器。ptrace系统调用提供了一种方法,允许父进程监视和控制其他进程,实现断点调试和系统调用跟踪。
ptrace函数的关键参数包括PTRACE_TRACEME、PTRACE_ATTACH、PTRACE_CONT等。PTRACE_TRACEME选项是由子进程调用的,表示子进程将被父进程跟踪,使得所有信号(除SIGKILL外)在被忽略时也会使子进程停止,父进程可通过wait()函数获取此信息。PTRACE_ATTACH选项则用于附加到指定进程,使其成为当前进程跟踪的子进程,尽管当前进程成为了被跟踪进程的父进程,但被跟踪进程通过getppid()获取的仍是原始父进程的pid。PTRACE_CONT选项用于继续运行之前停止的子进程。
GDB提供了三种调试方式:attach并调试已运行的进程、运行并调试新进程以及远程调试目标主机上的新进程。在使用GDB调试时,实现基础都是基于信号的,ptrace系统调用建立调试关系后,任何信号(除SIGKILL外)都会被GDB截获,GDB可以对信号进行处理,决定是否将信号交付给目标进程。
GDB的单步调试功能包括next、step、stepi和nexti命令,分别实现源代码级单步、严格指令集单步以及跨越函数调用的单步。在单步过程中遇到函数调用时,step命令将停止在子函数起始处,而next命令和nexti命令则不会跨越子函数。
断点功能允许用户在指定位置插入断点,目标进程运行至断点时产生SIGTRAP信号,由GDB捕获并确定是否命中断点。断点插入的目标进程和清除的时间点由GDB控制,确保了正确的调试体验。
SkyEye作为一款指令级别仿真平台,支持多种处理器体系架构,包括ARM、TI DSP、PowerPC、X、SPARC、龙芯和飞腾。SkyEye通过与GDB结合,实现远程调试,提供命令行工具和界面形式的调试功能。通过SkyEye,用户可以进行断点设置、单步执行、查看变量和寄存器信息等操作,同时支持指令集调试。调试信息直接展示在控制台中,实现高效的远程调试体验。