64位驱动中加入汇编代码

本文介绍如何在驱动程序的C文件中调用ASM文件中导出的汇编函数
引言
    Windows驱动程序使用DDK或者IFSDDK(以下简称DDK)中的Build.exe程序对源文件进行编译和链接,操作时只需启动相应的DDK命令行程序,进入待编译的源文件目录,执行Build命令即可得到.sys文件。
    Build命令实际上是调用了一个nmake程序,nmake程序随后调用cl.exe和link.exe,并指定所有的编译和链接选项,与应用程序不同,程序员在使用build命令时无法对编译链接选项进行修改,从而无法在build命令中干预编译或者链接过程。
    64位平台不再支持内嵌式的汇编代码,为了使驱动程序中仍然可以使用汇编语言,通常的做法是将汇编代码写成独立的函数放入单独的汇编文件中,并对汇编文件进行编译形成obj文件,而后在链接期对所有目标文件(包括驱动C代码的目标文件)进行链接。但是DDK内置的Build程序并不直接支持对汇编文件的编译和链接(也可能我还没有发现这样的方法)。因此需要通过其他手段将汇编代码目标文件和C代码目标文件进行链接。本文讲述的方法是通过修改SOURCE文件和强制指定外部函数调用约定的方式来达到在Windows驱动程序中调用独立汇编函数的目的。
需要注意的问题
1.32位平台和64位平台中调用约定的不同
    32位平台下,驱动程序默认采用__stdcall调用约定,该约定编译生成的函数名均带有后缀@xxx,而汇编代码采用__cdecl调用约定,用汇编器编译生成的函数名并不带后缀,从而导致C代码无法引用汇编文件中的函数(链接器会报错:unresolved external asm_rng_available@xxx)。解决这一问题的方法是强制指定调用外部的汇编函数为__cdecl调用约定,即如果在C文件中调用汇编文件中的asm_rng_available函数,声明方式应如下:
    extern int          __cdecl asm_rng_available() ;
强制指定__cdecl调用约定后可以保证C编译器生成的asm_rng_available函数名和汇编文件中生成的函数名是一样的。
    64位体系结构中只有一个本机调用约定和一个__cdecl约定可以被编译器忽略,其他的调用约定都已经被清除,所以64位下的DDK编译器生成的函数名和汇编器生成的函数名是一致的,函数名不会带有后缀,因此不必修改。即如果在C文件中调用汇编文件中的asm_rng_available函数,声明方式如下:
    extern int          asm_rng_available() ;


2.SOURCE文件的修改
    由于Build命令不会自动关联汇编文件的目标文件,为了将编译后的汇编目标文件和C目标文件进行链接,可以在SOURCE文件的TARGETLIBS宏中进行指定。具体方法如下:
    TARGETLIBS=.\instr_32.obj
这样就可以使用Build命令直接对所有目标文件进行链接了。
3.64位和32位中一些系统调用的差异
    64位DDK中的lib库并没有导出KeInitializeSpinLock, KeQueryInterruptTime函数,因此不能直接调用,必须通过MmGetSystemRoutineAddress得到函数的指针,利用函数指针进行调用。
驱动中调用汇编函数
    下面对整个过程进行完整的描述:
    1. 将汇编文件和驱动程序的C文件放在同一个目录下(简称驱动目录);
    2. 修改SOURCE文件,在SOURCE文件中添加TARGETLIBS = *.obj,其中*表示汇编文件的文件名(不加扩展名);
    3. 在驱动程序C文件中声明外部汇编文件的函数名,如果是32位体系结构,声明时需强制指定汇编函数为__cdecl调用约定;
    4. 编译汇编文件,在驱动目录下生成obj目标文件(编译器可以采用nasm或者masm);
    5. 在DDK编译环境下对整个驱动工程执行Build命令,从而生成.sys文件。
OK...


因为对于C语言,在vs2005.net环境下可直接进行64位平台的编译,其中注意事项已有许多文章涉及,这里不再复述。

A、而对于汇编语言,首先要注意,必须为纯汇编格式(*.asm文件)或intrinsic指令格式。其次,在64位平台上,不建议使用nasm编译器(我没找到其对于64位编译的相关支持),而建议采用yasm,这个汇编编译器是在nasm的基础上产生的,可以说对nasm的功能兼容,并且支持64位编译,详细地介绍及相关下载见:http://www.tortall.net/projects/yasm/

说明:可以使用Visual Studio中自带的ml64.exe对64位汇编源文件进行编译:


示例图:


