一种访问VM_IO地址空间的方法和用户态调试器转让专利

申请号 : CN200910190002.0

文献号 : CN101650688B

文献日 :

基本信息:

PDF:

法律信息:

相似专利:

发明人 : 吴春江程圣宇向红

申请人 : 中兴通讯股份有限公司

摘要 :

本发明公开了一种访问VM_IO地址空间的方法,包括在被调试进程空间中建立与外部设备I/O地址空间关联的VM_IO地址空间的第一过程,还包括由分配单元在被调试进程空间中分配与所述VM_IO地址空间关联的第一地址空间,并通过调试单元对所述第一地址空间的调试实现对VM_IO空间进行读访问或写访问的第二过程。本发明还公开了一种用户态调试器。本发明通过在被调试进程中分配与VM_IO地址空间关联的第一地址空间,对该地址空间的调试可以间接实现对VM_IO地址空间的读/写访问,从而解决了现有技术中无法在调试中访问VM_IO地址空间的问题。

权利要求 :

1.一种访问虚拟内存输入输出VM_IO地址空间的方法,包括在被调试进程空间中建立与外部设备I/O地址空间关联的虚拟内存输入输出VM_IO地址空间的第一过程,其特征在于,还包括由分配单元在被调试进程空间中分配与所述虚拟内存输入输出VM_IO地址空间关联的第一地址空间,并通过调试单元对所述第一地址空间的调试实现对虚拟内存输入输出VM_IO地址空间进行读访问或写访问的第二过程;

所述第二过程实现对虚拟内存输入输出VM_IO地址空间进行读访问具体包括以下步骤:步骤A1.在被调试进程空间中分配第一地址空间;

步骤B1.将所述虚拟内存输入输出VM_IO地址空间的数据拷贝到所述第一地址空间中;

步骤C1.用户态调试器读取经所述步骤B1拷贝后的第一地址空间中的数据;

所述第二过程实现对虚拟内存输入输出VM_IO地址空间进行写访问具体包括以下步骤:步骤A2.在被调试进程空间中分配第一地址空间;

步骤B2.用户态调试器将需要修改的数据写入所述第一地址空间;

步骤C2.将所述第一地址空间的数据拷贝到所述虚拟内存输入输出VM_IO地址空间中。

2.根据权利要求1所述的方法,其特征在于,所述数据拷贝具体通过用户态调试器调用Memcpy函数实现。

3.根据权利要求1所述的方法,其特征在于,所述第一地址空间的分配具体通过用户态调试器调用Malloc函数实现。

4.一种用户态调试器,用于调试外部I/O地址空间,在被调试进程空间中建立与外部设备I/O地址空间关联的虚拟内存输入输出VM_IO地址空间,其特征在于,包括:分配单元,用于在被调试进程空间中分配与所述虚拟内存输入输出MV_IO地址空间关联的第一地址空间; 调试单元,用于通过对所述第一地址空间的调试实现对虚拟内存输入输出VM_IO地址空间进行读访问或写访问;

当对虚拟内存输入输出VM_IO地址空间进行读访问时,所述分配单元还用于在被调试进程空间中分配第一地址空间,将所述虚拟内存输入输出VM_IO地址空间的数据拷贝到所述第一地址空间中;所述调试单元还用于读取经所述分配单元拷贝后的第一地址空间中的数据;

当对虚拟内存输入输出VM_IO地址空间进行写访问时,所述分配单元还用于在被调试进程空间中分配第一地址空间,将需要修改的数据写入所述第一地址空间;所述调试单元还用于将所述第一地址空间的数据拷贝到所述VM_IO地址空间中。

5.根据权利要求4所述的调试器,其特征在于,所述调试单元还用于通过调用Memcpy函数实现数据拷贝。

6.根据权利要求4所述的调试器,其特征在于,所述分配单元还用于通过调用Malloc函数实现所述第一地址空间的分配。

说明书 :

一种访问VM_IO地址空间的方法和用户态调试器

技术领域

[0001] 本发明涉及调试技术,具体涉及一种使用用户态调试器对VM_IO地址空间的调试方法以及一种用户态调试器。

背景技术

