当前位置: 首页 > news >正文

Windows下如何确定虚函数在虚函数表中的位置

我需要用c#调用 c++ 的 类的函数, 虽然可以通过头文件的顺序,但是如果可以打印出虚函数在虚表中的Offset更好。
测试要求:
Windows, x86 只有1层虚函数,没有被override过

虚函数调用如下

auto a_reqCreditDetail = &XtTraderApi::reqCreditDetail;
(api->*a_directOrder)(NULL, 0, NULL);

通过VisualStudio的汇编模式观察,
a_reqCreditDetail 其实是一个thunk, 他指向了一条语句:

    jmp xti::XtTraderApi::vcall'{4}' (0926C75h)//E9 Offset1 Offset2 Offset3 Offset4//E9 A1 58 00 00

E9 表示一个短跳转,偏移量就是后面 4个字节组成的数值

经过这个跳转,来到新的代码块,在这里,才是调用虚函数表,调用虚函数:

    //跳转到调用虚拟函数的代码块mov eax, dword ptr [ecx]//8B 01jmp dword ptr [eax + x]//FF 60 X1//FF A0 X1 X2 X3 X4

这里ecx指向的就是类, 也是类的虚函数表
之后他就会jmp 到虚函数

我们则需要得到这个 x 的值, 他就表示虚函数在虚函数表中byte的offset

需要指出的一点是 jmp dword ptr [eax + x] 对应的语句分别是
FF 60 X1
FF A0 X1 X2 X3 X4
前者是1个字节表示X,后者是3个字节表示X

我们完成工具函数:

int DetectVFTableOffset(void* p_thunk_fn)
{auto vftable_offset = -1;//p_thunk_fn 指向一个 近跳转//jmp xti::XtTraderApi::vcall'{4}' (0926C75h)//E9 Offset1 Offset2 Offset3 Offset4//E9 A1 58 00 00//然后跳转到调用虚拟函数的代码块//mov eax, dword ptr [ecx]//8B 01//jmp dword ptr [eax + x]//FF 60 X1//FF A0 X1 X2 X3 X4auto jmp_o1_o2_o3_o4 = *(char*)p_thunk_fn;if (jmp_o1_o2_o3_o4 != (char)0xE9){throw std::exception("没有找到类似 jmp xti::XtTraderApi::vcall'{4}' (0926C75h) 的语句");}auto offset = *(int*)((char*)p_thunk_fn + 1);auto new_address = (char*)p_thunk_fn + 5 + offset;auto mov_eax_dword_ptr_ecx = *(short*)new_address;if (mov_eax_dword_ptr_ecx != 0x018B){//抛出异常throw std::exception("没有找到语句mov eax, dword ptr [ecx]");}//确定jmp dword ptr [eax + x]auto jmp_ff = *((char*)new_address + 2);if (jmp_ff != (char)0xFF){//抛出异常throw std::exception("没有找到类似 jmp dword ptr [eax + x]的语句");}//确定下一句是60,还是A0auto byte_60_A0 = *((char*)new_address + 3);if (byte_60_A0 == 0x60){//FF 60 X1vftable_offset = *((char*)new_address + 4);}else if (byte_60_A0 == (char)0xA0){//FF A0 X1 X2 X3 X4vftable_offset = *(int*)((char*)new_address + 4);}else{//抛出异常throw std::exception("没有找到类似 jmp dword ptr [eax + x]的语句, 预期的数据不是60或者A0");}return vftable_offset;
}

还有一个小问题,就是如何把 a_reqCreditDetail 变成 void*

auto a_reqCreditDetail = &XtTraderApi::reqCreditDetail;

a_reqCreditDetail是一个复杂的虚函数指针。
但是在底层汇编中, 我们知道, a_reqCreditDetail 指向的是一个thunk代码块( 这个代码块只有1条jmp语句)
但是我无法在c++层面把他优雅的转为void*
我只能用如下的语句实现

template<typename src_type>
void* pointer_cast(src_type src)
{void* ret = NULL;__asm{mov eax, srcmov ret, eax}return ret;
}

这个函数使用__asm在底层把a_reqCreditDetail转为了 void*

最后为了方便我wrap了两者,生成了新的函数

template<typename src_type>
int DetectVFTableOffsetEx(src_type src)
{auto p_thunk_fn = pointer_cast(src);return DetectVFTableOffset(p_thunk_fn);
}

这样我的调用看上去是这样的:

 auto vfo_setCallback = DetectVFTableOffsetEx(&XtTraderApi::setCallback);auto vfo_init = DetectVFTableOffsetEx(&XtTraderApi::init);auto vfo_initSync = DetectVFTableOffsetEx(&XtTraderApi::initSync);auto vfo_join = DetectVFTableOffsetEx(&XtTraderApi::join);auto vfo_join_async = DetectVFTableOffsetEx(&XtTraderApi::join_async);auto vfo_destroy = DetectVFTableOffsetEx(&XtTraderApi::destroy);

这样我就不用写很麻烦的转换,元编程给我解决了。