B、x86-64较x86-32多了8个通用寄存器,而且,每个通用寄存器都是64位宽,它们是:
rax,rbx,rcx,rdx,rsi,rdi,rsp,rbp
r8,r9,r10,r11,r12,r13,r14,r15

同时,x86-64全面支持x86-32和x86-16的通用寄存器:
eax,ax,al,ah,
ebx,bx,bl,bh,

但是,在对寄存器进行入/出栈操作时只能对相应的64位寄存器入/出栈,即:
( Instructions that modify the stack (push, pop, call, ret, enter, and leave) are implicitly 64-bit. Their 32-bit counterparts are not available, but their 16-bit counterparts are. Examples in NASM syntax: 
    push eax  ; illegal instruction
    push rbx  ; 1-byte instruction
    push r11  ; 2-byte instruction with REX prefix)

C、关于x64的调用约定:

在设计调用约定时,x64 体系结构利用机会清除了现有 Win32 调用约定(如 __stdcall、__cdecl、__fastcall、_thiscall 等)的混乱。在 Win64 中,只有一个本机调用约定和 __cdecl 之类的修饰符被编译器忽略。除此之外,减少调用约定行为还为可调试性带来了好处。

您需要了解的有关 x64 调用约定的主要内容是:它与 x86 fastcall 约定的相似之处。使用 x64 约定,会将前 4 个整数参数(从左至右)传入指定的 64 位寄存器:

RCX: 1st integer argument第一个参数在RCX寄存器存放
RDX: 2nd integer argument第二个参数在RCX寄存器存放
R8: 3rd integer argument第三个参数在RCX寄存器存放
R9: 4th integer argument第四个参数在RCX寄存器存放

前 4 个以外的整数参数将传递到堆栈。该指针被视为整数参数,因此始终位于 RCX 寄存器内。对于浮点参数,前 4 个参数将传入 XMM0 到 XMM3 的寄存器,后续的浮点参数将放置到线程堆栈上。

更进一步探究调用约定,即使参数可以传入寄存器,编译器仍然可以通过消耗 RSP 寄存器在堆栈上为其预留空间。至少,每个函数必须在堆栈上预留 32 个字节(4 个 64 位值)。该空间允许将传入函数的寄存器轻松地复制到已知的堆栈位置。不要求被调用函数将输入寄存器参数溢出至堆栈,但需要时,堆栈空间预留确保它可以这样做。当然,如果要传递 4 个以上的整数参数,则必须预留相应的额外堆栈空间。


让我们看一个示例。请考虑一个将两个整数参数传递给子函数的函数。编译器不仅会将值赋给 RCX 和 RDX,还会从 RSP 堆栈指针寄存器中减去 32 个字节。在被调用函数中,可以在寄存器(RCX 和 RDX)中访问参数。如果被调用代码因其他目的而需要寄存器,可将寄存器复制到预留的 32 字节堆栈区域中。图 6 显示在传递 6 个整数参数之后的寄存器和堆栈。


图 6 传递整数

对这里的参数压栈,需要说明的是(对于整形参数):
1、第四个参数后的参数按顺序反向压栈(8字节)
2、为前四个参数预留的32字节占空间是固定的、预分配的,不用被调用函数的编写者负责,也就是说,不管你是否用到,这部分栈空间都占用了,rsp所指向的位置如上图。
所以对于上面的例子,如果在被调用函数重要访问p5,p6正确的方法是:
mov eax,[esp+32+8];p5
mov ebx,[esp+32+16];p6

 D、一个很特别的寄存器 rip,相当于x86-32的eip. 在x86-32是不可直接访问的,如mov eax,eip是错的,但在x86-64位下却可以,如 mov,rax,qword ptr [rip+100]是对的。而且,它除了是个程序计数器外,也是个“数据基地址”,有此可见,它现在是身兼两职!为什么在x86-64位下要用rip做访问数据的基地址呢?因为,在x86-64下,DS,ES,CS,SS都没有实际意义了,也就是说,它们不再参与地址计算,只是为了兼容x86-32。FS,GS还是参与地址计算,它们两个和x86-32的意义相同。由于DS,ES,CS,SS没有意义了,所以在编译动态库时,符号变量是不允许直接出现在汇编代码中的,而必须与rip一起使用,即
mov rax,[symb wrt rip]或者lea rbx,[symb wrt rip]

参考网页:
AMD64 Architecture ---http://www.tortall.net/projects/yasm/wiki/AMD64
开始进行 64 位 Windows 系统编程之前需要了解的所有信息---http://www.microsoft.com/china/MSDN/library/Windev/64bit/issuesx64.mspx?mfr=true
The history of calling conventions, part 5: amd64---http://blogs.msdn.com/oldnewthing/archive/2004/01/14/58579.aspx



Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1371665


