zoukankan      html  css  js  c++  java
  • 函数声明后面加个stdcall是什么意思

    首先先要清楚汇编中堆栈的原理 才能了解 stdcall的约束
        
    这里 是我通过ollydbg汇编代码调试去理解

    比如 

    我们看到这个方法的调用位置为 00401378 找到这个地址的调用代码为

    00401378  |.  E8 BD000000   call 0040143A (这里等价于 call MessageBoxA)

    进入 0040143A

    0040143A   $- FF25 AC314000      jmp 77D507EA 

    我们看到 无条件跳转到 77D507EA  看这个位置的代码
    我们看到进入了user32模块代码如下

    77D507EA >    8BFF               mov edi,edi
    77D507EC  /.  55                 push ebp
    77D507ED  |.  8BEC               mov ebp,esp
    77D507EF  |.  833D BC14D777 00   cmp dword ptr ds:[0x77D714BC],0x0
    77D507F6  |.  74 24              je XUSER32.77D5081C
    77D507F8  |.  64:A1 18000000     mov eax,dword ptr fs:[0x18]
    77D507FE  |.  6A 00              push 0x0
    77D50800  |.  FF70 24            push dword ptr ds:[eax+0x24]
    77D50803  |.  68 241BD777        push USER32.77D71B24
    77D50808  |.  FF15 C412D177      call dword ptr ds:[<&KERNEL32.Interlocke>;  kernel32.InterlockedCompareExchange
    77D5080E  |.  85C0               test eax,eax
    77D50810  |.  75 0A              jnz XUSER32.77D5081C
    77D50812  |.  C705 201BD777 0100>mov dword ptr ds:[0x77D71B20],0x1
    77D5081C  |>  6A 00              push 0x0                                 ; /LanguageID = 0 (LANG_NEUTRAL)
    77D5081E  |.  FF75 14            push [arg.4]                             ; |Style
    77D50821  |.  FF75 10            push [arg.3]                             ; |Title
    77D50824  |.  FF75 0C            push [arg.2]                             ; |Text
    77D50827  |.  FF75 08            push [arg.1]                             ; |hOwner
    77D5082A  |.  E8 2D000000        call USER32.MessageBoxExA                ; \MessageBoxExA
    77D5082F  |.  5D                 pop ebp
    77D50830  \.  C2 1000            retn 0x10

    在 77D507EA 断点后

    我们调用了MessageBoxA方法 看到堆栈中 信息为 

    0012FE8C   0040137D  /CALL 到 MessageBoxA 来自 CRACKME.00401378
    0012FE90   000206F6  |hOwner = 000206F6 ('CrackMe v1.0',class='No need to disasm the code!')
    0012FE94   00402169  |Text = "No luck there, mate!"
    0012FE98   00402160  |Title = "No luck!"
    0012FE9C   00000030  \Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL

    第一列是堆栈的地址 也就是 esp的地址

    说明从堆栈地址 0012FE9C   到0012FE8C   压入了 5个4字节的数据 通过右边可以看出 这些数据中4个4字节是MessageBoxA 的四个参数

    最上面的1个4字节压入的 子程序返回 要执行的代码的地址

    第二列存放的地址信息 实际是ds的地址 通过这个地址 找到找到的就是找到的数据 

    比如00402160  到00402169的9个字节  存放的是No luck!的2进制数据 可以看到  这里可以看到No luck!为8个字节 其实还有最后一字节 为‘\0’表示字符串的结束

    我们知道 call了某个地址 需要通过 retn指令结束子程序的运行  retn指令将esp对应的地址 pop出来 从而获得 call完成后的下一个运行代码的地址

    我们看到 0012FE8C   0040137D  /CALL 到 MessageBoxA 来自 CRACKME.00401378 

    说明 retn之后 下一个执行的代码是 0040137D  这个地址

    但是因为 堆栈中 pop了 

    0012FE90   000206F6  |hOwner = 000206F6 ('CrackMe v1.0',class='No need to disasm the code!')
    0012FE94   00402169  |Text = "No luck there, mate!"
    0012FE98   00402160  |Title = "No luck!"
    0012FE9C   00000030  \Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL

    这从0012FE9C到0012FE90这 16个字节的数据 都是子程序使用的 所有在retn的同时还要讲该数据清除掉 

    所以在retn的时候 会pop掉0012FE8C   0040137D  /CALL 到 MessageBoxA 来自 CRACKME.00401378  这里

    使esp的地址指向0012FE90   也就是0012FE8C+4 

    我们看到

    77D5082F  |.  5D                 pop ebp
    77D50830  \.  C2 1000            retn 0x10

    最后一句是retn 0x10 也就是 10是 10进制的16 也是 retn 16个字节 

    除了 retn的4个字节 还要加上后面参数的 16个字节 所以 我们可以看到

    retn 0x10 后  esp指向了(0012FE9C   00000030  \Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL)0012FE9C   的后一个地址

    也就是

    0012FEA0   0040124A  返回到 CRACKME.0040124A 来自 CRACKME.00401362


    这就是参数入栈的原理

    __stdcall----参数从右向左入栈

    我们看到

    0012FE8C   0040137D  /CALL 到 MessageBoxA 来自 CRACKME.00401378
    0012FE90   000206F6  |hOwner = 000206F6 ('CrackMe v1.0',class='No need to disasm the code!')
    0012FE94   00402169  |Text = "No luck there, mate!"
    0012FE98   00402160  |Title = "No luck!"
    0012FE9C   00000030  \Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL

    MessageBox的四个参数

    int WINAPI MessageBox(
      _In_opt_  HWND hWnd,
      _In_opt_  LPCTSTR lpText,
      _In_opt_  LPCTSTR lpCaption,
      _In_      UINT uType
    );
    我们看到栈顶是地址0012FE8C 栈顶也就是最后压入的参数 最先压入的参数也就是栈底的是   
    0012FE9C   00000030  \Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL 
    也就是最先压入的是UINT uType 说明是从右到左压入参数 是stdcall约定
    
    
    这里是其他blog转载来的 只是说明 除了stdcall还有其他约定
     在Win32汇编中,我们经常要和Api打交道,另外也会常常使用自己编制的类似于Api 的带参数的子程序,本文要讲述的是在子程序调用的过程中进行参数传递的概念和分析。一般在程序中,参数的传递是通过堆栈进行的,也就是说,调用者把要传递给子程序(或者被调用者)的参数压入堆栈,子程序在堆栈取出相应的值再使用,比如说,如果你要调用 SubRouting(Var1,Var2,Var3),编译后的最终代码可是      
    push Var3 
    push Var2 
    push Var1 
    call SubRouting 
    add esp,12 

          也就是说,调用者首先把参数压入堆栈,然后调用子程序,在完成后,由于堆栈中先前压入的数不再有用,调用者或者被调用者必须有一方把堆栈指针修正到调用前的状态。参数是最右边的先入堆栈还是最左边的先入堆栈、还有由调用者还是被调用者来修正堆栈都必须有个约定,不然就会产生不正确的结果,而Stdcall就是参数从右到左压入栈,由被调用的子程序来修正堆栈的指针。      __cdecl----参数从右向左入栈,调用者清除栈
         __stdcall----参数从右向左入栈,被调用者清除栈       函数func(int   a, int b) 
          补充一点: 
    __cdecl函数被编译成:_func 
    __stdcall函数被编译成:_func@8 (8 为参数的字节数)  Directive Parameter order Clean-up Passes parameters in registers?
    register Left-to-right Routine Yes 
    pascal Left-to-right Routine No 
    cdecl Right-to-left Caller No 
    stdcall Right-to-left Routine No 

    safecall Right-to-left Routine No

    VC里面:PASCAL==CALLBACK==WINAPI==__stdcall         _stdcall是Pascal程序的缺省调用方式,通常用于Win32  Api中,函数采用从右到左的压栈方式,自己在退出时清空堆栈。VC将函数编译后会在函数名前面加上下划线前缀,在函数名后加上"@"和参数的字节数。     _cdecl是C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。VC将函数编译后会在函数名前面加上下划线前缀。是MFC缺省调用约定。          关于PASCAL,其实你只要弄明白一点就行了:声明为这种调用约定的函数都是由它本身来清栈,而__cdecl的函数都是由调用者来清栈。  


  • 相关阅读:
    [Go] golang 两个数组 list 的合并方式
    [Go] assignment count mismatch 1 = 2
    [Go] golang 时间格式化 12小时制 与 24小时制
    [Go] freecache 设置 SetGCPercent 的作用
    [FAQ] Vue 如何控制标签元素的某个属性的显示 ?
    [FE] 实时视频流库 hls.js 重载切换资源的方式
    [FE] 关于网页的一些反爬手段的解析思路,比如 58 等
    [Go] 让 go build 生成的可执行文件对 Mac、linux、Windows 平台一致
    [Go] go build 减小二进制文件大小的几种方式
    [Go] gin-jwt 业务逻辑中使用实例化的 middleware 的方式
  • 原文地址:https://www.cnblogs.com/liaomin416100569/p/9331358.html
Copyright © 2011-2022 走看看