好友
阅读权限10
听众
最后登录1970-1-1
|
fastcall资料网上很多了,我再总结一下,调用方为被调用方开辟堆栈(维护堆栈),填充函数,准备调用环境。
样本程序。a函数入口打断点.
断下之后
void a()
{
00007FF6B11117C0 40 55 push rbp
00007FF6B11117C2 57 push rdi
00007FF6B11117C3 48 81 EC F8 00 00 00 sub rsp,0F8h
00007FF6B11117CA 48 8D 6C 24 30 lea rbp,[rsp+30h]
00007FF6B11117CF 48 8B FC mov rdi,rsp
00007FF6B11117D2 B9 3E 00 00 00 mov ecx,3Eh
00007FF6B11117D7 B8 CC CC CC CC mov eax,0CCCCCCCCh
00007FF6B11117DC F3 AB rep stos dword ptr [rdi]
00007FF6B11117DE 48 8D 0D 60 F8 00 00 lea rcx,[__0A9402C3_main@cpp (07FF6B1121045h)]
00007FF6B11117E5 E8 9D F8 FF FF call __CheckForDebuggerJustMyCode (07FF6B1111087h) //上面是Debug模式下的初始化代码 rep stos类似循环,循环次数在rcx中,一般都是for(;;;){memcpy();}这种代码
f(1,2,3,4,5);
00007FF6B11117EA C7 44 24 20 05 00 00 00 mov dword ptr [rsp+20h],5 //超过四个参数,存放在栈中。这里为什么是20h,因为rsp->rsp+16h是暂存前4个参数,寄存器有易失性,你要多次用参数就得保存
00007FF6B11117F2 41 B9 04 00 00 00 mov r9d,4 //第四个参数r9中
00007FF6B11117F8 41 B8 03 00 00 00 mov r8d,3 //第三个参数r8中
00007FF6B11117FE BA 02 00 00 00 mov edx,2 //第二个参数rdx中
00007FF6B1111803 B9 01 00 00 00 mov ecx,1 //第一个参数rcx中
00007FF6B1111808 E8 8D F9 FF FF call f (07FF6B111119Ah)
}
00007FF6B111180D 48 8D A5 C8 00 00 00 lea rsp,[rbp+0C8h]
00007FF6B1111814 5F pop rdi
00007FF6B1111815 5D pop rbp
00007FF6B1111816 C3 ret
bool f(int a,int b, int c,int d ,int f)
{
//x86可以根据ret n 来看函数参数,ia-64的话可以根据入口堆栈赋值和入口前堆栈赋值的情况判断参数个数
00007FF6B1111830 44 89 4C 24 20 mov dword ptr [rsp+20h],r9d //进入函数的第一件事,保存前4个参数,因为返回地址压入占8个字节,所以是rsp+8到rsp+20
00007FF6B1111835 44 89 44 24 18 mov dword ptr [rsp+18h],r8d
00007FF6B111183A 89 54 24 10 mov dword ptr [rsp+10h],edx
00007FF6B111183E 89 4C 24 08 mov dword ptr [rsp+8],ecx
00007FF6B1111842 55 push rbp
00007FF6B1111843 57 push rdi
00007FF6B1111844 48 81 EC 68 01 00 00 sub rsp,168h
00007FF6B111184B 48 8D 6C 24 20 lea rbp,[rsp+20h]
00007FF6B1111850 48 8B FC mov rdi,rsp
00007FF6B1111853 B9 5A 00 00 00 mov ecx,5Ah
00007FF6B1111858 B8 CC CC CC CC mov eax,0CCCCCCCCh
00007FF6B111185D F3 AB rep stos dword ptr [rdi]
00007FF6B111185F 8B 8C 24 88 01 00 00 mov ecx,dword ptr [rsp+188h]
00007FF6B1111866 48 8D 0D D8 F7 00 00 lea rcx,[__0A9402C3_main@cpp (07FF6B1121045h)]
00007FF6B111186D E8 15 F8 FF FF call __CheckForDebuggerJustMyCode (07FF6B1111087h)
b = c = d = f = 1;
00007FF6B1111872 C7 85 80 01 00 00 01 00 00 00 mov dword ptr [rbp+0000000000000180h],1 //用rbp索引,更常见的其实是rsp索引
00007FF6B111187C 8B 85 80 01 00 00 mov eax,dword ptr [rbp+0000000000000180h]
00007FF6B1111882 89 85 78 01 00 00 mov dword ptr [rbp+0000000000000178h],eax
00007FF6B1111888 8B 85 78 01 00 00 mov eax,dword ptr [rbp+0000000000000178h]
00007FF6B111188E 89 85 70 01 00 00 mov dword ptr [rbp+0000000000000170h],eax
00007FF6B1111894 8B 85 70 01 00 00 mov eax,dword ptr [rbp+0000000000000170h]
00007FF6B111189A 89 85 68 01 00 00 mov dword ptr [rbp+0000000000000168h],eax
int k1, k12k2, k2, k;
if (a == 1)
00007FF6B11118A0 83 BD 60 01 00 00 01 cmp dword ptr [a],1
00007FF6B11118A7 75 02 jne f+7Bh (07FF6B11118ABh)
return true;
00007FF6B11118A9 B0 01 mov al,1
}
00007FF6B11118AB 48 8D A5 48 01 00 00 lea rsp,[rbp+148h]
00007FF6B11118B2 5F pop rdi
00007FF6B11118B3 5D pop rbp
00007FF6B11118B4 C3 ret
//接下来实战分析一个内核api PsLookupProcessByProcessId
PAGE:00000001403530A6 loc_1403530A6: ; CODE XREF: PsOpenProcess+230↓j
PAGE:00000001403530A6 cmp qword ptr [rsp+218h+var_1C8+8], 0
PAGE:00000001403530AC jnz loc_140353147
PAGE:00000001403530B2 lea rdx, [rsp+218h+Object]
PAGE:00000001403530B7 mov rcx, qword ptr [rsp+218h+var_1C8]
PAGE:00000001403530BC call PsLookupProcessByProcessId //我们可以看到这个函数有2个参数
PsLookupProcessByProcessId proc near ; CODE XREF: ViCreateProcessCallback+AA058↑p
PAGE:00000001403531FC ; PsOpenProcess+15C↑p ...
PAGE:00000001403531FC
PAGE:00000001403531FC var_38 = dword ptr -38h
PAGE:00000001403531FC arg_0 = qword ptr 8
PAGE:00000001403531FC arg_8 = qword ptr 10h
PAGE:00000001403531FC arg_10 = qword ptr 18h
PAGE:00000001403531FC
PAGE:00000001403531FC ; FUNCTION CHUNK AT PAGE:00000001403B8030 SIZE 00000082 BYTES
PAGE:00000001403531FC
PAGE:00000001403531FC mov [rsp+arg_0], rbx //我们看到这里并不是将参数保存,而是保存其他的寄存器,说明这个函数的流程一定不长,并且参数只用一次(这点可以在wrk或者reactos中验证)
PAGE:0000000140353201 mov [rsp+arg_8], rbp
PAGE:0000000140353206 mov [rsp+arg_10], rsi
PAGE:000000014035320B push rdi
PAGE:000000014035320C push r12
PAGE:000000014035320E push r13
PAGE:0000000140353210 sub rsp, 20h
PAGE:0000000140353214 mov rdi, gs:_KPCR.Prcb.CurrentThread
PAGE:000000014035321D xor r12d, r12d
PAGE:0000000140353220 mov rbp, rdx
PAGE:0000000140353223 dec word ptr [rdi+1C4h]
PAGE:000000014035322A mov rbx, r12
PAGE:000000014035322D mov rdx, rcx //rcx(arg0 ) ProcessId 这里调用完之后此参数就不用了,所以不用保存这个参数,因此前面保存的是寄存器环境
PAGE:0000000140353230 mov rcx, cs:PspCidTable
PAGE:0000000140353237 call ExMapHandleToPointer
………………………………………………
函数末尾
PAGE:00000001403532C4 mov rbx, [rsp+38h+arg_0] //将原先保存的寄存器恢复
PAGE:00000001403532C9 mov rbp, [rsp+38h+arg_8]
PAGE:00000001403532CE mov rsi, [rsp+38h+arg_10]
PAGE:00000001403532D3 add rsp, 20h
PAGE:00000001403532D7 pop r13
PAGE:00000001403532D9 pop r12
PAGE:00000001403532DB pop rdi
PAGE:00000001403532DC retn
|
|