[0002] 调试是指开发人员分析和定位程序故障,在被调试进程中设置断点、查看变量和寄存器、显示堆栈和求表达式值,对被调试进程进行单步进入、单步跳过、单步返回、暂挂、继续运行等操作,或对被调试进程内存进行读写操作。而调试器则是执行上述操作的一种工具性软件。
[0003] 内存读取和修改是调试器的重要功能之一。在用户态调试器中,主要通过调用ptrace()函数来实现对被调试进程内存进行读写操作。ptrace()函数的定义为:long ptrace(enum_ptrace_request request,pid_t pid,void*addr,void*data),其中第一个参数决定了ptrace()函数的行为和其他参数的使用方法。当第一个参数为PTRACE_PEEKTEXT时,ptrace()函数就实现在pid进程空间中读取内存地址addr的值,并将读取到的值存入地址data中。当第一个参数为PTRACE_POKETEXT时,ptrace()函数就实现将地址data的值写入pid进程的内存地址addr中。
[0004] 在嵌入式软件开发中,往往涉及到对外部设备I/O地址空间的访问。以类UNIX系统为例,用户进程通过使用mmap()系统调用,从而调用系统内核中与之对应的mmap方法。该方法是file_operations结构体的一部分,用于请求将外部设备内存映射到用户进程地址空间。成功调用mmap方法后,内核在用户进程的虚拟地址空间查找一块VMA(Virtual Memory Area,虚拟机区域)。VMA是一种位标志,它定义在vm_area_struct结构中。和物理页的访问权限不同,VMA标志反映了内核处理页面所需要遵守的行为准则。同时,还要对该内存区域置上VM_IO(Virtual Memory InputOutput,虚拟内存输入输出)标志,表明用户程序具有能够直接访问外部设备I/O地址空间的能力。通过以上的操作,就完成了用户进程空间的一段地址与外部设备I/O空间的关联。当前用户进程在这段分配的地址空间内进行读操作或写操作,实际上就实现了对外部设备的访问。
[0005] 然而,具有VM_IO标志的内存空间只允许当前进程进行访问,如图1所示,在调试状态下,即使用户态调试器对被调试进程进行PTRACE_ATTACH操作后,也无法使用ptrace()函数对被调试进程VM_IO地址空间进行读写操作。
[0006] 在目前的用户态调试器中还没有实现访问被调试进程VM_IO地址空间的功能。

发明内容

[0007] 本发明要解决的技术问题是提供一种能够访问VM_IO地址空间的调试方法以及用户态调试器。
[0008] 本发明的技术问题通过以下技术方案加以解决:
[0009] 一种访问VM_IO地址空间的方法,包括在被调试进程空间中建立与外部设备I/O地址空间关联的VM_IO地址空间的第一过程,还包括由分配单元在被调试进程空间中分配与所述VM_IO地址空间关联的第一地址空间,并通过调试单元对所述第一地址空间的调试实现对VM_IO空间进行读访问或写访问的第二过程。
[0010] 所述第二过程实现对VM_IO空间进行读访问具体包括以下步骤:步骤A1.在被调试进程空间中分配第一地址空间;步骤B1.将所述VM_IO地址空间的数据拷贝到所述第一地址空间中;步骤C1.用户态调试器读取经所述步骤B1拷贝后的第一地址空间中的数据。
[0011] 所述第二过程实现对VM_IO空间进行写访问具体包括以下步骤:步骤A2.在被调试进程空间中分配第一地址空间;步骤B2.用户态调试器将需要修改的数据写入所述第一地址空间;步骤C2.将所述第一地址空间的数据拷贝到所述VM_IO地址空间中。
[0012] 所述数据拷贝具体通过用户态调试器调用Memcpy函数实现。
[0013] 所述第一地址空间的分配具体通过用户态调试器调用Malloc函数实现。
[0014] 一种用户态调试器,用于调试外部I/O地址空间,在被调试进程空间中建立与外部设备I/O地址空间关联的VM_IO地址空间,包括:分配单元,用于在被调试进程空间中分配与所述VM_IO地址空间关联的第一地址空间;调试单元,用于通过对所述第一地址空间的调试实现对VM_IO空间进行读访问或写访问。
[0015] 当对VM_IO空间进行读访问时,所述分配单元还用于在被调试进程空间中分配第一地址空间,将所述VM_IO地址空间的数据拷贝到所述第一地址空间中;所述调试单元还用于读取经所述分配单元拷贝后的第一地址空间中的数据。
[0016] 当对VM_IO空间进行写访问时,所述分配单元还用于在被调试进程空间中分配第一地址空间,将需要修改的数据写入所述第一地址空间;所述调试单元还用于将所述第一地址空间的数据拷贝到所述VM_IO地址空间中。
[0017] 所述调试单元还用于通过调用Memcpy函数实现数据拷贝。
[0018] 所述分配单元还用于通过调用Malloc函数实现所述第一地址空间的分配。
[0019] 本发明与现有技术相比较的有益效果是:
[0020] 本发明通过在被调试进程中分配第一地址空间,将VM_IO地址空间的数据拷贝至该第一地址空间,通过调试进程对第一地址空间数据的读取实现了对VM_IO地址空间数据的读取;本发明通过在被调试进程中分配第一地址空间,将需要修改的数据写入至该地址空间,并通过当前进程将该地址空间的数据拷贝到VM_IO地址空间,实现了对VM_IO地址空间数据的修改;可见,本发明通过在被调试进程中分配与VM_IO地址空间关联的第一地址空间,对该地址空间的调试间接实现对VM_IO地址空间的读/写访问,从而解决了现有技术中无法在调试中访问VM_IO地址空间的问题;并通过VM_IO地址空间与设备地址空间的关联,实现对外部设备的调试。
[0021] 本发明通过使用Malloc来分配空间、使用Memcpy来拷贝数据,实现简单、方便。