P.S ChatGPT 为我解释了机器语言我把对话内容贴出来,给大家参考:

翻译为机器码: mov         eax,dword ptr [ecx]
ChatGPT
在x86汇编语言中,指令 mov eax, dword ptr [ecx] 用于将ECX寄存器中的地址指向的32位(dword,即double word)值移动到EAX寄存器中。这条指令是数据传输指令的一部分,用于读取内存内容。当这条指令被转换成机器码时,它会被编码为一系列字节,这些字节在执行时由CPU解释为上述操作。对于这条具体的指令,其机器码(以Intel x86架构为例)将取决于指定的操作数和操作模式(例如,保护模式下的32位操作)。一个可能的机器码示例为(请注意,实际的机器码可以根据上下文和具体的指令格式有所不同):Copy code
8B 01
这里,8B 是操作码(opcode)表示MOV指令的一个变种,后续的01是指令的ModR/M字节,这里指定了源操作数(source operand)是通过ECX寄存器间接寻址的,并且目标操作数(destination operand)是EAX寄存器。解析机器码:
8B : MOV指令的操作码,表示这是一个从内存到寄存器的移动操作。
01 : ModR/M字节,指定了具体的寄存器和寻址模式。在这个例子中,它指示EAX是目标寄存器,而源数据来自ECX指向的内存地址。
请注意,实际使用中机器码的具体值可能因编译器、指令的上下文(如地址模式、段重载前缀等)以及目标平台的具体实现而有所不同。上述机器码仅为典型情况下的示例,用于说明mov eax, dword ptr [ecx]这条指令可能的二进制表示。在具体环境下,建议使用反汇编工具来获取精确的机器码。User
机器码: jmp         dword ptr [eax+4]
ChatGPT
指令 jmp dword ptr [eax+4] 在x86汇编中表示无条件跳转到一个地址,这个地址是通过寄存器 EAX 加上偏移量 4 来确定,并从那个计算得到的内存位置读取跳转目标地址。这种类型的跳转通常用于实现多态调用、函数指针调用等高级特性。将这条汇编指令转换为机器码的确切表示取决于多个因素,包括具体的处理器架构和编译器。然而,对于基于Intel x86的架构,这条指令通常会编译为以下形式的机器码:Copy code
FF 60 04
这里是如何解析这个机器码的:FF 是操作码(opcode),表示这是一个间接跳转(jmp)指令的一部分。
60 是ModR/M字节,指示这是一个基于寄存器加偏移量的间接寻址模式。
04 是偏移量,表示从 EAX 指向的地址开始,向前移动4个字节来找到跳转目标的地址。
说明
这个机器码序列使得CPU跳转到由 EAX 寄存器加4字节偏移所指向的内存地址中存储的地址处执行。这通常用在虚函数调用中,其中 EAX 可能指向一个对象的虚函数表(vtable),而具体的偏移量 4 指向表中的某个特定函数的地址。通过这种方式,可以实现根据对象的动态类型来选择调用哪个函数,这是实现多态的基础。请注意,具体的机器码可能因编译器的不同优化选项、目标平台的具体架构等因素而有所不同。上述机器码只是在典型情况下的一种可能表示。要获得准确的机器码表示,最好使用反汇编工具直接分析特定编译输出的二进制文件。User
机器码: jmp         dword ptr [eax+2ACh]
ChatGPT
指令 jmp dword ptr [eax+2ACh] 在x86汇编中执行一个无条件跳转到由 EAX 寄存器加上偏移量 2ACh(十进制为684)指向的内存地址处。这个地址中存储的值成为新的指令指针(即跳转目标)。这种跳转方式常见于基于表的跳转,如虚函数调用或其他间接调用中,其中 EAX 通常指向某种结构(如虚表)的开始,而具体的偏移量则指向该结构中的特定条目。将此汇编指令转换为机器码涉及到指定间接跳转的操作码,以及指定基址加偏移的寻址模式。对于Intel x86架构,大致的机器码形式可能如下:Copy code
FF A0 AC 02 00 00
这里的机器码分解如下:FF 是执行间接跳转指令的操作码。
A0 是ModR/M字节,它指定了使用基址加偏移的寻址方式,并且目标是间接通过内存地址给出的。
AC 02 00 00 是偏移量 2ACh 的小端表示(即,最低有效字节在前)。在小端存储约定中,地址 000002AC 实际上会被写作 AC 02 00 00。
注意事项:
真实的机器码和寻址方式可能会因具体的编译器、编译选项、目标架构的不同而有所变化。特别是在不同的处理器模式(如x86-64模式)中,指令的编码和处理可能会有显著差异。
上述机器码表示适用于32位x86架构。对于其他架构或模式(如x86-64),机器码会有所不同。
解析和理解机器码通常需要具备较深入的底层编程和架构知识。在实际开发中,除非在进行底层系统开发、逆向工程或特定类型的性能优化,否则一般不需要手动处理机器码。
要获得特定程序或指令的确切机器码表示,使用专业的反汇编工具或调试器查看编译后的二进制代码是最直接的方法。User
为何与机器码  jmp         dword ptr [eax+4]  差别那么大? 一个是 FF 60 04
ChatGPT
我之前的解释可能有些误导,对此我感到抱歉。让我来澄清一下。对于间接跳转指令(如 jmp dword ptr [eax+4] 和 jmp dword ptr [eax+2ACh]),确实存在一定的格式差异,主要是偏移量的大小影响了指令的编码方式。对于较小的偏移量(例如4),指令可能使用更短的编码形式。而对于较大的偏移量(如2ACh),则需要额外的字节来表示偏移量,因此会使得整个机器码变长。以 jmp dword ptr [eax+2ACh] 为例,正确的解析应当考虑到偏移量2ACh(十进制的684)如何影响机器码的表示。对于 jmp dword ptr [eax+4],一个更准确的机器码表示(假设是基于某些具体的编译器和设置)应该是像 FF 60 04 这样,这里:FF 是间接跳转的操作码。
60(实际上应该是一个包含ModR/M字节信息的值,这里我之前的解释有误,正确的值取决于具体的寻址模式和偏移量)。
04 是紧接着操作码的立即数,表示从基址寄存器指向的地址开始加上的偏移量。
而对于 jmp dword ptr [eax+2ACh],考虑到2ACh是一个较大的偏移量,不能简单地用单个字节来表示,所以需要更多的字节。指令格式可能更接近 FF A0 AC 02 00 00,这里:FF 同样表示这是一个间接跳转指令。
A0 或者相近的值(我之前的解释可能不够精确)指示了这是一个基址加偏移的寻址方式。
AC 02 00 00 是偏移量2ACh的具体值,以小端格式表示。
重要的是要理解,具体的机器码编码方式会受到多种因素的影响,包括但不限于寻址方式、偏移量的大小、目标处理器架构等。为了得到最准确的机器码,最好的方式是使用具体的编译器和反汇编工具直接生成或查看特定指令的编码。User
详细说说 jmp 语句转为 机器码
ChatGPT
jmp 指令是汇编语言中用于实现无条件跳转的指令。在x86架构下,这个指令有多种形式,可以直接跳转到指定的地址,也可以间接跳转到一个寄存器或内存地址中存储的地址。我们将通过一些例子来探讨如何将不同类型的 jmp 指令转换为机器码。直接跳转(Near Jump)
对于形式如 jmp 0x00401000 的直接跳转指令,它指示程序跳转到具体的地址执行。这种类型的跳转通常用于同一代码段内的跳转。在32位模式下,这类指令的机器码可能以 E9 开头,后跟一个相对偏移量,这个偏移量是目标地址与跳转指令下一条指令地址之间的差值。例如:css
Copy code
E9 [相对偏移量]
如果假设 jmp 0x00401000 指令的地址是 0x00401005,则相对偏移量将是 0x00401000 - (0x00401005 + 5)(这里的 5 是因为 E9 指令加上四字节偏移量的总长度)。间接跳转
间接跳转可以基于寄存器,如 jmp eax,或基于内存地址,如 jmp dword ptr [eax+4]。间接跳转让跳转的目的地更加灵活,常见于虚函数调用、跳转表等场景。基于寄存器的间接跳转
形式如 jmp eax 的指令表示跳转到 EAX 寄存器中存储的地址。这类指令的机器码通常由两部分组成:操作码和寄存器指示码。例如,jmp eax 的机器码可能是:Copy code
FF E0
这里,FF 是操作码,表示间接跳转,而 E0 是ModR/M字节,表示使用 EAX 寄存器作为跳转地址。基于内存的间接跳转
形式如 jmp dword ptr [eax+4] 的指令表示跳转到由 EAX 寄存器加上 4 偏移量指向的内存地址中存储的地址。这类指令的机器码同样分为操作码和寻址模式指示,例如:Copy code
FF 60 04
这里,FF 是操作码,60 是ModR/M字节,表示基于 EAX 的间接寻址,04 是具体的偏移量。注意事项
实际的机器码可能会根据具体的编译器、汇编器、目标架构(x86还是x86_64)、以及指令的具体使用场景有所不同。上面给出的机器码仅为示例,用于说明不同类型的 jmp 指令如何被编码。获取准确的机器码通常需要利用汇编器(如NASM、MASM)将汇编代码转换为机器码,或者使用反汇编工具(如IDA Pro、Ghidra)查看编译好的二进制文件中的机器码。User
ModR/M字节
ChatGPT
在x86架构的汇编语言中,ModR/M字节是一个关键组成部分,用于指定指令操作的寄存器和内存地址。它广泛用于指令编码中,以提供关于操作数(即指令作用的对象)的详细信息。ModR/M字节由三个部分组成:Mod字段、Reg/Opcode字段和R/M字段。组成
ModR/M字节共8位,按照位的分布,可以划分为三个部分:Mod(2位):最高的两位,指示操作数的类型(寄存器或内存地址)及寻址方式。它也决定了是否使用SIB字节(Scale-Index-Base,当寻址复杂时使用),以及是否包含位移(disp8或disp32)。
Reg/Opcode(3位):中间的三位,根据上下文,这部分要么用来指定一个寄存器,要么作为操作码的一部分。
R/M(3位):最低的三位,用来指定一个寄存器或者,结合Mod字段,表示内存寻址方式。
Mod字段值及含义
00:表示操作数是直接通过内存地址给出,或者(当R/M字段为100时)使用SIB字节寻址,或者(当R/M字段为101时)直接使用32位偏移量(disp32)。
01:表示操作数通过寄存器间接寻址,附加一个8位偏移量(disp8)。
10:表示操作数通过寄存器间接寻址,附加一个32位偏移量(disp32)。
11:表示操作数是直接在寄存器中,此时不使用内存寻址。
示例
假设有一条指令需要使用ModR/M字节来指定操作数,我们使用add eax, [ebx+4]作为例子。这条指令表示将EBX寄存器指向的内存地址加上偏移量4的内容加到EAX寄存器中。Mod字段:因为有一个8位偏移量,所以Mod为01。
Reg/Opcode字段:因为add操作目的地是EAX寄存器,EAX寄存器的编码为000,所以这部分也是000。
R/M字段:操作源是基于EBX的偏移,EBX的编码为011。
综合起来,ModR/M字节为01000011(二进制),或43(十六进制)。注意
实际的指令编码可能更复杂,包括使用SIB字节指定基址、索引和比例因子进行更复杂的内存寻址,或者根据具体指令包含立即数、位移等额外信息。理解和使用ModR/M字节需要熟悉指令集架构和具体的寻址模式。在汇编和反汇编工作中,正确解析ModR/M字节对于理解和生成正确的机器码至关重要。User
Reg/Opcode
ChatGPT
在x86架构的指令编码中,ModR/M字节是一个关键的编码组件,用于指定操作数(即指令作用的对象),其中包含了Mod字段、Reg/Opcode字段和R/M字段。Reg/Opcode字段是这个字节中的一部分,具有不同的作用,取决于具体的指令上下文。Reg/Opcode 字段
Reg/Opcode字段占据ModR/M字节的中间3位。它可以有两种用途:指定寄存器(Reg用途):在很多指令中,这个字段直接指定了一个寄存器,这个寄存器可以是源操作数、目的操作数,或者即是源操作数又是目的操作数之一。寄存器的指定遵循一定的编码规则,例如,000代表EAX/AX/AL(具体取决于操作数大小),001代表ECX/CX/CL,以此类推。扩展操作码(Opcode用途):对于一些指令,这3位并不指定寄存器,而是作为操作码的一部分,用来扩展指令的操作。这在一些如位操作(bit test、bit set等)和特定的算术操作指令中比较常见。举例说明
作为寄存器指定:例如,add eax, edx指令中,eax是目的操作数,而edx是源操作数。如果这条指令使用ModR/M字节来编码,Reg/Opcode字段将用于指示EDX寄存器,因为EDX的寄存器编码是010。作为操作码的一部分:指令test eax, imm32(测试EAX和一个立即数),其中test指令本身有一个基本的操作码,而ModR/M字节中的Reg/Opcode字段用作操作码的一部分,以区分test指令与其他可能共享同一基本操作码的指令。编码规则
寄存器编码:x86架构定义了一组通用寄存器的编码规则,例如,EAX是000,ECX是001,依此类推。这种编码同时适用于32位(如EAX)、16位(如AX)和8位(如AL)寄存器。操作码扩展:当Reg/Opcode字段作为操作码的一部分时,它扩展了基本操作码,允许编码更多的指令。例如,在进行算术运算时,这可以帮助区分是加法、减法还是按位与操作。总结
Reg/Opcode字段的具体意义和用途取决于指令本身的设计。在阅读或写入机器码时,正确解释这一字段对于理解指令的行为至关重要。不同的指令可能会以不同的方式使用这一字段,这就要求开发者或分析师熟悉x86指令集的细节。User
jmp dword ptr [eax+4] 的机器码是 FF 60 04,  讲解60 ,特别是其中 reg/opcode
ChatGPT
在x86架构下,指令jmp dword ptr [eax+4]是一条间接跳转指令,其目标地址存储在以eax寄存器加4偏移处的内存位置。这条指令的机器码是FF 60 04。这里,我们重点讨论中间的字节60,特别是其中的Reg/Opcode部分。机器码解析
FF是操作码(Opcode)部分,表示这是一条间接跳转指令。
60是ModR/M字节,用于指示操作数的具体寻址方式和相关寄存器。
04是指令中的立即数部分,表示相对于eax的偏移量。
ModR/M字节(60)
ModR/M字节的格式如下,共8位:最高的两位(Mod字段)指定寻址方式。
中间的三位(Reg/Opcode字段)扩展操作码或指定寄存器。
最低的三位(R/M字段)指定基寄存器或使用特定寻址模式。
对于60(二进制表示为01100000):Mod字段:01表示基寄存器加8位有符号位移(这里实际上用的是32位位移,由指令的上下文确定)。
Reg/Opcode字段:100,在这个上下文中,这部分并不是用来指示一个具体的寄存器,而是和操作码一起决定了操作的类型。对于FF操作码,100表示这是一条间接跳转(jmp)到内存地址的指令。
R/M字段:000,表示这个地址是基于EAX寄存器的。
在jmp dword ptr [eax+4]这条指令中,60字节通过其Mod和R/M部分指出了跳转目标地址基于EAX寄存器加上一个立即数偏移(在这条指令中偏移量由下一个字节04给出)。Reg/Opcode字段为100,与操作码FF结合,表明这是一个间接跳转。特别关注Reg/Opcode
在FF 60 04这个机器码中,Reg/Opcode字段(100)与jmp指令的操作码FF共同决定了操作的具体类型——即间接跳转到由[eax+4]给出的地址。在这个上下文中,Reg/Opcode字段不直接指定寄存器,而是作为操作码的一部分,指示了这是一个间接跳转指令。这显示了Reg/Opcode字段在指令编码中的灵活性:它既可以用于指定寄存器,也可以扩展基本操作码,以支持更多的指令。User
机器码 jmp         xti::XtTraderApi::`vcall'{4}' (0926C75h)
ChatGPT
在x86架构中,对于直接跳转(即跳转到硬编码的地址)和间接跳转(即跳转到寄存器或内存地址中存储的地址)的指令,机器码的表示形式会有所不同。你提到的jmp xti::XtTraderApi::vcall'{4}' (0926C75h)看起来像是想要直接跳转到一个特定的地址(在这个例子中是0926C75h`),但实际在编写汇编语言时,我们不直接编码目标地址。相反,我们使用符号名称(如函数名),编译器或链接器会解析这些符号到它们相应的地址。当我们在高级语言(如C++)中看到像xti::XtTraderApi::vcall'{4}'这样的表达式时,它代表一个虚函数调用的地址。但是在汇编级别,虚函数的调用通常是通过虚表(vtable)来实现的,即通过对象的内存布局中的虚表指针(vptr)间接访问。直接的跳转到虚函数地址(如jmp 0926C75h)在标准的x86汇编中并不是一个常见的操作,因为这要求编译时已经知道了虚函数的最终地址,这与虚函数的动态绑定特性相冲突。不过,如果我们确实需要在汇编中硬编码跳转到某个地址,比如说我们已经知道了某个函数在运行时的确切地址,并且我们想要直接跳转到那里,这通常会使用直接跳转的机器码。在x86汇编中,直接跳转的指令有多种形式,包括短跳转(jmp short)、近跳转(jmp near)和远跳转(jmp far),它们的机器码格式各不相同。例如,一个近跳转(jmp)到具体地址的指令可能会被编译成相对偏移形式(基于当前指令地址之后的偏移量),而不是直接编码为目标地址。如果我们假设jmp 0926C75h是一个有效的操作,并且编译器支持直接编码这个地址,它的机器码可能会依赖于指令的具体类型和目标地址。不过,请注意,直接通过机器码硬编码跳转到虚函数通常是不可行的,因为虚函数的地址在程序运行前是不确定的,它们依赖于运行时的动态链接。如果你需要在汇编中调用C++的虚函数,正确的做法是通过对象的虚表进行间接调用。User
机器码 E9 A1 58 00 00, 翻译成汇编语言
ChatGPT
机器码 E9 A1 58 00 00 表示一个近跳转(jmp)指令,这是一种相对跳转,意味着它会将当前指令指针(EIP寄存器的值)加上一个相对偏移量来计算跳转的目标地址。E9是近跳转指令的操作码,后面的四字节A1 58 00 00表示相对偏移量。为了将这个机器码翻译成汇编语言,我们需要解码相对偏移量。在这个例子中,偏移量以小端格式存储,这意味着最低有效字节在前。因此,偏移量为0x000058A1。这个偏移量是从跳转指令的下一条指令开始计算的,即从E9指令后的第一字节开始计算到跳转目标的偏移。假设E9 A1 58 00 00出现在地址0x1000(这里只是一个假设的示例地址),则跳转的目标地址计算如下:跳转指令的地址:0x1000
指令长度:5 字节(E9加上四字节的偏移)
下一条指令的地址:0x1000 + 0x5 = 0x1005
相对偏移量:0x000058A1
目标地址:0x1005 + 0x000058A1 = 0x158A6
因此,对应的汇编指令是从指令所在地址跳转到0x158A6。在具体的汇编代码中,这通常会被表示为:assembly
Copy code
jmp 0x158A6 ; 假设的目标地址
或者,如果你是在分析某个具体的可执行文件或正在编写汇编代码,你会根据实际的基地址和偏移来计算目标地址,然后使用符号标签来表示跳转目标。User
如果只看到 E9  , 就知道完整的语句是 5 个字节吗
ChatGPT
是的,如果你在x86架构下看到机器码以E9开头,你就可以知道这是一条近跳转(jmp)指令,其完整的指令长度将是5个字节。这5个字节由E9操作码本身加上后面的4个字节组成,这4个字节表示相对偏移量。在x86汇编中,近跳转指令jmp使用相对偏移量来计算跳转目标地址,该偏移量是相对于当前指令指针(EIP寄存器的值)加上jmp指令本身的长度(即E9后的第一字节开始)计算得到的。因此,当处理器执行到一个以E9开头的指令时,它会读取后面的4个字节作为偏移量,并据此计算出要跳转到的目标地址。

