一种函数调用方法和装置技术领域
本申请涉及通信技术领域,尤其涉及一种函数调用方法和装置。
背景技术
在不具有应用对应的源代码的情况下,如果需要对应用进行二次开发或者进行性
能测试,就需要想办法调用该应用程序中的一些函数,以对该函数进行修改或进行其他处
理。
很多情况下,对应用进行二次开发或性能测试的用户所感兴趣的函数可能会在应
用的动态链接库中,如果感兴趣的函数是动态链接库中的导出函数,那么通过操作系统的
调用接口可以直接对感兴趣的函数进行调用;如果感兴趣函数是动态链接库中的未导出的
函数,那么则无法通过操作系统的调用接口对开感兴趣的函数进行调用,从而影响到了对
应用的二次开发或者性能测试。因此,如何实现灵活、便捷调用动态链接库中的未导出的函
数是本领域技术人员迫切需要解决的技术问题。
发明内容
有鉴于此,本申请提供了一种函数调用方法和装置,在对应用程序进行性能测试
或二次开发的过程中,可以降低调用未导出的函数的复杂度,提高调用未导出的函数的便
捷性和灵活性。
为实现上述目的,一方面,本申请提供了一种函数调用方法,包括:
在应用中运行被注入的逻辑函数,所述逻辑函数用于对所述应用进行测试或二次
开发;
在运行所述逻辑函数的过程中,如果需要对所述应用的动态链接库中未导出的目
标函数进行调用时,运行注入到所述应用中且用于获取函数偏移地址的偏移地址函数,以
分析出当前时刻所述目标函数相对于所述动态链接库的目标偏移地址;
依据所述目标偏移地址,运行注入到所述应用中的指针生成函数,以确定调用所
述目标函数所需的函数指针。
另一方面,本申请还提供了一种函数调用装置,包括:
第一函数运行单元,用于在应用中运行被注入的逻辑函数,所述逻辑函数用于对
所述应用进行测试或二次开发;
第二函数运行单元,用于在运行所述逻辑函数的过程中,如果需要对所述应用的
动态链接库中未导出的目标函数进行调用时,运行注入到所述应用中且用于获取函数偏移
地址的偏移地址函数,以分析出当前时刻所述目标函数相对于所述动态链接库的目标偏移
地址;
指针生成单元,用于依据所述目标偏移地址,运行注入到所述应用中的指针生成
函数,以确定调用所述目标函数所需的函数指针。
经由上述的技术方案可知,在申请实施例中,向待测试或二次开发的应用中注入
用于对该应用进行测试或二次开发的逻辑函数的同时,还向应用中注入了用于获取函数偏
移地址的偏移地址函数以及生成函数指针的指针生成函数,这样,在应用运行该逻辑函数
的过程中,如果需要对应用中的某个动态链接库中未导出的某个函数进行调用时,可以通
过运行该偏移地址函数,实时动态的分析出当前时刻该函数相对于该动态链接库的目标偏
移地址,从而基于确定出的目标偏移地址以及注入到应用中的指针生成函数,便可以得到
调用该未导出的函数所需的函数指针,实现对未导出的函数的灵活调用。
而且,由于偏移地址函数具有普遍适用性,对于任意一款需要进行测试或二次开
发的应用而言,在向应用中注入该偏移地址函数之后,在需要调用该应用的动态链接库中
的任意未导出的函数时,均可以通过运行该偏移地址函数分析出该未导出的函数相对于该
动态链接库的偏移地址,并结合指针生成函数得到调用未导出的函数所需的函数指针,这
样,二次开发或测试系统只需要维护一套偏移地址函数以及该指针生成函数,便可以实现
对任意应用中任意未导出的函数的调用,避免了针对不同应用的不同未导出的函数维护不
同的调用代码,减少了系统维护的数据量,也提高了调用未导出的函数的灵活性和便捷性。
附图说明
为了更清楚地说明本申请实施例或现有技术中的技术方案,下面将对实施例或现
有技术描述中所需要使用的附图作简单地介绍,显而易见地,下面描述中的附图仅仅是本
申请的实施例,对于本领域普通技术人员来讲,在不付出创造性劳动的前提下,还可以根据
提供的附图获得其他的附图。
图1为本申请实施例公开的函数调用系统的一种组成结构示意图;
图2为本申请实施例公开的函数调用方法的一种流程示意图;
图3为本申请实施例公开的函数调用方法的又一种流程示意图;
图4为在一种应用场景下,本申请实施例公开的函数调用方法的又一种流程示意
图;
图5为一种节区头的十六进制内容的示意图;
图6为本申请实施例公开的函数调用装置的一种组成结构示意图;
图7为本申请实施例公开的一种终端的结构示意图。
具体实施方式
本申请可能使用的技术名词、简写或缩写如下:
动态链接库:是一种不可直接执行的二进制代码程序文件,在使用时需要被其他
可执行文件加载后才能执行其中的二进制代码,其中的代码以导出函数的形式被其他可执
行程序解析;
导出函数:在动态链接库中可以被其他模块调用的函数,如,对于linux操作系统
中动态链接库内的导出函数是指,在动态链接库的SHT_DYNSYM节中被申明函数的名字,在
该节中出现的符号会在被加载的时候会被链接器解析,可以直接使用操作系统的接口直接
调用解析该导出函数,从而可以很方便地进行调用;
未导出的函数,也称为内部函数,是动态链接库中不属于导出函数的一类函数,该
类函数不能使用操作系统提供的接口直接调用;
dlopen:linux的系统调用之一,其可以实现将一个动态链接库加载到当前程序地
址空间中来,加载之后可以直接执行动态链接库中的代码;
dlsym:linux系统调用之一,其可以获取给定的导出函数的函数名在某一个动态
链接库中的地址;
基址:动态链接库被加载到可执行的二进制程序之后,该动态链接库在该进程空
间中第一个字节所处的内存地址。
偏移地址:相对基址的一个地址差值,是一个无符号整数。
进程map文件:在Unix系列操作系统中,指的是文件/proc/[pid]/maps,其中[pid]
表示进程自身的进程id,该文件中记录了该进程加载的所有可执行代码模块,每一条记录
包括加载的起始地址、结束地址、加载方式(读/写/执行)和包含可执行代码模块的绝对路
径;
ELF文件:是UNIX系统实验室作为应用程序二进制接口(Application Binary
Interface,ABI)而开发和发布的,也是Linux的主要可执行文件格式;
ELFIO:一个ELF格式解析库;
Unity游戏:使用优美缔软件公司开发的游戏引擎Unity开发的游戏。
本申请的发明人通过对应用进行二次开发或测试的过程进行研究发现:在对应用
进行二次开发或测试的过程中,如果希望对应用中动态链接库中的未导出的函数(以下简
称未导出函数)进行调用,就需要获取到该未导出函数的函数指针,该函数指针可以理解为
未导出函数在应用的内存空间中运行的地址。
其中,该函数指针可以根据动态链接库在该内存空间中运行的基址,以及该未导
出函数相对于该动态链接库的偏移地址(即,相对于动态链接库的基址的偏移地址)得到。
而在动态链接库维持不变化的情况下,在应用中加载该动态链接库之后,该未导出函数相
对于该动态链接库的偏移地址也保持不变,因此,可以针对需要调用的未导出函数生成一
套用于调用该未导出函数的调用代码,该调用代码中预置了未导出函数相对于该动态链接
库的偏移地址,以及用于基于该偏移地址,生成未导出函数的函数指针的指针生成函数。在
对应用进行测试或二次开发之前,可以将该调用代码注入到该应用的进程中,这样,在对应
用进行测试或者二次开发的过程中,如果需要调用应用中的该未导出函数,则可以通过运
行该调用代码,获取该调用代码中预置的偏移地址,依据该偏移地址,并运行调用代码中的
指针生成函数便可以得到该未导出函数的函数指针。
发明人进一步研究发现:由于调用代码中预置的偏移地址是固定不变的,这样,一
旦该未导出函数的动态链接库发生更新,应用在加载该动态链接库之后,该未导出函数相
对于该动态链接库的偏移地址也会发生改变,因此,调用代码中预置的偏移地址与该未导
出函数实际对应的偏移地址不同,这样,基于该调用代码中预置的偏移地址所得到的函数
指针也是错误的,导致无法基于确定出的函数指针对该未导出函数进行调用。可见,在动态
链接库发生更新之后,为了实现对该动态链接库中的该未导出函数进行调用,就需要相应
的修改该调用代码中预置的偏移地址,增加了开发或测试人员的工作量。
而且,由于一套调用代码中固定写入的是某个未导出函数对应的偏移地址,因此,
一套调用代码仅仅适用于一款应用内的一个动态链接库内的一个未导出函数的调用,这
样,如果需要二次开发或测试的应用数量较多,或者应用中需要调用的未导出函数的数据
较多时,就需要分别针对不同的应用、不同的动态链接库以及不同的未导出函数分别维护
相应的调用代码,使得二次开发或测试系统中需要维护多套调用代码,数据维护量较大,不
利于灵活、便捷的实现对未导出函数的调用。
同时,当需要调用应用中的未导出函数时,需要预先从维护的多套调用代码中筛
选出与该应用中该未导出函数的调用代码,然后才可以注入该调用代码;而且,当需要调用
多个未导出函数时,则需要注入多套相应的调用代码,导致了调用未导出函数的复杂度高。
为了能够实现对未导出函数的调用,并且能够实现灵活、便捷的调用未导出函数,
本申请实施例提供了一种函数调用方法和装置,本实施例的方案具有普遍适用性,可以适
用于对任意应用进行二次开发或者进行性能等其他测试过程中,对应用的任意一款动态链
接库中的任意未导出函数的调用。本申请实施例的方案可以应用到任意终端中,如计算机
等,该终端中可以运行有待被二次开发或测试的应用,以及用于对该待被测试或二次开发
的应用进行监控的监控应用。
下面将结合本申请实施例中的附图,对本申请实施例中的技术方案进行清楚、完
整地描述,显然,所描述的实施例仅仅是本申请一部分实施例,而不是全部的实施例。基于
本申请中的实施例,本领域普通技术人员在没有做出创造性劳动前提下所获得的所有其他
实施例,都属于本申请保护的范围。
图1为本申请提供的函数调用系统的一种组成结构示意图,图1所示的函数调用系
统可以实施本申请实施例提供的函数调用方法,参照图1,本申请提供的函数调用系统可以
包括:
运行于终端101中,待被测试或二次开发的至少一个待处理应用102;
运行于终端101中,用于对待处理应用102的测试或二次开发过程进行监控的控制
应用103。
其中,该控制应用103中维护逻辑函数、偏移地址函数以及指针生成函数,其中,逻
辑函数用于实现对待处理的应用102进行测试或二次开发,在该逻辑函数中包含了用于执
行二次开发或测试的相关程序代码;该偏移地址函数用于获取未导出函数相对于该未导出
函数所在的动态链接库的偏移地址;该指针生成函数用于基于该偏移地址函数获取到的偏
移地址,确定该未导出函数对应的函数指针。
该控制应用103用于在需要对所述待处理的应用进行测试或二次开发之前,将所
述逻辑函数、偏移地址函数以及该指针生成函数注入到该待处理的应用102中。
该待处理应用102,用于运行被注入的逻辑函数;在运行该逻辑函数的过程中,如
果需要对动态链接库中未导出的目标函数进行调用时,运行注入的偏移地址函数,以分析
出当前时刻该目标函数相对于该动态链接库的目标偏移地址;依据该目标偏移地址,运行
注入的指针生成函数,以确定调用目标函数所需的函数指针。
基于图1所示的函数调用系统,下面从待被测试或二次开发的待处理应用角度对
本申请实施例的函数调用方法进行介绍。参见图2,其示出了本申请一种函数调用方法一个
实施例的流程示意图,本实施例的方法可以包括:
201,在应用中运行被注入的逻辑函数。
需要说明的是,在本实施例中所提到的应用均指待进行测试或二次开发的待处理
应用,实际上仅仅与监控应用进行区分,本申请中将待进行测试或二次开发的应用称为待
处理应用。
其中,该逻辑函数用于对该应用进行测试或二次开发。可以理解的是,由于待被测
试或开发的应用与该监控应用之间具有独立的内存空间,监控应用无法访问该被测试或开
发的应用对应的内存空间,因此,需要通过向待测试或二次开发的应用中注入逻辑函数,在
应用中注入该逻辑函数之后,该应用会加载该逻辑函数以运行该逻辑函数,实现对该应用
进行相应的测试或二次开发。
其中,本申请实施例中,注入的实现方式也可以有多种,终端所采用的操作系统不
同时,注入技术也会有所差别,如,当终端为windows操作系统时,可以采用远程线程注入技
术实现逻辑函数以及偏移地址函数等函数的注入,当然,对应其他操作系统而言,还可以有
其他注入技术,在此不加以限定。
202,在运行该逻辑函数的过程中,如果需要对该应用的动态链接库中未导出的目
标函数进行调用时,运行注入到该应用中且用于获取函数偏移地址的偏移地址函数,以分
析出当前时刻所述目标函数相对于该动态链接库的目标偏移地址。
其中,目标函数相对于动态链接库的偏移地址可以理解为在该应用加载该动态链
接库之后,目标函数在应用内存空间中运行的地址相对于该动态链接库的基址的偏移地
址。
其中,为了便于区分,将本申请实施例中需要调用的未导出的函数称为目标函数,
并将该目标函数相对于该动态链接库的偏移地址称为目标偏移地址。
与直接注入固化的某个未导出函数对应的偏移地址不同,本申请实施例中,向该
应用中注入了用于获取未导出的函数对应的偏移地址的偏移地址函数,在确定了需要调用
的目标函数之后,通过运行该偏移地址函数可以实时分析出当前时刻该目标函数相对于动
态链接库的偏移地址。可见,只要是需要调用的未导出的目标函数是确定出的,通过该偏移
地址函数均可以分析出当前该目标函数相对于该目标函数所在的动态链接库的偏移地址。
可选的,为了使得偏移地址函数在运行过程中可以明确需要调用的目标函数,在
应用运行注入的该偏移地址函数之前,还可以确定该动态链接库的标识以及目标函数的标
识,也就是说,应用需要确定运行该逻辑函数过程中所需调用的未导出的目标函数的标识,
以及该目标函数所在的动态链接库的标识。其中,此处所提到的标识可以为名称,也可以为
其他能够唯一标识动态链接库或目标函数的序号或者标号等等信息。相应的,可以依据该
动态链接库的标识和目标函数的标识,运行注入到应用中的该偏移地址函数。如,将该动态
链接库的标识和目标函数的标识作为偏移地址函数的输入参数,以在偏移地址函数的过程
中,基于该动态链接库的标识和目标函数的标识,分析该目标函数相对于该动态链接库的
偏移地址。
可以理解的是,能够实现分析出当前该目标函数相对于该目标函数所在的动态链
接库的偏移地址的偏移地址函数可以有多种可能,在一种可能的情况下,运行该偏移地址
函数,具体可以触发执行以下操作:
解析该操作系统中的内存映射文件,以从该内存映射文件中记录的已加载动态链
接库的路径信息中,确定出与该动态链接库的标识对应的动态链接库全路径;
依据该目标函数的标识,从该动态链接库全路径中提取出该目标函数所属的目标
文件对应的路径;
基于该目标文件对应的路径,访问并解析该目标文件,以确定出该目标函数相对
于该动态链接库的目标偏移地址。
其中,操作系统的内存映射文件中记录了应用中当前加载的所有动态链接库的路
径信息,如,内存映射文件中可以记录有动态链接库中每个文件加载的起始地址、结束地
址、加载方式(如,读/写)以及动态链接库的绝对路径等。
该动态链接库全路径中包含了该应用加载该动态链接库之后,在动态链接库中所
有函数所在文件各自对应的路径信息,通过函数所在文件对应的路径可以从该应用的内存
空间中定位出该函数所在文件。
通过在偏移地址函数中包括以上操作的代码,可以在运行偏移地址函数的过程中
执行以上几步操作,并最终确定出该目标函数相对于动态链接库的偏移地址。
203,依据该目标偏移地址,运行注入到应用中的指针生成函数,以确定调用该目
标函数所需的函数指针。
运行该指针生成函数的过程中,可以基于该目标偏移地址,生成该目标函数所需
的函数指针。
可以理解的是,一种较为常见的生成未导出的目标函数的函数指针的方式可以为
将该目标函数相对于该动态链接库的目标偏移地址,与该动态链接库的基址相加,得到的
值就是该目标函数运行在该应用的内存空间中的地址(该地址为函数的首条代码对应的起
始地址),而该目标函数在该内存空间中的地址就是该目标函数的函数指针。因此,在一种
可能的实现方式中,该指针生成函数可以包括用于获取动态链接库基址的基址函数,以及
用于确定函数指针的指针确定函数。相应的,运行该指针生成函数具体可以包括执行以下
操作:
运行该基址函数,以获取该动态链接库的基址;
基于该目标偏移地址以及该动态链接库的基址,运行该指针确定函数,以确定该
目标函数的函数指针。
其中,该基址函数包括用于获取动态链接库的基址的函数代码。
可选的,可以依据该动态链接库的标识,运行该基址函数,以获取到该动态链接库
的基址。
需要说明的是,运行该基址函数的操作可以是在确定出目标偏移地址之后执行,
也可以是在运行偏移地址函数的同时,运行该基址函数;当然,也可以是在运行该偏移地址
函数之前运行该基址函数。
可见,在本申请实施例中,在应用运行该逻辑函数的过程中,如果需要对应用中的
某个动态链接库中未导出的某个函数进行调用时,可以通过运行注入到应用中的该偏移地
址函数,实时动态的分析出当前时刻该未导出的函数相对于该动态链接库的目标偏移地
址,从而基于确定出的目标偏移地址以及注入到应用中的指针生成函数,便可以得到调用
该未导出的函数所需的函数指针,实现对该未导出的函数的灵活调用。
而且,由于偏移地址函数具有普遍适用性,对于任意一款需要进行测试或二次开
发的应用而言,在向应用中注入该偏移地址函数之后,在需要调用该应用的动态链接库中
的任意未导出的函数时,均可以通过运行该偏移地址函数分析出该未导出的函数相对于该
动态链接库的偏移地址,并结合指针生成函数得到调用未导出的函数所需的函数指针,这
样,二次开发或测试系统只需要维护一套偏移地址函数以及该指针生成函数,便可以实现
对任意应用中任意未导出的函数的调用,避免了针对不同应用的不同未导出的函数维护不
同的调用代码,减少了系统维护的数据量,也提高了调用未导出的函数的灵活性和便捷性。
可以理解的是,在本申请实施例中,用于确定目标函数对应的函数指针所需的偏
移地址函数以及指针生成函数可以分别注入到该应用中。
考虑到偏移地址函数以及指针生成函数需要相互配合才可以最终确定出该目标
函数对应的函数指针,因此,为了便于实现函数注入,并有利于应用的调用,可以将用于确
定未导出的函数所对应的函数指针所需的函数代码均封装到一个接口函数中,即该接口函
数中可以封装有偏移地址函数以及该指针生成函数,这样,应用需要确定目标函数对应的
函数指针时,可以直接调用并运行该接口函数,以运行该接口函数中封装的各个函数所对
应的函数代码。相对应的,控制应用可以直接将该接口函数注入到该应用的进程中,如果应
用调用该接口函数,则会触发运行该接口函数中封装的偏移地址函数以及该接口函数中封
装的该指针生成函数,这样,在运行指针生成函数的过程中,基于运行偏移地址函数所得到
的目标偏移地址,便可以最终得到目标函数对应的函数指针。
下面以通过接口函数封装用于确定未导出的函数对应的函数指针的所有函数代
码为例进行介绍。结合图1所述的函数调用系统,对本申请实施例的函数调用方法进行介
绍。参见图3,其示出了本申请一种函数调用方法又一个实施例的流程交互示意图,本实施
例的方法可以包括:
301,监控应用向待被测试或二次开发的待处理应用中注入逻辑函数和接口函数。
其中,该逻辑函数用于对该待处理应用进行测试或二次开发。该接口函数用于确
定待处理应用的动态链接库中未导出的函数的函数指针。
302,待处理应用运行该逻辑函数。
向待处理应用中注入该逻辑函数和接口函数之后,待处理应用可以加载并运行该
逻辑函数和接口函数。
303,在待处理应用运行该逻辑函数的过程中,如果逻辑函数需要对该应用的动态
链接库中未导出的目标函数进行调用时,确定该目标函数的名称以及该动态链接库的名
称。
其中,该目标函数的名称也就是目标函数的函数名。该目标函数的名称和动态链
接库的名称可以预置在逻辑函数中,逻辑函数需要调用某个未导出的目标函数时,可以提
供该目标函数的名称以及动态链接库的名称。
可以理解的是,本实施例以目标函数和动态链接库的标识为名称为例进行介绍,
但是当标识为其他情况,也同样适用于本实施例。
304,待处理应用调用并运行该接口函数,以执行以下步骤305至311的操作。
305,待处理应用根据动态链接库的名称,加载该动态链接库并获取该动态链接库
的窗口句柄。
需要说明的是,加载该动态链接库为可选操作,当待处理应用当前已经加载了该
动态链接库的情况下,则无需再加载该动态链接库。
其中,加载该动态链接库并获取动态链接库的窗口句柄可以通过调用操作系统中
用于打开动态链接库并返回动态链接库窗口句柄的库函数来实现。当然,对于不同的操作
系统而言,该库函数会有所区别,如,当操作系统为linux系统时,可以通过dlopen这一函数
来加载动态链接库并返回动态链接库的窗口句柄。
306,待处理应用基于该窗口句柄所对应的结构体指针,查询该结构体指针所指向
的结构体;
307,待处理应用从该结构体中解析出动态链接库的基址。
如,仍以linux系统为例,Linux中,sysinfo是用来获取系统相关信息的结构体,而
dlopen函数所返回的窗口句柄是一个整数,该窗口句柄的数值也就是soinifo结构体对应
的结构体指针,基于该soinifo的指针可以查询该soinifo结构体,在该soinifo结构体中存
储该动态链接库的名字,该动态链接库的基址等字段,从而可以通过该soinifo结构体中获
取到该动态链接库的基址。
例如,soinifo结构体的部分定义可以如下:
![]()
其中,soinfo结构体中的name字段为动态链接库的名字,而动态链接库加载的基
址存储在base字段中。
需要说明的是,执行以上步骤305至307所需的代码相当于前面实施例提到了基址
函数所对应的代码,而步骤305至307的执行过程相当于运行该基址函数所执行的操作步
骤。
308,待处理应用通过解析该操作系统中的内存映射文件,从该内存映射文件中记
录的已加载动态链接库的路径信息中,确定出与该动态链接库的名称对应的动态链接库的
全路径。
可以理解的是,动态链接库的全路径中包含了该动态链接库的名称,因此,根据该
动态链接库的名称,可以从该内存映射文件中查询出该动态链接库所对应的全路径。
仍以linux系统为例,内存映射信息可以存储在/proc/self/maps文件中,通过遍
历该map文件,可以得到加载的该动态链接库在本地的全路径。
309,待处理应用依据该目标函数的名称,从动态链接库全路径中提取出该目标函
数所属的目标文件对应的路径。
310,待处理应用基于该目标文件对应的路径,访问并解析该目标文件,以确定出
该目标函数相对于该动态链接库的目标偏移地址。
在确定出目标函数所属的目标文件之后,可以对该目标文件进行解析,以确定该
目标文件中存储偏移地址的区域,从而定位出该目标函数相对于动态链接库的目标偏移地
址。
当然,当操作系统不同时,解析该目标文件获取该目标函数相对于该目标偏移地
址的具体过程也会有所差异。如,仍以linux系统为例,基于该linux系统的动态链接库以及
可执行文件均为ELF文件,该ELF文件会有ELF头,ELF头中描绘了整个文件的组织结构。该
EFL头中还包括很多节(section),在确定出目标文件之后,可以获取该目标文件对应的EFL
头中节的个数,遍历所有的节,在定位到SHT_SYMTAB节之后遍历所有的符号名称,与该目标
函数的名称进行比较,如果定位该目标函数,就将该目标函数的偏移地址提取出来。
可以理解的是,执行以上步骤308至310所需的代码相当于前面实施例提到了偏移
地址函数所对应的代码,而步骤308至310的执行过程相当于运行该偏移地址函数所执行的
相关操作步骤。
需要说明的是,偏移地址函数与基址函数的运行前后顺序并不限于图3所示,因
此,也可以是在执行了步骤308至310之后,在执行该步骤305至307,当然,也可以是在执行
该步骤305至307的同时,执行该步骤308至310。
311,待处理应用依据该目标偏移地址和动态链接库的基址,确定该目标函数的函
数指针。
具体的,可以将动态链接库的基址加上该目标偏移地址,得到该目标函数运行在
应用的内存空间中的地址,即得到该函数指针。
其中,该步骤311相当于运行前面提到的指针确定函数所执行的操作。
312,待处理应用在运行逻辑函数的过程中,基于该目标函数的函数指针,调用该
目标函数。
其中,该步骤312为可选步骤。
可见,由于本申请的接口函数具有普遍适用性,对于任意一款需要进行测试或二
次开发的应用而言,在向应用中注入该接口函数之后,在需要调用该应用的动态链接库中
的任意未导出的函数时,依据该未导出的函数的名称,通过调用并运行该接口函数,便可以
分析出该未导出的函数相对于该动态链接库的偏移地址,并结合运行该接口函数所分析出
的动态链接库的基址,可以得到该未导出的函数所需的函数指针,这样,二次开发或测试系
统只需要维护一套接口函数以及需要调用的函数的函数名,便可以实现对任意应用中任意
未导出的函数的调用,避免了针对不同应用的不同未导出的函数维护不同的调用代码,减
少了系统维护的数据量,也提高了调用未导出的函数的灵活性和便捷性。
可以理解的是,本申请实施例的方案适用于对任意操作系统下的动态链接库中未
导出函数的调用。
为了便于理解本申请实施例的方案的具体实现过程,下面以linux操作系统为例,
对本申请实施例的函数调用方法进行介绍。结合图1,参见图4,其示出了本申请一种函数调
用方法在一种应用场景下的流程交互示意图,本实施例的方法可以包括:
401,监控应用向待被测试或二次开发的待处理应用中注入逻辑函数和接口函数。
其中,该逻辑函数用于对该待处理应用进行测试或二次开发。该接口函数用于确
定待处理应用的动态链接库中未导出的函数的函数指针。
402,待处理应用运行该逻辑函数。
403,在待处理应用运行该逻辑函数的过程中,如果需要对该应用的动态链接库中
未导出的目标函数进行调用时,确定该目标函数的名称以及该动态链接库的名称。
可以理解的是,本实施例以目标函数和动态链接库的标识为名称为例进行介绍,
但是当标识为其他情况,也同样适用于本实施例。
404,待处理应用调用并运行该接口函数,以执行以下步骤405至415的操作。
405,待处理应用根据动态链接库的名称,利用dlopen函数加载该动态链接库并获
取该动态链接库的窗口句柄。
其中,dlopen为linux操作系统中的一种调用函数。
406,待处理应用基于该窗口句柄所对应的sysinfo结构体指针,查询该sysinfo结
构体指针所指向的sysinfo结构体。
407,待处理应用对该sysinfo结构体进行解析,从该sysinfo结构体的base字段中
获取动态链接库的基址。
408,待处理应用通过解析该操作系统中的map文件,从该map文件中记录的已加载
动态链接库的路径信息中,确定出与该动态链接库的名称对应的动态链接库全路径。
409,待处理应用依据该目标函数的名称,从动态链接库全路径中提取出该目标函
数所属的目标文件对应的路径。
410,待处理应用基于该目标文件对应的路径,访问该目标文件的ELF头。
411,待处理应用依据该目标函数的名称,从该ELF头确定包含该目标函数对应符
号的节区符号表。
412,待处理应用依据该节区符号表中目标函数所对应的符号,从该EFL头的节区
头部表中定位出.symtab节和.strtab节。
413,待处理应用遍历该.symtab节,查询出该.symtab节中的sh_name在该.strtab
节中的索引值为目标函数的符号项。
414,待处理应用依据查询出的sh_name,在该.symtab节中查找该目标函数对应的
目标偏移地址。
为了便于理解该步骤410至步骤414,下面结合具体实例进行介绍:
首先读取ELF文件头,在该ElF文件的结构体中,本方案仅关注三个字段:e_shoff,
它的含义是节区头部表在文件中的偏移;第二个是e_shentsize,它的含义是每个节区头部
的大小;第三个是e_shstrndx,它的含义是节符号表头在节区头部的索引号。假设e_shoff
对应的值是0xbf4b88,e_shentsize对应的值是0x28,e_shstrndx对应的值是0x1a,由此计
算出节符号表头在文件中的偏移是0xbf4b88+0x28*0x1a=0xbf4f98。
其中,该偏移0xbf4f98处对应的是一个节区表头的结构,在该节区表头中,本方案
所关键的同样有三个字段:第一个是sh_name,它的含义是该节区的名字在节符号表中的偏
移;第二个是sh_offset,它的含义是这个节区在文件中的偏移;第三个是sh_size,它的含
义是这个节的大小。
查询目标文件在该0xbf4f98处的节符号表头的数据,假设得到节符号表头的名字
sh_name索引号为0x11,在文件中的偏移sh_offset是0xbf4a6c,其大小sh_size为0x11b。
查询目标文件在0xbf4a6c处的数据,假设得到:偏移0x11处所对应的字符串为
shstrtab,即节符号表本身的名字是shstrtab。至此,节符号表已经找到。
基于该节符号表可以用来确定接下来找的节区的名字:从上面确定出的节表头位
置开始一个个遍历节区头,找到sh_type的属性为SHT_SYMTAB的节区头,假设经过查找,发
现有一个sh_type字段为2的节区头,并假设该节区头的十六进制内容如图5所示。
查看节符号表的数据,在节符号表索引为1的位置表示的符号为”.symtab”,也就
是说该节表示的头就是symtab节。假设由symtab节头可知,.symtab节的真实位置为文件偏
移0xbf5010处,并查看0xbf5010处的数据,其中,.symtab节的每一项数据都对应着一个符
号结构,其中,字段sh_name为符号表中的索引,以遍历节区头表的方式查找到”.strtab”节
的起始地址,假设该起始地址为0xdd0df0。
然后,遍历.symtab节,找到该节中符号名字为_Z13getNumCamerasv的项,假设该
项的字符索引为0xb90f7,将该字符索引加上.strtab的0xdd0df0基址,得到0xe89ee7,假设
查看目标文件0xe89ee7处的数据为_Z13getNumCamerasv,则该数据正是我们要找的符号。
根据.symtab节的每一项数据对应的符号结构,可以得到符号_Z13getNumCamerasv的偏移。
415,将目标偏移地址和动态链接库的基址相加,得到该目标函数的函数指针。
416,待处理应用在运行逻辑函数的过程中,基于该目标函数的函数指针,调用该
目标函数。
其中,该步骤416为可选步骤。
下面对本发明实施例提供的一种函数调用装置进行介绍,下文描述的一种函数调
用装置可与上文描述的一种函数调用方法相互对应参照。
参见图6,其示出了本申请一种函数调用装置一个实施例的结构示意图,本实施例
的装置可以包括:
第一函数运行单元601,用于在应用中运行被注入的逻辑函数,所述逻辑函数用于
对所述应用进行测试或二次开发;
第二函数运行单元602,用于在运行所述逻辑函数的过程中,如果需要对所述应用
的动态链接库中未导出的目标函数进行调用时,运行注入到所述应用中且用于获取函数偏
移地址的偏移地址函数,以分析出当前时刻所述目标函数相对于所述动态链接库的目标偏
移地址;
指针生成单元603,用于依据所述目标偏移地址,运行注入到所述应用中的指针生
成函数,以确定调用所述目标函数所需的函数指针。
可选的,所述第二函数运行包括:
标识获取单元,用于在运行所述逻辑函数的过程中,如果需要对所述应用的动态
链接库中未导出的目标函数进行调用时,确定所述动态链接库的标识以及所述目标函数的
标识;
地址获取单元,用于依据所述动态链接库的标识和所述目标函数的标识,运行注
入到所述应用中且用于获取函数偏移地址的偏移地址函数,以分析出所述以分析出当前时
刻所述目标函数相对于所述动态链接库的目标偏移地址。
可选的,所述地址获取单元,包括:
全路径解析单元,用于解析所述操作系统中的内存映射文件,以从所述内存映射
文件中记录的已加载动态链接库的路径信息中,确定出与所述动态链接库的标识对应的动
态链接库全路径;
文件路径提取单元,用于依据所述目标函数的标识,从所述动态链接库全路径中
提取出所述目标函数所属的目标文件对应的路径;
地址获取子单元,用于基于所述目标文件对应的路径,访问并解析所述目标文件,
以确定出所述目标函数相对于所述动态链接库的目标偏移地址。
可选的,所述指针生成单元所运行的所述指针生成函数包括:用于获取动态链接
库基址的基址函数以及用于确定函数指针的指针确定函数;
所述指针生成单元,包括:
第三函数运行单元,用于运行所述基址函数,以获取所述动态链接库的基址;
第四函数运行单元,用于基于所述目标偏移地址以及所述动态链接库的基址,运
行所述指针确定函数,以确定所述目标函数的函数指针。
可选的,所述第一函数运行单元,具体为:用于在运行所述逻辑函数的过程中,如
果需要对所述应用的动态链接库中未导出的目标函数进行调用时,调用注入到所述应用中
的接口函数,并运行所述接口函数中封装的所述偏移地址函数;
所述指针生成单元具体为,用于依据所述目标偏移地址,运行所述接口函数中封
装的指针生成函数。
本申请实施例还提供了一种终端,该终端可以实现上述所述的一种函数调用方
法。
图7示出了终端的硬件结构框图,参照图7,终端700可以包括:处理器701,通信接
口702,存储器703和通信总线704;
其中处理器701、通信接口702、存储器703通过通信总线704完成相互间的通信;
可选的,通信接口702可以为通信模块的接口,如GSM模块的接口;
处理器701,用于执行程序;
存储器703,用于存放程序;
程序可以包括程序代码,所述程序代码包括计算机操作指令。
处理器701可能是一个中央处理器CPU,或者是特定集成电路ASIC(Application
Specific Integrated Circuit),或者是被配置成实施本发明实施例的一个或多个集成电
路。
存储器703可能包含高速RAM存储器,也可能还包括非易失性存储器(non-
volatile memory),例如至少一个磁盘存储器。
其中,程序可具体用于:
在应用中运行被注入的逻辑函数,所述逻辑函数用于对所述应用进行测试或二次
开发;
在运行所述逻辑函数的过程中,如果需要对所述应用的动态链接库中未导出的目
标函数进行调用时,运行注入到所述应用中且用于获取函数偏移地址的偏移地址函数,以
分析出当前时刻所述目标函数相对于所述动态链接库的目标偏移地址;
依据所述目标偏移地址,运行注入到所述应用中的指针生成函数,以确定调用所
述目标函数所需的函数指针。
需要说明的是,本说明书中的各个实施例均采用递进的方式描述,每个实施例重
点说明的都是与其他实施例的不同之处,各个实施例之间相同相似的部分互相参见即可。
对于装置类实施例而言,由于其与方法实施例基本相似,所以描述的比较简单,相关之处参
见方法实施例的部分说明即可。
最后,还需要说明的是,在本文中,诸如第一和第二等之类的关系术语仅仅用来将
一个实体或者操作与另一个实体或操作区分开来,而不一定要求或者暗示这些实体或操作
之间存在任何这种实际的关系或者顺序。而且,术语“包括”、“包含”或者其任何其他变体意
在涵盖非排他性的包含,从而使得包括一系列要素的过程、方法、物品或者设备不仅包括那
些要素,而且还包括没有明确列出的其他要素,或者是还包括为这种过程、方法、物品或者
设备所固有的要素。在没有更多限制的情况下,由语句“包括一个……”限定的要素,并不排
除在包括要素的过程、方法、物品或者设备中还存在另外的相同要素。
对所公开的实施例的上述说明,使本领域技术人员能够实现或使用本发明。对这
些实施例的多种修改对本领域技术人员来说将是显而易见的,本文中所定义的一般原理可
以在不脱离本发明的精神或范围的情况下,在其它实施例中实现。因此,本发明将不会被限
制于本文所示的这些实施例,而是要符合与本文所公开的原理和新颖特点相一致的最宽的
范围。
以上仅是本发明的优选实施方式,应当指出,对于本技术领域的普通技术人员来
说,在不脱离本发明原理的前提下,还可以做出若干改进和润饰,这些改进和润饰也应视为
本发明的保护范围。