Nisy 发表于 2015-1-1 12:11:09

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链了 )
以后再看 ...

iamok 发表于 2015-1-1 12:36:29

先站楼{:soso_e113:}

wangwei628 发表于 2015-1-1 12:55:21

学习了
现在好多程序都只有64位了
希望OD早日支持64位程序

Nisy 发表于 2015-1-1 13:08:22

wangwei628 发表于 2015-1-1 12:55
学习了
现在好多程序都只有64位了
希望OD早日支持64位程序




用这款神器


wangwei628 发表于 2015-1-2 10:05:01

Nisy 发表于 2015-1-1 13:08
用这款神器

谢谢大牛,但还是觉得OD习惯

wangwei628 发表于 2015-1-2 10:05:44

wangwei628 发表于 2015-1-2 10:05
谢谢大牛,但还是觉得OD习惯

x64-dbg也用过,感觉有些程序调试不了

small-q 发表于 2015-1-2 14:30:04

非常精彩!

chyx 发表于 2015-1-2 20:41:06

什么东东,用多了,就好了。

遗忘在角落 发表于 2017-2-12 23:49:44

挺详细的,感谢分享!
页: [1]
查看完整版本: X64 初接触续