软件调试相关


c++中vector的越界访问
    1.在vs或windbg中创建调试,可以提前拦截,获取调用栈
    2.在运行中断言出错后,再附加或创建dump文件进行调试,都无法获取调用栈
使用vs创建调试或直接运行.net程序,能够看到托管异常
注意,使用windbg进行创建调试时,进程创建即中断
    需执行继续命令,让进程运行起来,其会自动加载各种程序模块
调试.net程序时
    在程序加载完各种模块(module)后,需要加载扩展模块(.loadby sos clr)(辅助托管代码调试)
    一个示例流程如下
        创建调试,windbg executable
            增加参数-g,则会忽略初始化断点
            不增加参数,则会使用初始化断点,一般需配合g命令使用
        继续命令,g
        加载sos,可查看其帮助!help
        设置符号路径,支持下载和缓存
            .sympath cache*c:\Symbols;srv*https://msdl.microsoft.com/download/symbols
            或.sympath srv*c:\Symbols*https://msdl.microsoft.com/download/symbols
            注意本地路径一定要完全一致
        重新加载符号,.reload
        查看托管栈,!clrstack
        查看托管异常,!pe
Windows远程调试
    = 本地调试 + 网络通信 = [A1|B1] + [C]
    = 远程调试内核 + 通信服务器 + 通信客户端 + 本地调试外壳
    = [A1|] + [C] + [|B2]
vs远程调试
    remotedebugger
        功能,远程调试内核+通信服务器
        位置,msvs xx.x/common7/ide/remotedebugger
        注意
        使用管理员权限运行,以成功开启端口
        运行在调试目标所在系统
    vs
        功能,本地调试外壳+通信客户端
        运行在调试员所操作的系统
程序和符号版本
    同一程序或程序库的源码,可以编译出不同Debug版二进制和Release版二进制
        联系,两者都是二进制程序
        区别
            Debug版,以调试目的构建的
            Release版,以运行目的构建的
    符号的版本
        Private,含较多调试符号,一般用于内部调试
        Public,含较少调试符号,一般用于外部检查
        不生成或不提供,一般为了保护自身的知识产权
        注意
            每个版本的二进制程序,都有private和public两个版本符号文件
    程序模块间的版本依赖
        确保构建时使用的lib和运行时使用的dll,为同一版本
        注意
            debug版本程序的构建和运行,并非一定要使用debug版本的程序库
            而使用debug版本的程序库,主要是为了方便查到程序库中出现的bug
修改软件数据的外挂,可以调试器的方式实现
Windows调用约定
    win32(_stdcall),
        从右至左压栈,被调清栈
    native c++ method call(thiscall)
        继承win32,this放ecx
    com(_stdcall for c++ method call)
        继承win32,this最后压栈,相当于首参
    _fastcall
        从右至左压栈,第二一参分别存入edx和ecx,被调清栈
    _cdel
        从右至左压栈,调者清栈
        会用于参数个数可变的函数
网络资料,关键词 debug tutorial part
visual studio pdb产生设置
    linker>debugging
    optimize for debugging(/DEBUG)
native api
    轻量级api
    用途
        进程启动早期使用
        Windows组件实现
        Windows api实现,kernel32.dll
    实现
        大多api实现在ntoskrnl.exe
    使用
        通过ntdll.dll暴露到用户态
        ntdll.dll的入口点是LdrInitializeThunk
        内核通过system service descriptor table(ssdt),处理api调用
    函数组
        c runtime functions
        Nt或Zw前缀
            定义在ntdll.dll和ntoskrnl.exe中的系统调用
            用户态(user mode,用户空间低权限)调用ntdll.dll
                先陷入内核态(kernel mode,内核空间高权限)
                后通过ssdt调用ntoskrnl.exe中的相同函数
            内核态直接调用ntoskrnl.exe中函数
            Zw variants确保在内核态,Nt variants不确保
            Zw前缀无确定代表
        Rtl前缀
            the (extended) c run-time library
            不需要内核支持
        Csr前缀
            client-server functions
            用于与win32子系统进程通信
            csrss.exe,client/server runtime sub-system
        Dbg前缀
            debugging functions
            如breakpoint
        Ki前缀
            用于事件的内核调用
            如APC Dispatching
        Ldr前缀
            loader functions
            pe文件处理,启动新进程
        Nls前缀
            national languages support
            类似于code page
        Pfx前缀
            prefix handling
        Tp前缀
            threadpool handling
    其他api
    实现在user32.dll和gdi32.dll中,会陷入内核
    原不是Windows NT设计,因为硬件性能原因,才将图形子系统移入内核
    函数前缀为NtUser和NtGdi