附图说明

[0022] 图1是现有技术用户态调试器结构示意图;
[0023] 图2是本发明用户态调试器一种具体实施方式结构示意图;
[0024] 图3是本发明调试方法实施例1读取被调试进程VM_IO地址空间的流程图;
[0025] 图4是本发明调试方法实施例1对访问内存地址进行分析的流程图;
[0026] 图5是本发明调试方法实施例1在被调试进程空间中运行malloc()函数的流程图;
[0027] 图6是本发明调试方法实施例1在被调试进程空间中运行malloc()函数的堆栈构建布局示意图;
[0028] 图7是本发明调试方法实施例1返回信号处理的流程图;
[0029] 图8是本发明调试方法实施例1在被调试进程空间中运行memcpy()函数的流程图;
[0030] 图9是本发明调试方法实施例1在被调试进程空间中运行memcpy()函数的堆栈构建布局示意图;
[0031] 图10是发明调试方法实施例2修改被调试进程VM_IO地址空间的流程图。

具体实施方式

[0032] 下面用具体实施方式结合附图对本发明做进一步详细说明。
[0033] 本发明一种用户态调试器,其一种具体实施方式,如图2所示,用于调试外部I/O地址空间,在用户进程中建立与外部设备I/O地址空间关联的VM_IO地址空间,该调试器包括:分配单元,用于在被调试地址空间中分配与所述VM_IO地址空间关联的第一地址空间;调试单元,用于通过对所述第一地址空间的调试实现对VM_IO空间进行读或写访问。
[0034] 当对VM_IO空间进行读访问时,分配单元还用于在被调试进程空间中分配第一地址空间,将VM_IO地址空间的数据拷贝到第一地址空间中;调试单元还用于读取经分配单元拷贝后第一地址空间中的数据。
[0035] 当对VM_IO空间进行写访问时,分配单元还用于在被调试进程空间中分配第一地址空间,将需要修改的数据写入第一地址空间;调试单元还用于将第一地址空间的数据拷贝到VM_IO地址空间中。对VM_IO空间进行读访问时分配的第一地址空间,和对VM_IO空间进行写访问时分配的第一地址空间可以是同一地址空间,也可以是不同的地址空间。
[0036] 调试单元还用于通过调用Memcpy函数实现数据拷贝。
[0037] 分配单元还用于通过调用Malloc函数实现所述第一地址空间的分配。
[0038] 本发明使用用户态调试器的调试方法,其一种具体实施方式,包括在用户进程中建立与外部设备I/O地址空间关联的VM_IO地址空间的第一过程,还包括在被调试地址空间中分配与VM_IO地址空间关联的第一地址空间,并通过对第一地址空间的调试实现对VM_IO空间进行读或写访问的第二过程。
[0039] 该第二过程实现对VM_IO空间进行读访问具体包括以下步骤:
[0040] 步骤A1.在被调试进程空间中分配第一地址空间;
[0041] 步骤B1.将VM_IO地址空间的数据拷贝到第一地址空间中;
[0042] 步骤C1.用户态调试器读取经步骤B1拷贝后第一地址空间中的数据。
[0043] 该第二过程实现对VM_IO空间进行写访问具体包括以下步骤:
[0044] 步骤A2.在被调试进程空间中分配第一地址空间;
[0045] 步骤B2.用户态调试器将需要修改的数据写入第一地址空间;
[0046] 步骤C2.将第一地址空间的数据拷贝到VM_IO地址空间中。
[0047] 数据拷贝具体可通过用户态调试器调用Memcpy函数实现。
[0048] 第一地址空间的分配具体通过用户态调试器调用Malloc函数实现。
[0049] 本发明使用用户态调试器的调试方法,其另一种具体实施方式,以在x86体系中运用为例,包括读取被调试进程VM_IO地址空间的实施例1和修改被调试进程VM_IO地址空间的实施例2。
[0050] 实施例1:
[0051] 如图3所示,读取被调试进程VM_IO地址空间的整个流程包括以下步骤:
[0052] 步骤301:读取被调试进程的maps文件内容,分析待读取的内存地址;
[0053] 步骤302:判断待读取的内存地址是否有效,即是否在被调试进程的虚拟地址空间中并且具有可读属性,若有效转入步骤303,否则转入步骤311;
[0054] 步骤303:分析符号表:通过调用BFD库(Binary File Descriptor,二进制文件描述符)的相关接口,读取被调试进程的可执行文件和加载的所有动态库,获取所有符号的名称、虚拟地址和符号类型信息,并把这些符号信息以符号名为关键字,建立hash表;
[0055] 步骤304:在符号hash表中查找malloc()函数的入口地址;
[0056] 步骤305:判断malloc()函数是否存在,若存在转入步骤306,否则转入步骤311;
[0057] 步骤306:在被调试进程中运行malloc()函数,分配一块新的内存空间作为第一地址空间;
[0058] 步骤307:在符号hash表中查找memcpy()函数的入口地址;
[0059] 步骤308:判断memcpy()函数是否存在,若存在转入步骤309,否则转入步骤311;
[0060] 步骤309:在被调试进程中运行memcpy()函数,拷贝VM_IO地址空间的数据到第一地址空间;
[0061] 步骤310:按照一般读取被调试进程内存的方式,用户态调试器调用ptrace()函数,从被调试进程第一地址空间中获取到的值即是VM_IO地址空间中的值,流程结束;
[0062] 步骤311:报警,流程结束。
[0063] 本实施例中,步骤301对访问内存地址分析流程,其一种实施方式如图4所示,包括以下步骤:
[0064] 步骤401:判断打开被调试进程maps文件是否成功,若成功转入步骤402,否则流程结束;
[0065] 步骤402:判断被调试进程maps文件内容是否读取完毕,是则结束流程,否则转入步骤403;
[0066] 步骤403:读取被调试进程maps文件的一条记录;
[0067] 步骤404:获取虚拟内存的属性和地址范围;
[0068] 步骤405:判断虚拟地址是否可读或可写,是则转入步骤406;否则转入步骤402;
[0069] 步骤406:判断访问内存地址是否有效,有效则转入步骤407;否则转入步骤402;
[0070] 步骤407:将访问内存地址属性置为真,即为有效,流程结束。
[0071] 本实施例中,步骤306运行malloc()分配第一地址空间的流程,其一种实施方式,如图5所示,进一步包括以下步骤:
[0072] 步骤501:停止被调试进程的运行;
[0073] 步骤502:保存当前所有寄存器的值;
[0074] 步骤503:根据CPU的类型,本例为X86,在被调试进程的堆栈上构建运行malloc()函数的参数及返回地址,或者获取当前sp寄存器(堆栈寄存器)的值,将malloc()函数的参数size及返回地址写入当前堆栈;
[0075] 步骤504:将新的栈顶地址写入sp寄存器,构建后的堆栈如图6所示,其中参数size为分配内存空间的大小,返回地址为一个非法地址,用于返回异常信号;再将pc寄存器(程序计数寄存器)的值改为malloc()函数的入口地址;
[0076] 步骤505:恢复被调试进程的运行;
[0077] 步骤506:获取异常信号。由于构建malloc()函数的堆栈时,将返回地址设为一个非法地址。当运行malloc()函数结束时,进程会返回一个异常信号。用户态调试器获取到该信号,说明被调试进程运行malloc()函数成功;
[0078] 步骤507:从eax寄存器中获取malloc()函数的返回值,该值就是在被调试进程空间中新分配的内存起始地址;
[0079] 步骤508:恢复运行malloc()函数前所有寄存器的值,流程结束。
[0080] 本实施例中,步骤506获取异常信号的流程,如图7所示,包括以下步骤:
[0081] 步骤701:等待被调试进程的异步信号;
[0082] 步骤702:判断是否为异常信号,是则转入步骤704;否则转入步骤703;
[0083] 步骤703:处理异步信号,转入步骤701;
[0084] 步骤704:获取运行函数返回值,流程结束。
[0085] 本实施例的步骤309使用memcpy()拷贝数据的流程,如图8所示,包括以下步骤:
[0086] 步骤801:停止被调试进程的运行;
[0087] 步骤802:保存当前所有寄存器的值;
[0088] 步骤803:根据CPU的类型,本例为X86,在被调试进程的堆栈上构建运行memcpy()函数的参数及返回地址,或者获取当前sp寄存器(堆栈寄存器)的值,将memcpy()函数的参数to、from、size及返回地址写入当前堆栈;
[0089] 步骤804:将新的栈顶地址写入sp寄存器,构建后的堆栈如图9所示,将返回地址设为一个非法地址;再将pc寄存器(程序计数寄存器)的值改为memcpy()函数的入口地址;
[0090] 步骤805:恢复被调试进程的运行;
[0091] 步骤806:获取异常信号。由于构建memcpy()函数的堆栈时,同样将返回地址设为一个非法地址,当运行memcpy()函数结束时,进程会返回一个异常信号。用户态调试器获取到该信号,说明被调试进程运行memcpy()函数成功;
[0092] 步骤807:恢复运行memcpy()函数前所有寄存器的值,流程结束[0093] 这样就将被调试进程VM_IO地址空间中的值拷贝到第一地址空间中了。
[0094] 实施例2:
[0095] 如图10所示,修改被调试进程VM_IO地址空间的整个流程包括以下步骤:
[0096] 步骤1001:读取被调试进程的maps文件内容,分析待读取的内存地址;
[0097] 步骤1002:判断内存地址是否有效,即待修改的内存地址是否在被调试进程的虚拟地址空间中并且具有可写属性,若有效转入步骤1003,否则转入步骤1011;
[0098] 步骤1003:分析符号表:通过调用BFD库的相关接口,读取被调试进程的可执行文件和加载的所有动态库,获取所有符号的名称、虚拟地址和符号类型信息,并把这些符号信息以符号名为关键字,建立hash表;
[0099] 步骤1004:在符号hash表中查找malloc()函数的入口地址;
[0100] 步骤1005:判断malloc()函数是否存在,若存在转入步骤1006,否则转入步骤1011;
[0101] 步骤1006:在被调试进程中运行malloc()函数,分配一块新的内存空间作为第一地址空间;首先停止被调试进程的运行并且保存当前所有寄存器的值。然后获取当sp寄存器(堆栈寄存器)的值,将malloc()函数的参数size及返回地址写入当前堆栈,并将新的栈顶地址写入sp寄存器。构建后的堆栈,其中参数size为分配内存空间的大小,返回地址为一个非法地址,用于返回异常信号;再将pc寄存器(程序计数寄存器)的值改为malloc()函数的入口地址;最后恢复被调试进程的运行;
[0102] 由于构建malloc()函数的堆栈时,将返回地址设为一个非法地址。当运行malloc()函数结束时,进程会返回一个异常信号。用户态调试器获取到该信号,说明被调试进程运行malloc()函数成功。然后从eax寄存器中获取malloc()函数的返回值,该值就是在被调试进程空间中新分配的内存起始地址,同时恢复运行malloc()函数前所有寄存器的值;
[0103] 步骤1007:按照一般修改被调试进程内存的方式,用户态调试器调用ptrace()函数,将数据写入被调试进程的第一地址空间;
[0104] 步骤1008:在符号hash表中查找memcpy()函数的入口地址;
[0105] 步骤1009:判断memcpy()函数是否存在,若存在转入步骤1010,否则转入步骤1011;
[0106] 步骤1010:在被调试进程中运行memcpy()函数,将第一地址空间的数据拷贝到VM_IO地址空间。首先停止被调试进程的运行并且保存当前所有寄存器的值。然后获取当sp寄存器(堆栈寄存器)的值,将memcpy()函数的参数to、from、size及返回地址写入当前堆栈,并将新的栈顶地址写入sp寄存器。再将pc寄存器(程序计数寄存器)的值改为memcpy()函数的入口地址,最后恢复被调试进程的运行;
[0107] 由于构建memcpy()函数的堆栈时,同样将返回地址设为一个非法地址。当运行memcpy()函数结束时,进程会返回一个异常信号。用户态调试器获取到该信号,说明被调试进程运行memcpy()函数成功。同时恢复运行memcpy()函数前所有寄存器的值。此时就将被调试进程新分配的内存空间中的值拷贝到VM_IO地址空间中,完成用户态调试器修改被调试进程VM_IO地址空间的操作;流程结束。
[0108] 步骤1011:报警,流程结束。