HsOjo’s Blog

📒 A powerless rookie's tree hole.

C/C++ WinApiHook注意调用方式!!!

2017-11-2600 分钟
type
status
date
slug
summary
tags
category
icon
password

前言

在前段时间使用易语言hook的方式,实现了《11月的阿卡迪亚》的部分汉化,当时只是抱着试一试的心态,不过既然成功了,就当然想要深入的理解和研究。
最近又发现了个有趣的エロ游戏,于是乎我又打算尝试汉化,不过机会渺茫,没有搞懂资源结构,但是修改程序使其支持中文显示,应该还是可以的。
然后在尝试Hook某些API的时候,程序崩溃了,研究了整整一天才发现原因,So,写篇文章记录下来。

过程

放弃易语言

于是乎使用OllyDbg分析了一波api,发现这次的程序创建字体使用的是CreateFontIndirectA,传入参数是一个结构体,这d*g sh*t 易语言涉及到结构体的参数就不好处理,由于并不打算再深入研究易语言,So 我花了两天时间学习了C++

使用C++ & MHook

虽然搞懂了C++的基本使用,不过我还不打算直接就干新的程序,首先参考之前汉化《11月的阿卡迪亚》做一个C++ Ver的Hooker替代易语言Ver的Hooker,了解了一番C++ APIHook后,我选择了使用MHook库,方便。

传统Hook & MHook的区别

传统Hook
调用被Hook的API时一般分为以下几步,比较繁琐。
调用Hooked API跳转到Hook Func暂停Hook修改参数并调用True Func恢复Hook返回结果
  • MHook
使用MHook可以简化为以下几步~~ 帮你封装好了 ~~,比较简单,完善。
调用Hooked API跳转到Hook Func修改参数并调用True Func返回结果
  • 注意:在Hook Func里不能调用Hooked API,必须通过True Func来操作原API,否则会崩溃(死循环了)

开始Hook

操作环境

系统:Windows 7
软件: Clion & MinGW(CMake), MHook(Source Code), LordPE

修改MHook

由于MHook原来是个VC++的project,我所使用的编译环境是CMake(好像是gcc),某些VC++特性不支持,比如没有某些头文件。
经过一番观察,还好MHook用到VC++的部分都是些debug用的输出内容,将其全部去掉即可,出了问题又不会修。还有一些基础类型不存在,不过有替代的选项,全部换掉即可。

尝试Hook

首先Hook的是,KERNEL32-GetSystemDefaultLangID,很轻松的Hook成功了。
于是乎,我傻逼傻逼的以为Hook就这么简单,一口气把之前的所有API的Hook Code都写好了,然后便是噩梦的开始。

莫名崩溃

当时我Hook的API有以下:
KERNEL32-GetSystemDefaultLangIDKERNEL32-MultiByteToWideCharUSER32-MessageBoxAGDI32-CreateFontA
当我Hook KERNEL32-MultiByteToWideCharGDI32-CreateFontA的时候,程序是绝对崩溃的,但我Hook KERNEL32-GetSystemDefaultLangIDUSER32-MessageBoxA是正常的。
这让我很不解,为什么有些API正常,有些就不正常。于是我只Hook KERNEL32来进行测试。
一开始我以为是Hook时传值给API的方式不对,比如MultiByteToWideChar,返回的是转换后的长度,传入的参数lpWideCharStr会被改变,我觉得是引用(&)还有指针(*)的问题。
于是乎我折腾了半天,无果(还tm换回了传统Hook)

发现问题

既然换回传统Hook还不行,我已经在怀疑人生了,然后我去翻阅了一遍原来易语言Ver的code,发现:
notion image
易语言版本Hooker
我*****,才想起来那个程序不是以C的方式调用API(之前也因此崩溃过),而是用更传统的方式。
参考文章后发现,原来Win32 Api都是使用__stdcall的方式调用,我写的Hook Func(没有标记调用方式)默认是以__cdecl的方式调用的。
1._stdcall调用
_stdcall是Pascal程序的缺省调用方式,参数采用从右到左的压栈方式,被调函数自身在返回前清空堆栈。
2._cdecl调用
_cdecl是C/C++的缺省调用方式,参数采用从右到左的压栈方式,传送参数的内存栈由调用者维护。_cdecl约定的函数只能被C/C++调用,每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。
由于Visual C++默认采用_cdecl 调用方式,所以VC中中调用DLL时,用户应使用_stdcall调用约定。
3._fastcall调用
_fastcall调用较快,它通过CPU内部寄存器传递参数。
按C编译方式,_fastcall调用约定在输出函数名前面加“@”符号,后面加“@”符号和参数的字节数,形如@functionname@number。

解决方法

在Hook Func前面加上__stdcall标记即可。

下一篇

【汉化】11月的阿卡迪亚

Loading...