相关文章:

Windows下如何确定虚函数在虚函数表中的位置

我需要用c#调用 c 的 类的函数, 虽然可以通过头文件的顺序&#xff0c;但是如果可以打印出虚函数在虚表中的Offset更好。 测试要求: Windows, x86 只有1层虚函数&#xff0c;没有被override过 虚函数调用如下 auto a_reqCreditDetail &XtTraderApi::reqCreditDetail; (a…...

C++设计模式:观察者模式(三)

1、定义与动机 观察者模式定义&#xff1a;定义对象间的一种1对多&#xff08;变化&#xff09;的依赖关系&#xff0c;以便当一个对象&#xff08;Subject&#xff09;的状态发生比改变时&#xff0c;所有依赖于它的对象都得到通知并且自动更新 再软件构建过程中&#xff0c…...

CentOS运行Py脚本报错illegal instruction故障处理

测试Python脚本运行环境及依赖 [root@localhost network]# python3 devops_ping_test1.py Illegal instruction ①、illegal instruction报错 由于本人第一次测试时运行是正常的,但是在测试过程中多次修改、覆盖代码运行后提示Illegal instruction(非法指令),所以不能单…...

软件设计师——1.备考提纲

知识点说明比例软件工程基础知识11开发模型、设计原则、测试方法、质量特性、CMM、Pert图、风险管理14.67%面向对象12面向对象基本概念、面向对象分析与设计、UML、设计模式16.00%数据结构与算法10数组、栈、队列、树与二叉树、图、查找与排序、常见算法13.33%程序设计语言6文法…...

