软件调试-PE & call convention
单步:Flags寄存器中的标志位,CPU每执行一条指令后会检查IF位值。如为1则产生调试异常。
断点:使用INT 3替代目标指令头,执行时候则产生调试异常切换到调试器,而后返回时恢复
分支踪迹存储:记录最后一次JMP的地址,LastBranchRecording
系统中对对应的中断向量表,当中断产生时,PUSH现场,查询对应表中对应序号的地址,赋予CS:IP,使之执行中断程序,而后恢复现场。表中包含了中断与异常(软中断)。
软件同时运行在用户空间、内核空间(前者主要包括CPU,GPU计算,后者包括IO,系统时间等获取),故调试时候会发生模式切换:软中断与快速系统调用
快速系统调用与软中断相似,跳过了执行中断前的一系列权限检查
用户态调试:调试器程序,被调试进程,调试子系统(事件驱动)
进程的虚拟内存空间:共享的系统内容与独立的进程内存
进程&线程:进程控制块/进程环境块;线程内核对象/线程环境块
栈与函数调用
每个线程都至少有一个栈,系统线程外的有两个栈:内核空间与用户空间
调用过程与约定
Push P1 Push P2 Call Func1 // push IP + JMP Func1 Push BP Push Var1 Push Var2 POP Var2, Var1, BP, IP, P2, P1 |
C约定调用,Pop P2, P1 与caller执行,接受可变参数 标准调用, callee销毁P2,P1;该过程指令不必反复出现在caller中 快速调用, 部分参数通过寄存器传递,速度快 |
以上可能发生栈的溢出与下溢
堆的分配过程:
进程空间中的堆 -----》堆管理器 ------》内存管理器
堆器管理器维护着堆表,进程释放对空间时,返回给堆管理器(标为未使用);内存管理器为堆管理器请求分配内存空间
code
#include <stdlib.h> #include <stdio.h> int __cdecl func2(int a, int b, int c, int d) { int f = a + b; int e = c * d; return f+e; } int __stdcall func1(short a, long b) { int f = a + b; return f; } int __fastcall func3(short a, int b, int c, int d) { int f = a + b; int e = c * d; return f + e; } int main() { int i = 0; int a, b, c, d, f; long aa = 12; a = 3, b = 4, c = 5, d = 6; if (i = 0) i += 1; for (; i < 5; i++) {} // call convention f = func2(a, b, c, d); f = func1(a, aa); f = func3(a,b,c,d); return 0; }