CN114428636A - 一种模块回调函数参数不匹配的修复方法及相关设备 - Google Patents
一种模块回调函数参数不匹配的修复方法及相关设备 Download PDFInfo
- Publication number
- CN114428636A CN114428636A CN202011172062.2A CN202011172062A CN114428636A CN 114428636 A CN114428636 A CN 114428636A CN 202011172062 A CN202011172062 A CN 202011172062A CN 114428636 A CN114428636 A CN 114428636A
- Authority
- CN
- China
- Prior art keywords
- parameters
- callback
- function
- class
- program
- Prior art date
- Legal status (The legal status is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the status listed.)
- Pending
Links
Images
Classifications
-
- G—PHYSICS
- G06—COMPUTING; CALCULATING OR COUNTING
- G06F—ELECTRIC DIGITAL DATA PROCESSING
- G06F8/00—Arrangements for software engineering
- G06F8/70—Software maintenance or management
- G06F8/72—Code refactoring
-
- G—PHYSICS
- G06—COMPUTING; CALCULATING OR COUNTING
- G06F—ELECTRIC DIGITAL DATA PROCESSING
- G06F9/00—Arrangements for program control, e.g. control units
- G06F9/06—Arrangements for program control, e.g. control units using stored programs, i.e. using an internal store of processing equipment to receive or retain programs
- G06F9/46—Multiprogramming arrangements
- G06F9/48—Program initiating; Program switching, e.g. by interrupt
- G06F9/4806—Task transfer initiation or dispatching
- G06F9/4843—Task transfer initiation or dispatching by program, e.g. task dispatcher, supervisor, operating system
- G06F9/4881—Scheduling strategies for dispatcher, e.g. round robin, multi-level priority queues
Landscapes
- Engineering & Computer Science (AREA)
- Software Systems (AREA)
- Theoretical Computer Science (AREA)
- General Engineering & Computer Science (AREA)
- Physics & Mathematics (AREA)
- General Physics & Mathematics (AREA)
- Stored Programmes (AREA)
Abstract
本发明提供了一种模块回调函数参数不匹配的修复方法以及相关设备,包括:获取修复程序中回调函数类型中包含的新建参数和源程序上层回调函数中包含的原始参数;比较新建参数和原始参数,将所述新建参数中与所述原始参数中的不同的参数作为新增参数信息;查找所有源程序中关于原始参数回调函数的回调指针,并利用所回调类回调指针对应的虚函数接口,通知上层允许基本代码调用新增参数信息对应的扩展代码来修复,进而忽略掉所述新增参数信息,从而将收到数据的处理逻辑与底层数据进行解耦合,通知上层允许基本代码调用新增参数信息对应的扩展代码来修复,进而忽略掉所述新增参数信息,以解决参数不匹配导致程序执行出现崩溃的问题。
Description
技术领域
本发明涉及计算机程序软件技术领域,尤其涉及一种模块回调函数参数不匹配的修复方法及相关设备。
背景技术
在程序开发中,为了对代码解耦合,通常都会使用设置回调函数的指针,来解决底层功能与上层调用者解除耦合关系,回调函数指针的执行可以让底层功能执行完成后通知调用者。
然而现有的问题是,很多时候程序开发者会对程序来进行分模块开发,那么有的模块升级了代码功能,如对某个内部通知外部的回调函数多增加了一个参数,而另一个模块没有对所有使用回调的地方进行修改增加参数,程序执行时则会导致crash问题。同时回调函数指针无法检查其参数个数,因此如果底层模块增加了函数参数个数,而对应的回调函数没有增加参数个数,程序并不知道不匹配,只有程序执行时并且执行到回调函数时,才会导致程序crash。即为在正常计算机系统运行过程中,因某种原因宕机,或主机、程序停止工作等情况。
对于程序代码没有发布的情况下,很容易的通过修改程序代码来修复该功能。而对于程序已经发布的情况下,则需要一种方法能够修复这样的参数不匹配的情况。
发明内容
本发明提供了一种模块回调函数参数不匹配的修复方法,通过查找所有源程序中关于原始参数回调函数的回调指针,并调用回调类回调指针对应的虚函数接口,基于hook修复技术,忽略掉所述新增参数信息,以解决参数不匹配导致程序执行出现崩溃的问题。
一种模块回调函数参数不匹配的修复方法,包括:
获取修复程序中回调函数类型中包含的新建参数和源程序上层回调函数中包含的原始参数;
比较所述新建参数和所述原始参数获得新增参数;其中,所述新增参数为所述新建参数中与所述原始参数中的不同参数;
查找所有源程序中关于原始参数回调函数的回调指针;
调用所述回调指针对应的虚函数接口,并基于hook技术实现程序修复。
优选的是,所述新增参数的信息包括参数个数、参数命名和参数类型。
优选的是,所述虚函数接口的查找过程包括如下步骤:
编写一个具有嵌套类的新建类,所述嵌套类内定义了虚函数接口,以继承源程序中的类,实现读取数据产生回调的处理逻辑;
查找并记录源程序中所有继承自源程序的类,并记录所述继承自源程序类的创建地址;
并查找所述继承自源程序的类的调用虚函数接口的地址;
其中,所述查找调用虚函数接口的地址的过程通过汇编代码实现。
优选的是,所述查找虚函数接口地址的过程包括:
解析模块中移植的可执行的文件,并解析出只读字段;其中,所述只读字段中存储有每个类的名称和对应的续表;
遍历所述只读字段,通过名称检索方式查找所有继承类,进而创建目标类;
遍历所述续表中的类,并将所述目标类对应的第一个虚函数作为析构函数,并将所述析构函数对应续表的位置作为匹配函数进行调用;
记录所有满足查找条件的目标类名称和和对应的类的相对于模块的起始地址和偏移地址,进而获取模块的真实加载内存地址。
优选的是,还包括建立查找链表,其包括:
将所述目标类的起始地址和偏移地址均存储到查找链表中;
并通过对比创建的实例化对象的大小和存储续表的地址来对应于创建的目标类。
优选的是,所述基于hook技术实现程序修复的过程包括:
建立多个堆栈,并在所述堆栈中分别压入寄存器的值,其中,所述堆栈与新建参数个数相同;
从实例化指针中取出对象的虚表指针;
新建对象指针寄存器,并将对象指针赋值给所述对象指针寄存器;
调用虚表的第二个函数,并将所述第二个函数的调用指令修改为跳转指令,忽略并弹出多余的参数。
优选的是,所述查找链表以34位系统或64位系统方式定义。
一种模块回调函数参数不匹配的修复装置,包括:
获取模块,用于获取修复程序中回调函数类型中包含的新建参数和源程序上层回调函数中包含的原始参数;
比较模块,用于比较所述新建参数和所述原始参数获得新增参数;
查找模块,用于查找所有源程序中关于原始参数回调函数的回调指针;
修复模块,用于利用所述回调指针对应的虚函数接口,通知上层允许基本代码调用新增参数信息对应的扩展代码来修复,进而忽略掉所述新增参数的信息,从而将收到数据的处理逻辑与底层数据进行解耦合,实现程序修复。
一种模块回调函数参数不匹配的修复服务器,所述服务器包括存储器、处理器及存储在存储器上并可在处理器上运行的计算机程序,所述处理器执行所述程序时实现所述模块回调函数参数不匹配的修复方法的步骤。
一种计算机可读介质,其上存储有计算机程序,所述程序被处理器执行时实现所述模块回调函数参数不匹配的修复方法的步骤。
有益效果
本发明提供了一种模块回调函数参数不匹配的修复方法,通过查找所有源程序中关于原始参数回调函数的回调指针,并利用所回调类回调指针对应的虚函数接口,通知上层允许基本代码调用新增参数信息对应的扩展代码来修复,进而忽略掉所述新增参数信息,以解决参数不匹配导致程序执行出现崩溃的问题。
本发明通过编写一个类来继承之前编写的类,并实现其中的虚接口,从而再程序中找到所有的虚函数指针的调用点,解决了汇编程序中虚接口查找困难的问题,进而保证修复所有的函数调用点,以保证程序稳定运行。
附图说明
图1为本发明所述的模块回调函数参数不匹配的修复方法的流程图。
图2为本发明所述的查找所有源程序中关于原始参数回调函数的回调指针方法流程图
图3为本发明所述的查找继承自源程序的类的调用虚函数接口的地址过程流程图。
图4为本发明所述的模块回调函数参数不匹配的修复装置结构示意图。
具体实施方式
以下由特定的具体实施例说明本发明的实施方式,熟悉此技术的人士可由本说明书所揭露的内容轻易地了解本发明的其他优点及功效,显然,所描述的实施例是本发明一部分实施例,而不是全部的实施例。基于本发明中的实施例,本领域普通技术人员在没有做出创造性劳动前提下所获得的所有其他实施例,都属于本发明保护的范围。
需要说明的是,在本发明的描述中,术语“中”、“上”、“下”、“横”、“内”等指示的方向或位置关系的术语是基于附图所示的方向或位置关系,这仅仅是为了便于描述,而不是指示或暗示所述装置或元件必须具有特定的方位、以特定的方位构造和操作,因此不能理解为对本发明的限制。此外,术语“第一”、“第二”仅用于描述目的,而不能理解为指示或暗示相对重要性。
此外,还需要说明的是,在本发明的描述中,除非另有明确的规定和限定,术语“设置”、“安装”、“相连”、“连接”应做广义理解,例如,可以是固定连接,也可以是可拆卸连接,或一体地连接;可以是机械连接;可以是直接相连,也可以通过中间媒介间接相连,可以是两个元件内部的连通。对于本领域技术人员而言,可根据具体情况理解上述术语在本发明中的具体含义。
如图1所示,基于背景技术提出的技术问题,本发明提供了一种模块回调函数参数不匹配的修复方法,包括:
步骤S110、获取修复程序中回调函数类型中包含的新建参数和源程序上层回调函数中包含的原始参数;
具体的说,在C语言程序语言情况下:源程序上层回调函数定义为:void(*Recv_Notify)(void*buff,int recv_length)
其中,函数指针类型为Recv_Notify,且回调函数中包含两个原始参数,分别为buff和length。
修复程序中的回调函数定义为:
void(*Recv_Notify)(void*buff,int recv_length,int already_length);
其中函数指针类型为回调函数中,包含三新建参数,分别为buff、length和intalready_length,通常源程序和修复程序中的参数通过查找获得。
步骤S120、比较新建参数和原始参数获得新增参数;其中,新增参数信息为所述新建参数中与原始参数中的不同参数,即新增参数为原始参数比新建参数多增加的参数或减少的参数;可见新建参数与原始参数相比增加了一个参数int already_length,新增加了一个参数int already_length,
在另一实施例中,还包括获取新增参数信息,包括参数个数、参数命名和参数类型等,获取参数信息能够更好的根据参数类型确定程序崩溃的原因。
步骤S130、查找所有源程序中关于原始参数回调函数的回调指针;在本实施例中给出了C语言和C++语言程序中两种回调指针。
C语言的函数指针可以解释为:如果在程序中定义了一个函数,那么在编译时系统就会为这个函数代码分配一段存储空间,这段存储空间的首地址称为这个函数的地址。而且函数名表示的就是这个地址。既然是地址本实施例就可以定义一个指针变量来存放,这个指针变量就叫作函数指针变量,简称函数指针,具体的C语言的编程语言如下:
typedefvoid(*Recv_Notify)(void*buff,intrecv_length);
Void Socket_Recv·(Recv_Notify fun_callback){
fun_callback(buff,length)}
其中,Recv_Notify为定义的函数指针类型,且回调函数中包含两个参数,分别为参数buff和recv_length。Socket_Recv·(Recv_Notify fun_callback){
是socket网络通信收到服务器的数据后,通过参数设置进来的回调函数来通知上层,从而将收到数据的处理逻辑与底层的socket数据进行解耦合。fun_callback(buff,length)为socket网络套接字收到服务器的数据后,通过此参数设置进来的回调通知上层的数据处理层。
C++语言的类对象的成员函数指针,C++语言的一类指针数据类型,用于存储一个指定类具有给定的形参列表与返回值类型的成员函数的访问信息。不同于普通函数,类成员函数的调用有一个特殊的不写在形参表里的隐式参数:即类实例的地址。因此,C++的类成员函数调用使用thiscall调用协议。类成员函数是限定于所属类之中的。
C++语言的情况如下:
class File{
class Callback{
Public:
Virtual~Callback(){}
virtual voidread(uint8_t*buff,int length)=0;
};
Void setCallback(Callback*callback){
cb=callback;}
Void ReadFile()
{cb->read(buff,length)}
其中,class File为定义类为File类,即文件读写类,class Callback{Public:Virtual~Callback(){}为建立虚析构函数;virtual void read(uint8_t*buff,intlength)=0;定义了一个虚函数接口。};allback为套嵌类,并含有一个接口read,和2个参数buff和length;Callback*cb为成员变量,用于存储设置进来的回调类的指针。VoidsetCallback(Callback*callback){为接口,用于接收一个类的实例指针并保存到成员变量中。Void ReadFile()为实际读取文件内容的函数读取完成后,则通知上层内容读取完成,可以进行处理,cb->read(buff,length)为调用之前编写的虚函数接口来通知上层。
步骤S140、调用所述回调指针对应的虚函数接口,并基于hook技术实现程序修复进而忽略掉新增参数信息,从而将收到数据的处理逻辑与底层数据进行解耦合,实现程序修复。
本步骤则介绍了2种情况,一种是使用C语言设置回调接口,另一种则是通过C++的指针设置回调接口,因此本实施例对于上述2种情况在已经发布的程序中进行修复时,需要采取不同的方案来执行。具体的C++语言的修复方案如下:
对于C++语言来说,由于回调函数多传入了一个参数,而上层设置回调函数的对应函数并没有增加一个参数,从而缺少一个参数,导致不匹配,从而执行时会导致程序crash。因此本实施例的一个修复方案则是对所有调用c++的虚函数接口处,进行Hook代码,来忽略掉最后的一个参数,从而可以修复程序的崩溃问题,由于C++语言的特性,本实施例需要查找到所有的调用处并全部进行Hook,进而能够进而忽略掉新增参数信息,以解决参数不匹配导致程序执行出现崩溃的问题,实现源程序中回调函数的整体查找、替换。
如图2所示,程序中回调函数的整体查找过程由步骤130实现,查找所有源程序中关于原始参数回调函数的回调指针的具体步骤如下:
步骤131、编写一个具有嵌套类的新建类,嵌套类内定义了虚函数接口,以继承源程序中的类,实现读取数据产生回调的处理逻辑;在C++程序语言中,需要编写一个类来继承自之前编写的类,并实现其中的虚接口,具体的程序语言如下:
Class MyCallback:public Callback{
Virtual~MyCallback(){}
virtual void read(uint8_t*buff,int length){
其中,MyCallback为编写的新建类,且继承自之前编写的类Callback,read(uint8_t*buff,int length)为虚函数接口,能够需要实现当读取数据后产生回调的处理逻辑。
步骤S132、查找并记录源程序中所有继承自源程序的类,并记录继承自源程序类的创建地址,具体的实现过程通过汇编代码来实现:
Push edx
Push ebx
mov eax,[esi]
其中,Push edx为往堆栈中压入寄存器edx的值,其中edx存储了参数buff的指针。Push ebx为往堆栈中压入寄存器ebx的值,其中ebx存储了参数length的值。mov eax,[esi],esi存储了MyCallback的实例化对象的指针,此汇编代码则是从指针中取出对象的虚表指针。
步骤S133、查找继承自源程序的类的调用虚函数接口的地址;其中,查找调用虚函数接口的地址的过程通过汇编代码实现;
mov ecx,esi
call dwordptr[eax+4];
其中,mov ecx,esi表示将对象指针赋值给ecx寄存器,call dwordptr[eax+4]此汇编代码则是调用续表的第二个函数,第一个是析构函数,对应于本实施例的read函数。此步骤通过编写一个类来继承之前编写的类,并实现其中的虚接口,从而再程序中找到所有的虚函数指针的调用点,解决了汇编程序中虚接口查找困难的问题,进而保证修复所有的函数调用点,以保证程序稳定运行。
如图3所示,步骤S133查找继承自源程序的类的调用虚函数接口的地址的详细过程如下:
步骤S211、解析模块中移植的可执行的文件,并解析出只读字段;其中,只读字段中存储有每个类的名称和此类的续表;
具体的说:在C++语言中,编译器对其进行编译时,会创建一份RTTI的实现,其中RTTI记录了每个类的名称以及此类的续表。而RTTI的信息存储在这个程序的只读数据段中.rdata,因此本实施例则可以解析程序的PE文件,并按照PE文件格式来解析出.rdata段,然后本实施例对.rdata段进行遍历,查找所有继承自Callback的类。
首先从.rdata段中查找到类Callback,作为一种优选,可以依据类名找到Callback类;
步骤S212、遍历只读字段,通过名称检索方式查找所有继承类,进而创建目标类,查找所有续表中第一个虚函数为Callback的类。
步骤S213、遍历续表中的类,并将目标类对应的第一个虚函数作为析构函数,并将析构函数对应续表的位置作为匹配函数进行调用;
其中如果一个类继承自Callback类,那么其对应的虚表中第一个函数则是Callback类的析构函数。
步骤S214、记录所有满足查找条件的目标类名称和和对应的类的相对于模块的起始地址和偏移地址,进而获取模块的真实加载内存地址。
MyCallback的虚表第一项值则vftable_dd offset Callback____destructor
其中第二项值则是第二个虚函数,对应于本实施例的dd offsetconnectSuccess。
基于本实施例这样的查找方案,可以查找到所有满足要求的所有类名称,和对应的类的相对于此模块的起始地址的偏移地址。查找时是程序没有执行时,从程序代码的汇编代码中查找,其中程序每次执行时其加载到内存的起始位置是变化的,因此在没加载之前只能以假设其加载的内存起始地址为基础,来记录其的偏移地址。
如果假设模块的起始地址是suppose_Start,那么查找到的子类在模块中的代码偏移地址则是suppose_MyCallback_Offset.
在另一实施例中,还包括查找链表的建立,目标类的起始地址和偏移地址均存储到查找链表中,将续表地址与目标类相对应,以便查找。
具体通过对比创建的实例化对象的大小,以及存储续表的地址来对应于创建的目标类,且查找链表以34位系统或64位系统方式定义创建方式如下:
If(system64){
List<uint64_t>offsetlists;
}else{
List<uint32_t>offsetlists;
}
Offsetlists.push_back(suppose_MyCallback_Offset);
suppose_MyCallback_Offset。
Map<uint64_t,List<uint64_t>>class_info;
Map<uint32_t,List<uint32_t>>class_info;
class_info[suppose_Start]=offsetlists;
另外本实施例再单独设计一个map
Map<string,uint64_t>class_size
class_size[MyCallback]=sizeof(MyCallback obj);
其中,system64为定义系统为64位,来存储地址,List<uint64_t>offsetlists;
}else{否则系统是32位的,本实施例使用32位的数据来存储地址。
List<uint32_t>offsetlists;}
Offsetlists.
push_back(suppose_MyCallback_Offset);
其中,本实施例只存储suppose_MyCallback_Offset,而不存储模块的起始地址suppose_Start。并定义一个map来存储上述的所有数据,34位和64位的定义方式分别为:Map<uint64_t,List<uint64_t>>class_info;64位系统定义方式。Map<uint32_t,List<uint32_t>>class_info;32位系统定义方式。起始地址和模块内的所有偏移数据关联起来的语言程序为:
class_info[suppose_Start]=offsetlists;
另外,本实施例再单独设计一个map,用于存储每个类的实例化大小。Map<string,uint64_t>class_size其中第一个也是类的名称,第二个则是类的实例化对象大小。class_size[MyCallback]=sizeof(MyCallback obj);类的大小本实施例可以通过创建一个类的实例对象MyCallback obj,并通过系统函数size_of(obj)来得到。
查找到的所有子类的偏移存储到一个链表中,后续方便来查找链表的所有实例对象的地方且具体查找链表的创建方式包括34位系统或64位系统。
查找到了每个继承自Callback类的所有子类,那么接下来需要基于子类来查找代码中有那些地方创建了具体的实例化对象。
在另一实施例中,给出了如何查找一个类的对象的代码的具体实施例,具体过程为:
将类实例化对象的大小设定为4;,类实例化对象的大小和具体类的实例化对象大小有关。类的大小可以通过创建一个类的实例对象MyCallback obj,并通过系统函数size_of(obj)来得到。
call operator_new分配类的实例化的内存空间。
mov ecx,eax内存分配空间完成后,得到了分配的内存起始地址则存储在eax寄存器中,对于c++程序,通常类的对象指针需要存储到ecx寄存器中,即对eax寄存器继续宁赋值操作。
mov dword ptr[eax],suppose_MyCallback_Offset是第一个地址空间中存储类对应的虚表。
依据suppose_MyCallback_Offset这个地址来查找哪些地方创建了类的实例化对象。具体实现过程如下:
首先,是遍历模块map,对应的程序语言为:
For(auto it=class_info.begin();it!=class_info.end();++it)
然后是遍历每个存储的偏移,对应的程序语言为:
{For(auto it2=it.second.begin();it2!=it.second.end();++it2){
然后上述特征来查找程序中的所有代码,并通过对比创建的实例化对象的大小,通过之前本实施例创建的class_size则方便查找类的大小,以及存储虚表的地址来对应于创建的类。
}
查找到类的所有创建的代码的位置后,可以从虚表中查找到本实施例需要查找的回调函数的偏移,也就是在虚表中的第几个位置。找到位置本实施例则可以匹配该函数的调用。
本实施例的调用函数是虚表的第二个位置。
call dwordptr[eax+4],
那么这个虚表位置的计算公式如下:
如果是如果系统是64位,offset=eax+8*no;其中no表示虚表中的第几个位置。
如果是如果系统是32位,offset=eax+4*no;
找到这些地址后后,即查找到了所有调用本实施例之前设置回调函数的地方。
本实施例需要查找到所有的调用处并全部进行Hook,进而能够进而忽略掉新增参数信息,以解决参数不匹配导致程序执行出现崩溃的问题,实现源程序中回调函数的整体查找、替换。
其中hook为钩子函数,通知上层允许基本代码调用新增参数信息对应的扩展代码来修复,hook修复过程为:
找到了所有创建对象并且调用对应的函数的位置后,那么首先原始代码调用如下:
Push eax
Push edx
Push ebx
mov eax,[esi]
mov ecx,esi
call dwordptr[eax+4]
其中,Push edx为往堆栈中压入寄存器edx的值,其中edx存储了参数buff的指针,Push ebx为往堆栈中压入寄存器ebx的值,其中ebx存储了参数length的值,mov eax,[esi]esi存储了MyCallback的实例化对象的指针,此汇编代码则是从指针中取出对象的虚表指针。mov ecx,esi此汇编代码表示将对象指针赋值给ecx寄存器。call dwordptr[eax+4]此汇编代码则是调用虚表的第二个函数的调用。
本实施例的方案对Hook修复过程进行了修改,是通过修改call这条指令为jmp指令,即将调用指令修改为调转指令,然后从堆栈中取出数据来调用call dwordptr[eax+4],并从堆栈中弹出多余设置的一个已经接收的数据长度,那么调用的call函数还是只需要传入2个参数。
通过修改后如下:
Push eax
Push edx
Push ebx
mov eax,[esi]esi
mov ecx,esi
Jump newmemory//
此次需要修改成跳转指令,跳转指令的目的地址newmemory那么newmemory是一段本实施例新分配的一段可以执行的内存空间。Newmemory的实现。在newmeory中,本实施例是通过系统函数分配一段可以执行的内存空间。Newmeory VirtualAlloc(NULL,Size,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
分配的内存空间后,本实施例需要编写对应执行的汇编代码,具体过程为:
Pop ebx
Pop edx
Pop ecx
Mov ecx,esi
Push ebx
Push edx
call dwordptr[eax+4]
其中,Pop ebx为弹出参数,Pop edx为弹出参数,Pop ecx弹出多余的一个参数,Mov ecx,esi为恢复ecx的指针为对象的指针,Push ebx,Push edx call dwordptr[eax+4]调用时则只传入了2个参数,从而保障了参数是匹配的。如果对于另一种不匹配的情况是少了一个参数。可以在上述代码中增加一个push操作,从而多push一个参数,即在寄存器中多传入一个参数。
本实施例通过hook修复技术,修改call这条指令为jmp指令,然后从堆栈中取出数据来调用call dwordptr[eax+4],并从堆栈中弹出多余设置的一个已经接收的数据长度,只需传入两个参数,使修复过程更简单,且可以兼顾增加参数和减少参数两种情况,保证了程序修复的稳定性。
设计对于C语言的修复方案,具体的实现过程如下:
对于C语言的情况则简单很多,其中下面本实施例展示C语言的情况下函数的调用的汇编代码。
push ebx
push ebx
push ffset Recv_Notify。
call read
假设模块的起始地址是suppose_Start,查找到的子类在模块中的代码偏移地址则是suppose_Recv_Notify_Offset
实际加载的内存偏移算法如下:
Offset=suppose_Recv_Notify_Offset-suppose_Start+RealStart
计算出了offset从程序中基于程序的反汇编代码,查找代码特征:
push ebx压入参数
push ebx压入参数
push Offset此处本实施例则需要传入对应的回调函数指针。
call read调用具体的对应的函数。
只有满是上述的汇编代码特征则满足本实施例需要查找的情况,以依据补充参数或者减少参数来对所有查找到的情况进行修复。其中push ffset Recv_Notify为查找到回调函数的函数指针,Recv_Notify是编写的函数指针。采用本实施例的查找方法,很容易的查找所有使用调用函数的位置,可以找到所有的调用点,基于调用点,按照C++的方式来修复参数不匹配的问题。
实施以现有回调函数参数增加了一个参数的情况,且以C++语言修复过程为例,做进一步说明:
对于C++语言情况如下:
virtual void read(uint8_t*buff,int length,int already_length)=0;
增加了一个参数int already_length,表示当前已经读取了文件的多少数据;对于C++语言来说,由于回调函数多传入了一个参数,而上层设置回调函数的对应函数并没有增加一个参数,从而缺少一个参数,导致不匹配,从而执行时会导致程序crash。因此本实施例的一个修复方案则是对所有调用c++的虚函数接口处,进行Hook代码,来忽略掉最后的一个参数,从而可以修复程序的崩溃问题。那么由于C++语言的特性,本实施例需要查找到所有的调用处并全部进行Hook。
步骤一、找到所有的虚函数指针的调用点,本实施例要实现对应的回调功能,那么本实施例则需要编写一个类来继承自之前编写的类,并实现其中的虚接口。
Class MyCallback:public Callback{
Virtual~MyCallback(){}
virtual void read(uint8_t*buff,int length){
}
其中,MyCallback为编写的新建类,且继承自之前编写的类Callback,read(uint8_t*buff,int length)为虚函数接口,能够需要实现当读取数据后产生回调的处理逻辑。
步骤二、查找所有源程序中关于原始参数回调函数的回调指针,本实施例提供的查找思路如下:
push 4;
MyCallback obj;
size_of(obj);
call operator_new;
mov ecx;
mov dwordptr[eax];
suppose_MyCallback_Offset;
其中,push 4为类实例化对象的大小,类的大小可以通过创建一个类的实例对象MyCallback obj,并通过系统函数size_of(obj)来得到。call operator_new为分配类的实例化的内存空间。mov ecx,eax为内存分配空间完成后,其得到了分配的内存起始地址则存储在eax寄存器中,将类的对象指针存储到ecx寄存器中,进行赋值操作。mov dword ptr[eax],suppose_MyCallback_Offset是第一个地址空间中存储类对应的虚表,可以依据suppose_MyCallback_Offset的地址查找创建了类的实例化对象的地址。
步骤三、通过创建的class_size则方便查找类的大小,以及存储虚表的地址来对应于创建的是那个类,具体实现过程为:
call dwordptr[eax+4],回调续表的第二个函数,如果是如果系统是64位,offset=eax+8*no;如果是如果系统是32位,offset=eax+4*no;其中,no则是虚表中的第几个位置。
步骤四、找到了所有创建对象并且调用对应的函数的位置后,通过hook的方式来修复这样多一个参数的问题,原始代码调用如下:
Pop ebx
Pop edx
Pop ecx
Mov ecx,esi
Push ebx
Push edx
call dwordptr[eax+4]
其中,Pop ebx为弹出参数,Pop edx为弹出参数,Pop ecx弹出多余的一个参数,Mov ecx,esi为恢复ecx的指针为对象的指针,Push ebx,Push edx call dwordptr[eax+4],调用时则只传入了2个参数,从而保障了本实施例的参数是匹配的。如果对于另一种不匹配的情况是少了一个参数。可以在上述代码中增加一个push操作,从而多push一个参数,即多传入一个参数,本实施例设置的回调函数的函数指针,采用嵌套类继承的查找方法,能够查找到所有使用回调函数的地址,并获取应得所有的调用点,基于调用点,本按照C++的方式来修复参数不匹配得问题,修复效果好,运行稳定。
如图4所示,本实施例还提供了一种模块回调函数参数不匹配的修复装置,包括:获取模块310、比较模块320、查找模块330和修复模块340。
其中,获取模块310用于获取修复程序中回调函数类型中包含的新建参数和源程序上层回调函数中包含的原始参数;比较模块320用于比较新建参数和所述原始参数获得新增参数;查找模块330用于查找所有源程序中关于原始参数回调函数的回调指针;修复模块340用于利用回调指针对应的虚函数接口,通知上层允许基本代码调用新增参数信息对应的扩展代码来修复,进而忽略掉新增参数的信息,从而将收到数据的处理逻辑与底层数据进行解耦合,实现程序修复。
修复装置内存储具有修复功能的修复程序,实现对程序来进行分模块开发,模块升级了代码功能,可以检查到回调函数指针的调用地点,执行回调函数时保证计算机程序稳定运行,有效避免了因参数不匹配原因导致的宕机,或主机、程序停止工作等情况。
在另一实施例中,本发明还提供了一种模块回调函数参数不匹配的修复服务器,服务器包括存储器、处理器及存储在存储器上并可在处理器上运行的计算机程序,所述处理器执行所述程序时上述方法的步骤,模块回调函数参数不匹配的修复服务器中存储的计算机程序能够稳定运行修复代码。
在另一实施例中,本发明还包括一种计算机可读介质,其上存储有计算机程序,所述程序被处理器执行时实现上述方法的步骤。
在另一实施例中,本发明还提供了一种块回调函数参数不匹配的修复系统,包括模块回调函数参数不匹配的修复服务器。
本发明提供的一种模块回调函数参数不匹配的修复方法,通过查找所有源程序中关于原始参数回调函数的回调指针,并利用所回调类回调指针对应的虚函数接口,通知上层允许基本代码调用新增参数信息对应的扩展代码来修复,进而忽略掉新增参数信息,以解决参数不匹配导致程序执行出现崩溃的问题。本发明通过编写一个类来继承之前编写的类,并实现其中的虚接口,从而再程序中找到所有的虚函数指针的调用点,解决了汇编程序中虚接口查找困难的问题,进而保证修复所有的函数调用点,以保证程序稳定运行。
至此,已经结合附图所示的优选实施方式描述了本发明的技术方案,但是,本领域技术人员容易理解的是,本发明的保护范围显然不局限于这些具体实施方式。在不偏离本发明的原理的前提下,本领域技术人员可以对相关技术特征做出等同的更改或替换,这些更改或替换之后的技术方案都将落入本发明的保护范围之内。
Claims (10)
1.一种模块回调函数参数不匹配的修复方法,其特征在于,包括:
获取修复程序中回调函数类型中包含的新建参数和源程序上层回调函数中包含的原始参数;
比较所述新建参数和所述原始参数获得新增参数;其中,所述新增参数为所述新建参数中与所述原始参数中的不同参数;
查找所有源程序中关于原始参数回调函数的回调指针;
调用所述回调指针对应的虚函数接口,并基于hook技术实现程序修复。
2.根据权利要求1所述的模块回调函数参数不匹配的修复方法,其特征在于,所述新增参数的信息包括参数个数、参数命名和参数类型。
3.根据权利要求1或2所述的模块回调函数参数不匹配的修复方法,其特征在于,所述虚函数接口的查找过程包括如下步骤:
编写一个具有嵌套类的新建类,所述嵌套类内定义了虚函数接口,以继承源程序中的类,实现读取数据产生回调的处理逻辑;
查找并记录源程序中所有继承自源程序的类,并记录所述继承自源程序类的创建地址;
并查找所述继承自源程序的类的调用虚函数接口的地址;
其中,所述查找调用虚函数接口的地址的过程通过汇编代码实现。
4.根据权利要求3所述的模块回调函数参数不匹配的修复方法,其特征在于,所述查找虚函数接口地址的过程包括:
解析模块中移植的可执行的文件,并解析出只读字段;其中,所述只读字段中存储有每个类的名称和对应的续表;
遍历所述只读字段,通过名称检索方式查找所有继承类,进而创建目标类;
遍历所述续表中的类,并将所述目标类对应的第一个虚函数作为析构函数,并将所述析构函数对应续表的位置作为匹配函数进行调用;
记录所有满足查找条件的目标类名称和和对应的类的相对于模块的起始地址和偏移地址,进而获取模块的真实加载内存地址。
5.根据权利要求4所述的模块回调函数参数不匹配的修复方法,其特征在于,还包括建立查找链表,其包括:
将所述目标类的起始地址和偏移地址均存储到查找链表中;
并通过对比创建的实例化对象的大小和存储续表的地址来对应于创建的目标类。
6.根据权利要求5所述的模块回调函数参数不匹配的修复方法,其特征在于,所述基于hook技术实现程序修复的过程包括:
建立多个堆栈,并在所述堆栈中分别压入寄存器的值,其中,所述堆栈与新建参数个数相同;
从实例化指针中取出对象的虚表指针;
新建对象指针寄存器,并将对象指针赋值给所述对象指针寄存器;
调用虚表的第二个函数,并将所述第二个函数的调用指令修改为跳转指令,忽略并弹出多余的参数。
7.根据权利要求6所述的模块回调函数参数不匹配的修复方法,其特征在于,所述查找链表以34位系统或64位系统方式定义。
8.一种模块回调函数参数不匹配的修复装置,其特征在于,包括:
获取模块,用于获取修复程序中回调函数类型中包含的新建参数和源程序上层回调函数中包含的原始参数;
比较模块,用于比较所述新建参数和所述原始参数获得新增参数;
查找模块,用于查找所有源程序中关于原始参数回调函数的回调指针;
修复模块,用于利用所述回调指针对应的虚函数接口,通知上层允许基本代码调用新增参数信息对应的扩展代码来修复,进而忽略掉所述新增参数的信息,从而将收到数据的处理逻辑与底层数据进行解耦合,实现程序修复。
9.一种电子设备,包括存储器和处理器,其特征在于,所述处理器用于实现存储器中存储得计算机管理程序式时实现权利要求1-7中任一项所述得模块回调函数参数不匹配的修复方法的步骤。
10.一种计算机可读介质,其上存储有计算机程序,所述程序被处理器执行时实现权利要求1-7任一项所述模块回调函数参数不匹配的修复方法的步骤。
Priority Applications (1)
Application Number | Priority Date | Filing Date | Title |
---|---|---|---|
CN202011172062.2A CN114428636A (zh) | 2020-10-28 | 2020-10-28 | 一种模块回调函数参数不匹配的修复方法及相关设备 |
Applications Claiming Priority (1)
Application Number | Priority Date | Filing Date | Title |
---|---|---|---|
CN202011172062.2A CN114428636A (zh) | 2020-10-28 | 2020-10-28 | 一种模块回调函数参数不匹配的修复方法及相关设备 |
Publications (1)
Publication Number | Publication Date |
---|---|
CN114428636A true CN114428636A (zh) | 2022-05-03 |
Family
ID=81310289
Family Applications (1)
Application Number | Title | Priority Date | Filing Date |
---|---|---|---|
CN202011172062.2A Pending CN114428636A (zh) | 2020-10-28 | 2020-10-28 | 一种模块回调函数参数不匹配的修复方法及相关设备 |
Country Status (1)
Country | Link |
---|---|
CN (1) | CN114428636A (zh) |
Cited By (1)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
CN115408272A (zh) * | 2022-08-08 | 2022-11-29 | 南京航空航天大学 | 一种基于TOPSIS法的Java冗余代码评估方法 |
-
2020
- 2020-10-28 CN CN202011172062.2A patent/CN114428636A/zh active Pending
Cited By (2)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
CN115408272A (zh) * | 2022-08-08 | 2022-11-29 | 南京航空航天大学 | 一种基于TOPSIS法的Java冗余代码评估方法 |
CN115408272B (zh) * | 2022-08-08 | 2024-02-06 | 南京航空航天大学 | 一种基于TOPSIS法的Java冗余代码评估方法 |
Similar Documents
Publication | Publication Date | Title |
---|---|---|
US6976221B2 (en) | System and method for flexible software linking | |
JP4878715B2 (ja) | オペレーティングシステムに適合しないアプリケーションにパッチを当てる方法、コンピュータシステム及びコンピュータ可読記録媒体 | |
US5802365A (en) | Dynamic device matching using driver candidate lists | |
US6876996B2 (en) | Method and apparatus for using a shared library mechanism to facilitate sharing of metadata | |
US8032886B2 (en) | Tracking asynchronous execution of program using hierarchical structure of objects | |
US7788314B2 (en) | Multi-computer distributed processing with replicated local memory exclusive read and write and network value update propagation | |
US5911074A (en) | Process for manipulating data models used in software engineering | |
US7784043B2 (en) | Method and system for automated code-source indexing in Java Virtual Machine environment | |
US6415435B1 (en) | Method and apparatus for determining compatibility of parent classes in an object oriented environment using versioning | |
US5630076A (en) | Dynamic device matching using driver candidate lists | |
US5303392A (en) | Accessing current symbol definitions in a dynamically configurable operating system | |
US7814472B2 (en) | System and method for shared code-sourcing in a Java Virtual Machine environment | |
US10025572B2 (en) | Dynamic collection attribute-based computer programming language methods | |
US7197511B2 (en) | Methods and apparatus for type safe, lazy, user-defined class loading | |
US7882198B2 (en) | Shared JAVA JAR files | |
US10929149B2 (en) | Method and system for updating firmware | |
US7974987B1 (en) | Database for storing device handle data in an extensible firmware interface environment | |
EP1645959A2 (en) | Grouping of run-time components in entity domains | |
JP2005531066A (ja) | ソフトウェア・アトマイゼーション用のビュー | |
AU2891800A (en) | Method and apparatus for dispatch table construction | |
US6918126B1 (en) | Method and apparatus for creating and enforcing protected system level Java code | |
CN114428636A (zh) | 一种模块回调函数参数不匹配的修复方法及相关设备 | |
US11200203B1 (en) | Accessing files stored in a firmware volume from a pre-boot application | |
CN117462961A (zh) | 一种不停机游戏发布方法、系统、设备及介质 | |
US6999964B2 (en) | Support for domain level business object keys in EJB |
Legal Events
Date | Code | Title | Description |
---|---|---|---|
PB01 | Publication | ||
PB01 | Publication | ||
SE01 | Entry into force of request for substantive examination | ||
SE01 | Entry into force of request for substantive examination |