[开源] 基于GRU的时间序列预测模型python代码

基于GRU的时间序列预测模型python代码分享给大家&#xff0c;记得点赞哦 #!/usr/bin/env python # coding: utf-8import time time_start time.time() import numpy as np import matplotlib.pyplot as plt import pandas as pd import math from keras.models import Sequent…...

SQL SERVER 备份

目录 1.备份概念 1.1 为何备份? 1.2 SQL Server 备份模式 2.SQL Server 数据库备份 2.1 借助SSMS备份数据库 2.2 借助 T-SQL 备份数据库 2.3 创建加密备份 2.4 备份文件和文件组 权限 步骤 2.5 备份事务日志 3.维护计划 3.1 完整备份 3.2 差异备份...

提示词专场:从调整提示改善与LLMs的沟通,到利用LLMs优化提示效果

编者按&#xff1a;欢迎阅读“科研上新”栏目&#xff01;“科研上新”汇聚了微软亚洲研究院最新的创新成果与科研动态。在这里&#xff0c;你可以快速浏览研究院的亮点资讯&#xff0c;保持对前沿领域的敏锐嗅觉&#xff0c;同时也能找到先进实用的开源工具。 提示词的好坏决…...

测开面经(pytest测试案例,接口断言,多并发断言)

pytest对用户登录接口进行自动化脚本设计 a. 创建一个名为"test_login.py"的测试文件&#xff0c;编写以下测试脚本 import pytest import requests# 测试用例1&#xff1a;验证登录成功的情况 # 第一个测试用例验证登录成功的情况&#xff0c;发送有效的用户名和密…...

