1.支持32位应用程序是64位OS的核心功能
Q:在64位OS中支持现有的32位应用程序运行的机制是什么?
A:WOW64(即windows on windows 64)
Q:具体是如何实现的?
A:
64位windows提供了32位windows的系统环境,借助WOW64将其变化为64位环境
此处需要注意的是:
1.WOW仅在用户模式下运行
2.运行在内核模式中的驱动程序(Driver)文件必须编译成64位。
3.内核模式中发生内存引用错误时,就会引发BSOD(BlueScreen Of Death)蓝屏死机
2.两者文件夹结构的区别
代码验证
#include <iostream> #include <Windows.h> int main() { char systemDirectory[MAX_PATH]; GetSystemDirectoryA(systemDirectory, MAX_PATH); std::cout << systemDirectory << std::endl; return 0; }
首先是在32位环境先输出
然后再在64位环境下输出
可以看到返回结果都是system32
这是因为WOW64在中间截获了API调用并进行操作后返回的结果
同理,32位的应用程序使用SHGetSpecialFolderPath()API获取Progarm File文件夹路径时,WOW64会在中间对其进行截获,并返回Program Files(X86)路径
3.在注册表项的区别
在software项下存在一个WOW6432Node子项
当32位的进程访问HKLM\SOFTWARE下的键时,WOW64会将其重定向到32位的HKLM\SOFTWARE\WOW6432Node下。
4.X64中新增的项目
最显著的变化:内存地址是64位,指针大小是64位,寄存器和栈的基本单位也是64位
机器 | 32 | 64 |
进程的虚拟内存 | 4GB | 16TB |
通用寄存器 | EAX…… | 新增R8-R15, 不使用段寄存器 |
CALL/JMP指令 | FF15 XXXXXXXX 其中XXXXXXXX绝对地址指向IAT区域的某一个位置 | 在FF15之后仍然跟着4个字节大小,不过是相对地址 Q:相对地址是怎么转化成绝对地址的呢? A:CALL指令所在地址+相对地址+6(CALL指令的长度)=变换后的绝对地址 |
函数调用约定 | 存在3种函数调用约定,分别是cdcall,stdcall,fastcall, | 在64位系统中的是统一的变形后的fastcall 最多可以将4个参数传递到寄存器中,如上是使用寄存器的顺序 当参数的个数超过4个的时候,第五个参数会在栈中进行传递,后期栈的清理由函数调用者完成, 注意:函数的前四个参数虽然是使用寄存器进行传递的,但是在栈中依然保留了4个参数也就是32个字节的空间 |
栈\栈帧 | 使用push指令传递参数 | 通过mov指令操作寄存器和预定的栈来传递 优点是调用子函数时不需要改变栈指针,函数返回时也不需要清理堆栈 |
5.代码验证
#include <windows.h> #include <stdio.h> int main() { HANDLE hFile; // 使用 CreateFile 函数创建文件 hFile = CreateFile( L"example.txt", // 文件名 GENERIC_WRITE, // 访问权限:写入 0, // 共享模式:无 NULL, // 安全属性:默认 CREATE_ALWAYS, // 打开方式:如果文件存在则覆盖,否则创建 FILE_ATTRIBUTE_NORMAL, // 文件属性:普通文件 NULL // 模板句柄:无 ); if (hFile != INVALID_HANDLE_VALUE) { // 关闭文件句柄 CloseHandle(hFile); } return 0; // 返回零值表示程序正常结束 }
32
调用子函数时用栈传递参数
在32位环境下采用stdcall,属于内平栈,由被调用者清理栈
进入CreateFile()API 中查看
768DA520 | 8BFF | mov edi, edi | 768DA522 | 55 | push ebp | 768DA523 | 8BEC | mov ebp, esp | 768DA525 | 51 | push ecx | 768DA526 | 51 | push ecx | 768DA527 | 8B55 | mov edx, dword ptr ss:[ebp+0x8] | 768DA52A | 8D4D | lea ecx, dword ptr ss:[ebp-0x8] | 768DA52D | E8 6 | call <kernelbase._Basep8BitStringToDynamicUnico | 768DA532 | 85C0 | test eax, eax | eax:___argc 768DA534 | 74 2 | je kernelbase.768DA564 | 768DA536 | 56 | push esi | esi:&"C:\\Users\\win2021ltsc\\AppData\\Local\\Temp\\vmware-win2021ltsc\\VMwareDnD\\7158df7b\\RVATOFOA.exe" 768DA537 | FF75 | push dword ptr ss:[ebp+0x20] | 768DA53A | FF75 | push dword ptr ss:[ebp+0x1C] | 768DA53D | FF75 | push dword ptr ss:[ebp+0x18] | 768DA540 | FF75 | push dword ptr ss:[ebp+0x14] | [ebp+14]:__RtlUserThreadStart+2F 768DA543 | FF75 | push dword ptr ss:[ebp+0x10] | 768DA546 | FF75 | push dword ptr ss:[ebp+0xC] | [ebp+C]:@BaseThreadInitThunk@12 768DA549 | FF75 | push dword ptr ss:[ebp-0x4] | 768DA54C | E8 E | call <kernelbase._CreateFileW@28> | 768DA551 | 8BF0 | mov esi, eax | esi:&"C:\\Users\\win2021ltsc\\AppData\\Local\\Temp\\vmware-win2021ltsc\\VMwareDnD\\7158df7b\\RVATOFOA.exe", eax:___argc 768DA553 | 8D45 | lea eax, dword ptr ss:[ebp-0x8] | eax:___argc 768DA556 | 50 | push eax | eax:___argc 768DA557 | FF15 | call dword ptr ds:[<&_RtlFreeUTF8String@4>] | 768DA55D | 8BC6 | mov eax, esi | eax:___argc, esi:&"C:\\Users\\win2021ltsc\\AppData\\Local\\Temp\\vmware-win2021ltsc\\VMwareDnD\\7158df7b\\RVATOFOA.exe" 768DA55F | 5E | pop esi | esi:&"C:\\Users\\win2021ltsc\\AppData\\Local\\Temp\\vmware-win2021ltsc\\VMwareDnD\\7158df7b\\RVATOFOA.exe" 768DA560 | C9 | leave | 768DA561 | C2 1 | ret 0x1C | 768DA564 | 83C8 | or eax, 0xFFFFFFFF | eax:___argc 768DA567 | EB F | jmp kernelbase.768DA560 |
在调用CreateFile()API之前就使用过Push指令将接收的参数压入栈,此时栈中有两份相同的参数()
Q:为什么参数要传递两次呢
A:这是因为函数支持多种字符编码或者字符串格式
64
00007FF84EE62630 | 48:83EC 78 | sub rsp,0x78 | 00007FF84EE62634 | 4C:8BC9 | mov r9,rcx | 00007FF84EE62637 | 48:8B05 B2791100 | mov rax,qword ptr ds:[<&BaseThreadInitT | 00007FF84EE6263E | 48:85C0 | test rax,rax | 00007FF84EE62641 | 74 10 | je ntdll.7FF84EE62653 | 00007FF84EE62643 | 4C:8BC2 | mov r8,rdx | 00007FF84EE62646 | 48:8BD1 | mov rdx,rcx | 00007FF84EE62649 | 33C9 | xor ecx,ecx | 00007FF84EE6264B | FF15 AF091300 | call qword ptr ds:[0x7FF84EF93000] | 00007FF84EE62651 | EB 20 | jmp ntdll.7FF84EE62673 | 00007FF84EE62653 | 48:8BCA | mov rcx,rdx | 00007FF84EE62656 | 49:8BC1 | mov rax,r9 | 00007FF84EE62659 | FF15 A1091300 | call qword ptr ds:[0x7FF84EF93000] | 00007FF84EE6265F | 8BC8 | mov ecx,eax | 00007FF84EE62661 | E8 8A1F0000 | call <ntdll.RtlExitUserThread> | 00007FF84EE62666 | 90 | nop | 00007FF84EE62667 | 8BD0 | mov edx,eax | 00007FF84EE62669 | 48:83C9 FF | or rcx,0xFFFFFFFFFFFFFFFF | 00007FF84EE6266D | E8 6EAC0400 | call <ntdll.NtTerminateProcess> | 00007FF84EE62672 | 90 | nop | 00007FF84EE62673 | 48:83C4 78 | add rsp,0x78 | 00007FF84EE62677 | C3 | ret |