vector越界情况分析
    _Debug_message
        若调试运行,发送断点调试事件,第一次机会进入
            break instruction exception - code 8000 0003
        若普通运行,则调用ucrtbased!CrtDbgReportW函数
            终会出现报错窗口,后退出
                此时出错时的调用栈不可观察
    _ASSERTE
        会出现断点调试事件
    _invalid_parameter
        若调试运行,发送调试事件,第二次机会进入
        stack buffer overflow - code c000 0409
内存泄漏检查
    基于crt的内存泄漏问检查
        通过重载内存申请和释放函数,记录未释放的内存
    基于windbg的内存泄漏检查
        定位堆和块
            堆条件,通过crt分配,堆中特征内存块
            AppVerifier工具
            _CrtDumpMemoryLeaks函数
            windbg
        查看分配时调用栈
        注意,需要启用pageheap一些选项
    使用gflags或pageheap设置调试选项
        操作
            使用管理员权限启动cmd,然后设置
            参考windbg帮助文档
        用途1
            内容,暴露内存泄漏
            方法,将内存分配在页尾,将相邻下页置为PAGE_NOACCESS
    内存泄漏检查例子
        gflags /p /enable executable,设置调试选项
        windbg,启动调试
        !heap -p,查看堆概况,需有COLLECT_STACK_TRACES
        查看堆创建时的调用栈
            !heap -p -h heap_address,查看堆详情
            dt ntdll!_DPH_HEAD_ROOT CreateStackTrace root_address
            dps stack_trace_address
            !heap -stat -h 0,查看所有堆的统计信息
            !heap -flt s size,过滤内存块
            !heap -p -a address,查看内存块详情,含申请时的调用栈
ntsd,kd,cdb,windbg的关系
    使用相同的调试引擎和命令
    windbg,支持gui,支持用户态和内核态调试
    ntsd,kd,cdb支持cli
    ntsd,cdb支持用户态调试
    kd支持内核态调试
异常处理过程
    1.  有调试器且完成处理,则完成
    2.  应用程序有异常处理程序且完成处理,则完成
    3.1 有调试器且完成处理,则完成
    3.2 应用程序注册过回调(SetUhandledExceptionFilter)
    3.3 调用注册表中AeDebug指定的调试器
    4.  对于clr,会触发AppDomain的UnhandledException
在c/c++ debug中,可使用
    _CrtDbgReportf发送调试报告
    _CrtSetReportHook指定调试报告的处理函数,如
        自动创建dump文件
        弹窗等待手动创建dump文件,或附加调试器
AeDebug方式
    条件,管理员权限,run as Administrator
    手动设置注册表方式
        查找key为AeDebug的项
        export项到注册表文件
        备份和编辑注册表
        Debugger,指定调试器路径,初始化命令或脚本,可用于创建dump文件
        import注册表文件
    windbg命令行
        -I,设置尸检(postmortem)调试器
调试的方式
    新建调试
    附加调试
    AeDebug触发调试
    dump调试
进程模块
  dll,作为基层模块,无程序入口,可共享,载入位置一般不确定(以实际为准)
  exe,作为顶层模块,有程序入口,不会共享,载入位置一般确定(以实际为准)
Dll调用
    动态调用,由代码负责
        加载和卸载模块
        据实际加载位置动态获取符号地址
    静态调用,由编译器和加载器负责
        加载和卸载模块
        从只读段中,获取符号地址
        一般只读段中,地址变量的符号为_imp_xxx
DLL共享
    Dll中包含代码段、数据段、bss段
    代码段,所有进程间共享
    数据段、bss段,同一进程内所有模块间共享
调用windows API时,若中断入调试器
    在Debug版本程序中,调试器栈回溯异常,手动回溯正常
    在Release版本中正常
常用的call方式
    call address,相对寻址,e8指令码,eip+=address
    call (dword|qword),绝对间接寻址,ff15指令码,eip=[eip+address]