Golang 开发实战day09 - package Scope

&#x1f3c6;个人专栏 &#x1f93a; leetcode &#x1f9d7; Leetcode Prime &#x1f3c7; Golang20天教程 &#x1f6b4;‍♂️ Java问题收集园地 &#x1f334; 成长感悟 欢迎大家观看&#xff0c;不执着于追求顶峰&#xff0c;只享受探索过程 Golang 教程09 - package Sc…...

24考研-东南大学916经验贴

文章目录 一、个人情况二、初试备考经验1.政治 67&#xff0c;客观382.英语 60&#xff0c;客观大概40左右3.数学 136&#xff0c;客观应该满分4.专业课 数据结构计网 114小分不清楚 三、复试备考经验笔试&#xff1a;C面试复试流程 附一下成绩单&#xff1a; 一、个人情况 本…...

【AI面试】YOLO 如何通过 k-means 得到 anchor boxes的?Yolo、SSD 和 faster rcnn 的正负样本定义

如果你的项目中有目标检测相关的内容,那么本篇内容就一定要好好看看。不会的看到了理解下,会的看看是不是和自己理解的一样。 一、YOLO 如何通过 k-means 得到 anchor boxes的? YOLOv2 和 YOLOv3是目标检测领域中非常流行的算法,它们都使用了anchor boxes来提高检测的准确…...