下面给出一个示例代码:

汇编中实现一个加法函数,实现四个参数传入,分别对应上面介结的RCX第一个参数,RDX第二个参数,R8第三个参数,R9第四个参数

x64asm.asm文件名

.code
AddSub proc
push rcx

add rcx,rdx
add rcx,r8
add rcx,r9
mov rax,rcx

pop rcx
ret
AddSub endp
end


在驱动中调用汇编代码中提供的加法函数:AddSub

x64AsmDriver.c源文件名

#include <ntddk.h>

extern int AddSub(PVOID, PVOID, PVOID, PVOID);

VOID DriverUnload( IN PDRIVER_OBJECT DriverObject )
{
KdPrint(("DriverUnload OK!\n"));
}

#pragma code_seg("INIT")
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING pRegPath
)
{
ULONG32 nResult = 0;
ULONG32 a = 10;
ULONG32 b = 20;
ULONG32 c = 30;
ULONG32 d = 40;

DriverObject->DriverUnload = DriverUnload;

nResult = AddSub(a, b, c, d);
KdPrint(("nResult = %d\n", nResult));

return STATUS_SUCCESS;
}


说明:关于驱动Source文件配置:将汇编产生的OBJ文件加到这里TARGETLIBS=.\x64Asm.obj

TARGETNAME=x64AsmDriver
TARGETTYPE=DRIVER
TARGETLIBS=.\x64Asm.obj


SOURCES=x64AsmDriver.c




  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: x64dbg是一款功能强大的开源调试工具,它可以用于分析和调试32位和64位应用程序。而vt过驱动保护(Virtualization Technology-based code protection)是一种基于虚拟化技术的代码保护方法。 在使用x64dbg进行分析和调试时,有时会遇到一些应用程序使用了驱动程序进行保护,这些驱动程序可以实现对应用程序的安全保护,例如防止被调试、反反调试等。这时,我们需要绕过这些驱动程序的保护,以便更好地进行分析和调试。 x64dbg vt过驱动保护是指通过虚拟化技术来绕过驱动程序的保护。它的原理是在物理机上构建一个虚拟环境,将应用程序和保护驱动程序都运行在虚拟环境进行分析和调试。通过虚拟化技术,我们可以对驱动程序的行为进行分析,找出其保护的特征和机制,进而绕过它们。 具体实现vt过驱动保护的步骤大致如下: 1. 寻找应用程序使用的保护驱动程序; 2. 对驱动程序进行虚拟化处理,将其加载到虚拟环境; 3. 运行虚拟环境,并将目标应用程序加载到虚拟环境; 4. 在虚拟环境使用x64dbg进行分析和调试。 通过vt过驱动保护,我们可以绕过驱动程序的防护,实现对应用程序的深入分析和调试。但需要注意的是,vt过驱动保护仅用于研究和教育目的,使用时需遵守法律法规,并且尊重软件的使用许可协议。 ### 回答2: x64dbg vt过驱动保护是指在使用x64dbg调试工具时,如何绕过或者应对针对驱动的保护机制。 在调试驱动程序时,为了防止恶意用户通过调试工具对驱动程序进行逆向工程、解密或修改等操作,开发者通常会加入一些保护机制,如反调试技术。 针对这些保护机制,可以通过以下方式进行绕过或应对: 1. 使用反反调试插件:x64dbg支持使用插件扩展功能,可以通过安装一些专门的反反调试插件来对抗驱动的反调试机制。 2. 分析并修改驱动代码:通过仔细分析驱动程序的代码,了解其的保护机制实现方式,并进行必要的修改,以绕过或应对这些保护措施。 3. 使用虚拟机调试:在虚拟机环境调试驱动程序,因为虚拟机对调试工具的防护相对较弱,可以更轻松地进行调试和分析。 4. Hook相关API函数:通过在调试程序hook一些与保护相关的API函数,可以改变这些API函数的行为,使得驱动程序无法检测到调试环境。 5. 使用其他调试工具:x64dbg是一款强大的调试工具,但并不是唯一选择。根据需要,可以尝试其他调试工具,寻找更适合绕过驱动保护的工具。 绕过或应对驱动保护是一项技术挑战,需要有一定的反汇编和逆向工程知识。在进行任何操作前,请确保遵守法律法规,并仅在合法授权的范围内使用这些技术。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值