windbg中查看地址相关源码,.open -a address
在不同的层次和角度去观察同一事物,如程序的逻辑结构、程序物理结构、进程
地址
    程序中很常见很重要的一一类数据
    类别
        相对地址,指向不依赖于模块加载位置
        绝对地址,指向依赖于模块加载位置,又分为指向自身模块的绝对地址和指向外部模块的绝对地址
    问题
        由于模块加载位置不确定,绝对地址为了指向的有效性,需要动态调整
    方案
        绝对地址的变化度分离,符号和地址值
        以变量实现
            符号表示为相对地址,指向的地址变量
            地址值表示为地址变量中的变量值
        绝对地址变量组成的列表
            对内的表,由重定位表指向
            对外为IAT
        当模块及其依赖模块加载完成后,更新绝对地址变量表,设定地址数据只读
可执行文件
    COFF中,文件分头部、节头表(含段加载信息),节数据
    ELF中,文件分头部、程序头表(段加载信息)、节头表、节数据
可执行文件的加载测试
    了解PE format
    使用CFF explorer(支持pe)或dumpbin(支持pe和.lib)查看文件数据
    使用windbg查看进程数据
关于IAT
    .idata section中
        含import directory table及其相关附表
        可查看导入依赖的模块,每一个模块含一个lookup table和一个IAT
    Lookup table
        记录了外部模块导入项的查找方式(名称或序号)和查找参数(名称字符串或序号值)
    Import address table(IAT)
        PE中,为指向lookup table中导入项的相对当前模块的偏移地址
        内存中,会被替换为导入项的绝对地址
模块间依赖的变化度分离
    依赖例子,模块导出函数
    分离的角度,概念的接口与实现
    变化度分离,能够实现模块间的松耦合,降低变化传递能力
        静态直接常量,动态间接变量
    客户模块依赖实现模块的两种方式
        静态绑定
            不分离接口和实现,直接暴露和依赖实现,使用导出函数在实现模块中的相对地址
            实现模块中的任意变化都可能会传到客户模块中
        动态绑定,分离接口和实现,只暴露和依赖接口,使用导出函数的名称或序号
            实现模块的接口变化才可能会传到客户模块,如函数类型、函数的名称或序号
模块的接口存储
    服务接口,导出表,export table,.rdata节或.edata节中
        导出表
            含export directory table及其相关附表
            记录了模块实现的服务接口,包括服务接口的名称、序号、地址、调用约定等
    实现接口,导入表,import table,.idata节中
模块重定位
    一般而言,exe可指定程序入口地址,从而可影响加载位置,而dll不可
    使用时以实际加载位置为准
    当模块的实际加载位置和默认加载位置不一致时,需要使用到重定位表
关于重定位表
    .reloc节中,含重定位表
        记录了指向模块内部的绝对地址变量的相对于模块的偏移地址
    使用方法
        系统加载当前模块后
        遍历绝对地址变量,据加载偏差调整其指向
        模块的默认加载位置为image base,在PE optional header中
    变量偏移地址的记录方式
        思想,以页(1000h字节)为组,记录页内偏移,避免数据冗余
        方法
            .reloc节头中,记录了所有数据起止位置
            所有数据,分为多个块(block),一个块记录一页中的地址变量
            块数据组成
                页基址,RVA,相对虚拟地址
                块长度,字节数,4字节
                多个页内偏址项
                    高4位为项类型
                    低12位为页内偏址
                对齐项,2字节0,可选,块数据是四字节对齐的
        使用重定位表的场景
            需要静态获得模块内的绝对地址(VA),而非相对地址(RVA)
            绝对地址
                可动态获取则动态获取,避免重定位
                    如函数中获取符号的地址,如全局变量,静态变量,函数,字符串常量等
                    汇编代码实现为,对相对地址指向的目标位置取地址,以动态获取而非静态获取
                否则,静态获取后重定位,如初始化全局地址变量或静态地址变量
 Vector越界访问检查
    消息框出现后,手动创建dump文件
        根据messagebox函数,切换线程
        查看栈情况,可用则用,不可用则基于寄存器分析
    自动创建dump文件不易分析

 

你可能感兴趣的:(软件调试,调试器,windows)