MySQL高级篇(B-Tree、Btree)

目录 1、Btree&#xff08;B-Tree&#xff09; 1.1、B-Trees的特点 二叉树缺点&#xff1a;顺序插入时&#xff0c;会形成一个链表&#xff0c;查询性能大大降低。大数据量情况下&#xff0c;层级较深&#xff0c;检索速度慢。红黑树&#xff1a;大数据量情况下&#xff0c;层…...

Zookeeper脑裂解决方案

Zookeeper脑裂原因&#xff1a; 主要原因是Zookeeper集群和Zookeeper client判断超时并不能做到完全同步&#xff0c;也就是说可能一前一后&#xff0c;如果是集群先于client发现&#xff0c;那就会出现上面的情况。同时&#xff0c;在发现并切换后通知各个客户端也有先后快慢…...

常用日常脚本

日常脚本 1&#xff1a;主机初始化脚本 通用脚本&#xff1a; curl -s http://内网ip:3333/soft/shell/init/init_vm.sh |sh 以下是单一功能脚本 2&#xff1a;定时检测dns&#xff0c;并修改为固定dns curl -s http://内网ip:3333/soft/shell/init/deploy_dns_product.sh | s…...

Longan Pi 3H 开发板体验

Longan Pi 3H 开发板体验 开箱内容 打开包装&#xff0c;你可以看到以下物品 一个Longan Pi 3H盒子Longan Pi 3H开发板 产品基本介绍 Longan Pi 3H 是基于 Longan Module 3H 核心板的 ARM Linux 开发板&#xff0c;以 H618 (Quad core ARM Cortex-A531.5Ghz , 64-bit) 为主控…...

