X64 初接触续
正好这三天看一下这方面的资料,希望可以有所收获。看X64的汇编,得先去弄明白这几个事情:
一方面是看官方文档,找点资料,另一方面自行事件,非亲眼所见的东西都不靠谱。
http://msdn.microsoft.com/zh-cn/library/h2k70f3s.aspx
先来个下马威,EXE的载入的默认基地址是大于 0xFFFF-FFFF的,当然用户也可以指定基地址。
一、传参,先看非浮点的。
参数小于4,参数大于4。谁去平衡堆栈,调用系统 stdcall 和 cdecl 的堆栈平衡。X64使用 __vectorcall 调用约定 ...
// DEBUG 编译
000000013F6B197D | B9 01 00 00 00 | mov ecx,1 | ;crtexe.c:527
000000013F6B1982 | FF 15 10 9B 00 00 | call qword ptr ds:[<__imp__CrtSetCheckC |
000000013F6B1988 | 4C 8B 1D 11 9B 00 00 | mov r11,qword ptr ds:[<__imp___winitenv | ;crtexe.c:582
000000013F6B198F | 48 8B 05 DA 77 00 00 | mov rax,qword ptr ds:[<envp>] |
000000013F6B1996 | 49 89 03 | mov qword ptr ds:,rax | ;r11:__winitenv
000000013F6B1999 | 4C 8B 05 D0 77 00 00 | mov r8,qword ptr ds:[<envp>] | ;crtexe.c:583
000000013F6B19A0 | 48 8B 15 D1 77 00 00 | mov rdx,qword ptr ds:[<argv>] |
000000013F6B19A7 | 8B 0D BB 77 00 00 | mov ecx,dword ptr ds:[<argc>] |
000000013F6B19AD | E8 6C F6 FF FF | call <testasm64.@ILT+25(wmain)> |
// 为什么没有看到调用函数前先把堆栈空间申请出来,函数内就直接用 RSP+XX 往里塞数据了,看下文解释 ....
int _tmain(int argc, _TCHAR* argv[])
000000013F6B1140 | 48 89 54 24 10 | mov qword ptr ss:,rdx | ;testasm64.cpp:29
000000013F6B1145 | 89 4C 24 08 | mov dword ptr ss:,ecx |
000000013F6B1149 | 57 | push rdi |
000000013F6B114A | 48 81 EC 90 00 00 00 | sub rsp,90 |
000000013F6B1151 | 48 8B FC | mov rdi,rsp |
000000013F6B1154 | 48 B9 24 00 00 00 00 00| mov rcx,24 |
000000013F6B115E | B8 CC CC CC CC | mov eax,CCCCCCCC |
000000013F6B1163 | F3 AB | rep stos dword ptr es: |
....
000000013F6B1247 | 48 81 C4 90 00 00 00 | add rsp,90 |
000000013F6B124E | 5F | pop rdi |
000000013F6B124F | C3 | ret |
01.函数内基本看不到再用 EBP 去寻址,全部变成用 ESP 来寻址参数及其函数内局部变量了。
函数内部无需保存的寄存器:
RAX, RCX, RDX, R8, R9, R10, R11(一下子可以多用4个寄存器,爽歪歪)
函数内部无需保存的寄存器:
RBX, RSP, RBP, RSI, RDI, R12~R15 (前五个寄存器和X86是一样的,后边四个算是福利了)
所以编译器,在函数内部能不使用后边的寄存器就不使用,所以基本看不到 PUSHAD POPAD 这样的全部压栈指令了。
02. 我们来看下,为啥函数调用方不去申请 RSP 空间,而函数内部就可以直接给 RSP+XX 赋值:
__declspec(noinline)
int
__tmainCRTStartup(
void
)
{
__try
{
void *lock_free=0;
void *fiberid=((PNT_TIB)NtCurrentTeb())->StackBase;
int nested=FALSE;
while((lock_free=InterlockedCompareExchangePointer((volatile PVOID *)&__native_startup_lock, fiberid, 0))!=0)
{
....
}
if (__native_startup_state == __initializing)
{
_amsg_exit( _RT_CRT_INIT_CONFLICT);
}
else if (__native_startup_state == __uninitialized)
{
__native_startup_state = __initializing;
if (_initterm_e( __xi_a, __xi_z ) != 0) // here ...
{
return 255;
}
}
else
{
has_cctor = 1;
}
000000013F6F1820 | 48 83 EC 68 | sub rsp,68 | ;crtexe.c:410
000000013F6F1824 | 48 C7 44 24 40 00 00 00| mov qword ptr ss:,0 | ;+40 // void *lock_free
000000013F6F182D | E8 EE 01 00 00 | call <testasm64.NtCurrentTeb> | ;crtexe.c:458
000000013F6F1832 | 48 8B 40 08 | mov rax,qword ptr ds: |
000000013F6F1836 | 48 89 44 24 38 | mov qword ptr ss:,rax | ;+38 // void *fiberid
000000013F6F183B | C7 44 24 30 00 00 00 00| mov dword ptr ss:,0 | ;+30 // int nested
000000013F6F1843 | 48 8B 54 24 38 | mov rdx,qword ptr ss: | ;crtexe.c:460
000000013F6F1848 | 48 8D 0D 01 7F 00 00 | lea rcx,qword ptr ds:[<__native_startup_lock> | ;rcx:__xi_a
000000013F6F184F | 33 C0 | xor eax,eax | ;0
000000013F6F1851 | F0 48 0F B1 11 | lock cmpxchg qword ptr ds:,rdx | ;rcx:__xi_a, rdx:__xi_z
000000013F6F1856 | 48 89 44 24 40 | mov qword ptr ss:,rax |
000000013F6F185B | 48 83 7C 24 40 00 | cmp qword ptr ss:,0 | ;跟0比较
000000013F6F1861 | 74 23 | je testasm64.13F6F1886 |
000000013F6F1863 | 48 8B 44 24 38 | mov rax,qword ptr ss: | ;crtexe.c:462
000000013F6F1868 | 48 39 44 24 40 | cmp qword ptr ss:,rax |
000000013F6F186D | 75 0A | jnz testasm64.13F6F1879 |
000000013F6F186F | C7 44 24 30 01 00 00 00| mov dword ptr ss:,1 | ;crtexe.c:464
000000013F6F1877 | EB 0D | jmp testasm64.13F6F1886 | ;crtexe.c:465
000000013F6F1879 | B9 E8 03 00 00 | mov ecx,3E8 | ;crtexe.c:472
000000013F6F187E | FF 15 6C 9A 00 00 | call qword ptr ds:[<__imp_Sleep>] |
000000013F6F1884 | EB BD | jmp testasm64.13F6F1843 | ;crtexe.c:473
000000013F6F1886 | 8B 05 B4 7E 00 00 | mov eax,dword ptr ds:[<__native_startup_state | ;crtexe.c:475
000000013F6F188C | 83 F8 01 | cmp eax,1 |
000000013F6F188F | 75 0C | jnz testasm64.13F6F189D |
000000013F6F1891 | B9 1F 00 00 00 | mov ecx,1F | ;crtexe.c:477
000000013F6F1896 | E8 95 0D 00 00 | call <testasm64._amsg_exit> |
000000013F6F189B | EB 41 | jmp testasm64.13F6F18DE |
000000013F6F189D | 8B 05 9D 7E 00 00 | mov eax,dword ptr ds:[<__native_startup_state | ;crtexe.c:479
000000013F6F18A3 | 85 C0 | test eax,eax |
000000013F6F18A5 | 75 2D | jnz testasm64.13F6F18D4 |
000000013F6F18A7 | C7 05 8F 7E 00 00 01 00| mov dword ptr ds:[<__native_startup_state>],1 | ;crtexe.c:481
000000013F6F18B1 | 48 8D 15 A8 4D 00 00 | lea rdx,qword ptr ds:[<__xi_z>] | ;crtexe.c:483
000000013F6F18B8 | 48 8D 0D 71 4A 00 00 | lea rcx,qword ptr ds:[<__xi_a>] | ;rcx:__xi_a
000000013F6F18BF | E8 74 10 00 00 | call <testasm64._initterm_e> | ;这个时候 RSP 就没有变过 ...
000000013F6F18C4 | 85 C0 | test eax,eax |
// call <testasm64._initterm_e>
000000005B992A60 | 48 89 54 24 10 | mov qword ptr ss:,rdx | ;rdx:__xi_z
000000005B992A65 | 48 89 4C 24 08 | mov qword ptr ss:,rcx | ;rcx:__xi_a
000000005B992A6A | 48 83 EC 38 | sub rsp,38 |
000-002BFCC0 : -68 // sub rsp,68
XXXX// 函数参数 START 这里预留给本函数内,调用其他函数(B)时函数B的预留参数位置。猜测预留参数个数是本函数内调用函数的参数最大值。
XXXX
XXXX
XXXX
XXXX// 超过四个参数时 第五个之后直接从这里开始赋值
XXXX// 函数参数 End 最大参数个数
CCCC // 局部变量 START 这里是函数内部的变量 预留空间,DEBUG是有多少分配多少,Release应该会有所优化。
CCCC
CCCC
CCCC // 局部变量 END
000-002BFD28 : 进到函数时的 ESP
所以,函数内调用函数B时,ESP总在 000-002BFCC0(-68) 这里,为什么这么NB,因为X64空间无限大,有内存就可以这样任性 。。。
3. Release 编译:
// scanf( "%d %c",&a,&c);
0000000070A62E30 | 48 8B C4 | mov rax,rsp |
0000000070A62E33 | 48 89 48 08 | mov qword ptr ds:,rcx |
0000000070A62E37 | 48 89 50 10 | mov qword ptr ds:,rdx |
0000000070A62E3B | 4C 89 40 18 | mov qword ptr ds:,r8 |
0000000070A62E3F | 4C 89 48 20 | mov qword ptr ds:,r9 |
0000000070A62E43 | 48 83 EC 28 | sub rsp,28 |
0000000070A62E47 | 48 8B D1 | mov rdx,rcx |
0000000070A62E4A | 4C 8D 48 10 | lea r9,qword ptr ds: |
0000000070A62E4E | 48 8D 0D 13 16 02 00 | lea rcx,qword ptr ds: |
0000000070A62E55 | 45 33 C0 | xor r8d,r8d |
0000000070A62E58 | E8 A7 48 00 00 | call msvcr90.70A67704 |
0000000070A62E5D | 48 83 C4 28 | add rsp,28 |
0000000070A62E61 | C3 | ret |
用 RAX 来寻址参数
在X64下,全部由函数内部来维护堆栈平衡,自己获取参数,自己申请空间处理变量, sub rsp, xx,最后恢复堆栈 ret .
4. 参数全部为浮点数,或参数为浮点+非浮点。
以后再看 ...
二、类机制传参分析。
NsTestX64::NsTestX64(void)
{
double bb = 12.36;
m_a = 0;
m_b = TRUE;
}
000000013FB71360 | 48 89 4C 24 08 | mov qword ptr ss:,rcx | ;this 指针仍然放于 rcx
000000013FB71365 | 57 | push rdi |
000000013FB71366 | 48 83 EC 10 | sub rsp,10 |
000000013FB7136A | 48 8B FC | mov rdi,rsp |
000000013FB7136D | 48 B9 04 00 00 00 00 00| mov rcx,4 |
000000013FB71377 | B8 CC CC CC CC | mov eax,CCCCCCCC |
000000013FB7137C | F3 AB | rep stos dword ptr es: |
000000013FB7137E | 48 8B 4C 24 20 | mov rcx,qword ptr ss: |
000000013FB71383 | F2 0F 10 05 6D 55 00 00| movsd xmm0,qword ptr ds: |
000000013FB7138B | F2 0F 11 04 24 | movsd qword ptr ss:,xmm0 |
000000013FB71390 | 48 8B 44 24 20 | mov rax,qword ptr ss: | ;this 给 rax 去寻址
000000013FB71395 | 48 C7 00 00 00 00 00 | mov qword ptr ds:,0 |
000000013FB7139C | 48 8B 44 24 20 | mov rax,qword ptr ss: |
000000013FB713A1 | C7 40 08 01 00 00 00 | mov dword ptr ds:,1 |
000000013FB713A8 | 48 8B 44 24 20 | mov rax,qword ptr ss: |
000000013FB713AD | 48 83 C4 10 | add rsp,10 |
000000013FB713B1 | 5F | pop rdi |
000000013FB713B2 | C3 | ret |
obj.NsSetThreeParam( TRUE, &a, ii );
// this 指针仍处于 RCX 中
000000013FB71213 | 4C 8B 8C 24 80 00 00 00| mov r9,qword ptr ss: | ;__int64
000000013FB7121B | 4C 8D 44 24 44 | lea r8,qword ptr ss: | ;PVOID
000000013FB71220 | BA 01 00 00 00 | mov edx,1 | ;TURE
000000013FB71225 | 48 8D 8C 24 98 00 00 00| lea rcx,qword ptr ss: | ;this
000000013FB7122D | E8 D3 FD FF FF | call testasm64_.13FB71005 |
类机制的话和X86传参一样 this指针放 rcx 中,可认为是普通函数多了第一个参数。
三、异常机制分析。(不使用SEH链了 )
以后再看 ...
先站楼{:soso_e113:} 学习了
现在好多程序都只有64位了
希望OD早日支持64位程序 wangwei628 发表于 2015-1-1 12:55
学习了
现在好多程序都只有64位了
希望OD早日支持64位程序
用这款神器
Nisy 发表于 2015-1-1 13:08
用这款神器
谢谢大牛,但还是觉得OD习惯
wangwei628 发表于 2015-1-2 10:05
谢谢大牛,但还是觉得OD习惯
x64-dbg也用过,感觉有些程序调试不了
非常精彩! 什么东东,用多了,就好了。 挺详细的,感谢分享!
页:
[1]