首页
社区
课程
招聘
[讨论]为什么vs的64位C运行时库开头的几句汇编总是改写栈数据
发表于: 2019-5-7 16:36 3520

[讨论]为什么vs的64位C运行时库开头的几句汇编总是改写栈数据

2019-5-7 16:36
3520
   225: static int __cdecl flsall (
   226:         int flushflag
   227:         )
   228: {
 000007FEDCDFEC80 48 89 5C 24 08       mov         qword ptr [rsp+8],rbx  
000007FEDCDFEC85 48 89 74 24 10       mov         qword ptr [rsp+10h],rsi  
000007FEDCDFEC8A 48 89 7C 24 18       mov         qword ptr [rsp+18h],rdi  
000007FEDCDFEC8F 41 55                push        r13  
000007FEDCDFEC91 41 56                push        r14  
000007FEDCDFEC93 41 57                push        r15  
000007FEDCDFEC95 48 83 EC 30          sub         rsp,30h  
000007FEDCDFEC99 44 8B F1             mov         r14d,ecx  
   229:    int i;
   230:    int count = 0;
000007FEDCDFEC9C 33 F6                xor         esi,esi  
如上所示, 在 msvcr120.dll 里, flsall()函数的前3句汇编改写了栈上的数据, 这些数据按道理只有 rsp+8 是可以安全改写的(只有1个参数时)
我看了下其它的函数, 也都是这样,
    53: int __cdecl fflush (
    54:         FILE *stream
    55:         )
    56: {
000007FEDCDFEC3C 48 89 5C 24 10       mov         qword ptr [rsp+10h],rbx  
000007FEDCDFEC41 48 89 4C 24 08       mov         qword ptr [rsp+8],rcx  
000007FEDCDFEC46 57                   push        rdi  
000007FEDCDFEC47 48 83 EC 20          sub         rsp,20h  
000007FEDCDFEC4B 48 8B D9             mov         rbx,rcx  
    57:     int rc = 0;
    58: 
    59:     /* if stream is NULL, flush all streams
    60:      */
    61:     if ( stream == NULL )

它这样子改是为了什么

这个特性搞得我的hook代码一直异常

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 0
支持
分享
最新回复 (8)
雪    币: 5
活跃值: (531)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
2
我也碰到过这个问题,把我的HOOK堆栈搞得乱七八糟.....
观察了下,应该是为了给参数保留相应的堆栈。64位调用约定虽然优先使用寄存器传参,但还是会给参数留有相应的堆栈空间。比如我的GetStringVal函数有三个参数,根据调用约定使用了rcx rdx r8三个寄存器传参,但是在函数头还是将这三个寄存器的内容以从右到左的参数顺序保存到了堆栈中。
bool GetStringVal(HKEY hKey, LPCTSTR lpValue, LPCWSTR lpszContent)
{
000000013F811100  mov         qword ptr [rsp+18h],r8  
000000013F811105  mov         qword ptr [rsp+10h],rdx  
000000013F81110A  mov         qword ptr [rsp+8],rcx  
000000013F81110F  push        rdi  
000000013F811110  sub         rsp,80h  
000000013F811117  mov         rdi,rsp  
2019-5-7 17:03
0
雪    币: 47
活跃值: (45)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
你那种情况都是3个参数 还好一点
那个flush 只有一个参数, 但是它写了两个位置的数据, 而且从源码来看是绝对看不出来的
2019-5-7 18:15
0
雪    币: 3574
活跃值: (4719)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
因为Debug版本. 会把参数写回栈 Release就没有
Release 不是参数的 就是保存非易失寄存器的...
这个基本常识吧...
2019-5-7 18:18
0
雪    币: 47
活跃值: (45)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
@syser, 你没认真看啊,  一个参数, 它要写回也是只写一个吧, 为什么写两个甚至三个?
其次, 我用的是 msvcr120.dll 后面不带d说明不是debug版本
2019-5-7 18:38
0
雪    币: 43
活跃值: (388)
能力值: ( LV9,RANK:140 )
在线值:
发帖
回帖
粉丝
6
https://docs.microsoft.com/en-us/cpp/build/prolog-and-epilog?view=vs-2019这不懂是不是你想找的
2019-5-7 19:42
0
雪    币: 5734
活跃值: (1737)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
7
x64编译器默认fastcall, 当你使用&var1这种方式去访问参数地址的时候, 因为rcx是没有地址的所以编译器需要一个栈上的地址来存储数据,使逻辑不会出错,这也是fastcall为啥要有20h个(rcx, rdx, r8, r9)保留堆栈的原因, 当没有这种方式的访问时,一般是用来保存易失性的数据,节省栈的开销.
2019-5-7 22:46
0
雪    币: 3574
活跃值: (4719)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
frdGo @syser, 你没认真看啊, 一个参数, 它要写回也是只写一个吧, 为什么写两个甚至三个? 其次, 我用的是 msvcr120.dll 后面不带d说明不是debug版本
...我说的还不够详细么.,.
相当于Push 非易失寄存器...
2019-5-8 04:05
0
雪    币: 47
活跃值: (45)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
谢谢各位, 搞明白了
2019-5-8 09:28
0
游客
登录 | 注册 方可回帖
返回
//