SpringCloud Alibaba Sentinel 创建流控规则

一、前言 接下来是开展一系列的 SpringCloud 的学习之旅&#xff0c;从传统的模块之间调用&#xff0c;一步步的升级为 SpringCloud 模块之间的调用&#xff0c;此篇文章为第十四篇&#xff0c;即介绍 SpringCloud Alibaba Sentinel 创建流控规则。 二、基本介绍 我们在 senti…...

Mysql底层原理五:如何设计、用好索引

1.索引的代价 空间上的代价 时间上的代价 每次对表中的数据进⾏增、删、改操作时&#xff0c;都需要去修改各个B树索引。⽽且我们讲过&#xff0c;B树每层节点都是按照索引列的值从⼩到⼤的顺序排序⽽组成了双 向链表。不论是叶⼦节点中的记录&#xff0c;还是内节点中的记录&a…...

python学习杂记

做为一个接近40岁的人&#xff0c;开始学习python会有什么结果&#xff1f;反正很迷茫&#xff0c;思维方式也开始下降了&#xff0c;希望可以学得好吧 早期做的是前端开发&#xff0c;java也有所接触&#xff0c;但是都学得不精&#xff0c;后来转做项目管理&#xff0c;把技…...

C# Socket发送、接收结构体

Socket发送&#xff1a;Socket的使用 一、Socket发送结构体 结构体如下&#xff1a; [StructLayout(LayoutKind.Sequential, Pack 1)] public struct OutPoint_ST {public int LeftheartX;public int LeftHeartY;public float WidthHeart;public int RightHeartX;public in…...

ics-05-攻防世界

题目 点了半天只有设备维护中心能进去 御剑扫一下 找到一个css 没什么用 再点击云平台设备维护中心url发生了变化 设备维护中心http://61.147.171.105:65103/index.php?pageindex试一下php伪协议 php://filter/readconvert.base64-encode/resourceindex.php base64解一下…...

Web API(三)之事件流事件委托其他事件

Web API(三)之事件流&事件委托&其他事件 事件流捕获和冒泡事件捕获事件冒泡阻止冒泡解绑事件两种注册事件的区别事件委托其他事件页面加载事件元素滚动事件页面滚动事件-获取位置页面滚动事件-滚动到指定的坐标页面尺寸事件元素尺寸与位置元素尺寸与位置-尺寸...

SSL证书的作用是什么?

SSL证书让网站和用户之间安全传输信息&#xff0c;就像给网络对话加了一把密码锁。它主要做四件事&#xff1a; 1. 证明身份&#xff1a; - 像警察局一样&#xff0c;有个叫“证书颁发机构”的家伙负责检查网站是不是真的。网站要向它证明自己是谁&#xff08;比如&#xff0c;…...

皮具5G智能制造工厂数字孪生可视化平台,推进企业数字化转型

皮具5G智能制造工厂数字孪生可视化平台&#xff0c;推进企业数字化转型。随着信息技术的快速发展&#xff0c;数字化转型已成为企业提升竞争力、实现可持续发展的关键路径。皮具行业&#xff0c;作为一个传统的手工制造业&#xff0c;正面临着巨大的市场变革和技术挑战。如何在…...

RH850从0搭建Autosar开发环境【3X】- Davinci Configurator之Port模块配置详解(MCAL配置)

Port模块配置详解 前言一、如何添加Port模块?1.1 导入Port模块二、Port模块详细配置说明2.1 Port模块问题解决2.2 Port模块配置步骤2.2.1 数据手册查找Port对应的Group2.2.2 配置Port为CAN功能2.2.3 选择芯片型号总结前言 我们还差一个Port模块进行配置io的复用功能选择。就是…...

多叉树题目:子树中标签相同的结点数

文章目录 题目标题和出处难度题目描述要求示例数据范围 解法思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;子树中标签相同的结点数 出处&#xff1a;1519. 子树中标签相同的结点数 难度 5 级 题目描述 要求 给你一个树&#xff08;即一个连通的无向无环图…...

帝国CMS模板源码整站安装说明(图文)

安装步骤 第一步&#xff1a;先把得到的文件解压缩&#xff0c;把文件通过FTP传到空间里。&#xff08;请不要把类似www.lengleng.net这个文件夹传到FTP&#xff0c;请传这个大文件夹下面的所有文件夹和文件到空间根目录&#xff0c;请不要上传到2级目录&#xff0c;除非你自己…...

物联网系统未来的发展趋势

一、引言 物联网系统作为新一代的信息技术&#xff0c;正在逐渐改变我们的生活和工作方式。随着物联网技术的不断发展和应用场景的拓展&#xff0c;未来物联网系统的发展趋势将更加明显。本文将从技术、应用、安全等方面探讨物联网系统未来的发展趋势。 二、技术发展趋势 1.…...

基于支持 GPT 的服务的初创公司

Kafkai&#xff1a;多语言长篇内容生成&#xff0c;AI写作的新趋势 介绍 随着生成式预训练 Transformer (GPT) 的出现&#xff0c;技术世界正在见证范式转变。 这种人工智能驱动的创新不仅仅是一种转瞬即逝的趋势&#xff0c;而是一种趋势。 它已成为科技行业的基石&#xff0c…...

基于springboot实现教师人事档案管理系统项目【项目源码+论文说明】

基于springboot实现IT技术交流和分享平台系统演示 摘要 我国科学技术的不断发展&#xff0c;计算机的应用日渐成熟&#xff0c;其强大的功能给人们留下深刻的印象&#xff0c;它已经应用到了人类社会的各个层次的领域&#xff0c;发挥着重要的不可替换的作用。信息管理作为计算…...

上行上传rsync+inotify

引言 使用inotify通知接口&#xff0c;可以用来监控文件系统的各种变化情况&#xff0c;如文件存取、删除、移动、修改等。利用这一机制&#xff0c;可以非常方便地实现文件异动告警、增量备份&#xff0c;并针对目录或文件的变化及时作出响应。 将inotify机制与rsync工具相结合…...

如何搭建 seo网站/电商运营公司

方法一&#xff1a;采用直接追加文件内容到hosts文件的方式&#xff1a; 1). adb shell //进入android系统shell命令模式 2). adb root adb remount //两条指令获取root权限 3). echo -e test >> /system/etc/hosts //hosts里追加了test文字 方…...

分享音乐到wordpress/seo网站关键字优化

1.启动程序并输入到指定日志 nohup python manage.py runserver 0.0.0.0:9090 > /data/zyj/xadstat/xadstat.log 2&>1 & 2.查看当前进程号&#xff08;可能会起两个进程要杀死两次&#xff09; ps -ef |grep python 3.杀死进程 kil -9...

手机网站模板 导航/百度关键词排名手机

弱监督任务就是原本没有标签&#xff0c;所以通过一定的方法构造出伪标签作为Ground Truth来监管训练&#xff0c;通常是交替迭代训练&#xff0c;即一边训练网络完成预测&#xff0c;一边又用该预测结果作为新的Ground Truth来训练网络. 1.《Learning to Detect Salient Obje…...

广州有哪些网站建设/正规接单赚佣金的平台

今天我们来看一看Linux中的gdb调试和Makefile命令。一。gdb:调试器。注&#xff1a;要用gdb调试&#xff0c;前提是在源代码生成时要加上-g选项才能应用&#xff0c;否则会失败。它的基本命令有&#xff1a;l行号&#xff1a;显示从第几行开始&#xff0c;屏幕每次只能显示10行…...

像素人物制作网站/2345王牌浏览器

题目 L1-070 吃火锅 (15 分) chg.jpg 以上图片来自微信朋友圈&#xff1a;这种天气你有什么破事打电话给我基本没用。但是如果你说“吃火锅”&#xff0c;那就厉害了&#xff0c;我们的故事就开始了。 本题要求你实现一个程序&#xff0c;自动检查你朋友给你发来的信息里有没…...

做展示类网站/惠州seo外包公司

不知道你在使用mac vim编辑器的时候&#xff0c;整个编辑界面就一个颜色&#xff0c;也没有行号&#xff0c;是不是很崩溃&#xff1f;那么我们来简设置一下&#xff0c;变得很漂亮&#xff01; 第一步&#xff1a;如果你不想改默认的设置文件&#xff0c;那么我们复制一份&am…...