二、使用__cdecl调用方式的函数和链接静态库编译
(一)、__cdecl调用方式
常用的调用方式有__stdcall和__cdecl,易语言和大部分的API使用的是__stdcall,C语言默认使用的是__cdecl。所以实现__cdecl调用方式,可以调用更多的外部模块的函数。
比如例程里的,API文本格式化函数wsprintf,微软C语言函数库msvcrt.dll,里面有比如:控制台格式化文本输出函数printf,字符串处理,文件操作,底层I/O操作等等函数,利用这些C语言的函数,可以方便直接的转译C语言的代码。
以及用C语言编写的函数为__cdecl调用方式的DLL,比如著名的轻量级数据库驱动sqlite3.dll。
实现这种调用方式,将大大扩展黑月的功能和应用范围。
1、黑月如何实现__cdecl调用
所谓的调用方式,就是对参数入栈顺序和传递参数用的堆栈内存的平栈处理方式,参数用栈内存的大小就与参数数目和类型有关。我们要实现__cdecl就必须计算出参数栈内存大小值,然后加上平栈代码。本来这是编译器做的事,但易语言的编译器没有提供这个功能,所以我们要手动计算并填值。下面来学习参数栈内存大小的计算:
这是例程里__cdecl方式的DLL子程序声明,DLL库命令名里有“wsprintfA@@12”字样,wsprintfA是函数原名称,两个@是黑月自己的约定,表示这个函数是__cdecl方式,黑月分析程序时就会认别出并做相应处理。@@后面的值就是参数栈内存大小值,与参数数目和类型有关。
计算这个值很简单,首先来了解一下数据类型所占内存的大小。
(1)4字节类型,因为是WIN32系统,大多数都是4字节32位数据的。
字节型(BYTE)、字符型(char)、短整数型(short)、整数型(int)、小数型(float)等等
以及所有的指针型,包括字符串指针(char*)、双精浮点小数型指针(double*)、长整数型指针(INT64*)等等。后面跟有*号的。
(2)8字节类型
双精浮点小数型(double)、长整数型(INT64)以及日期时间型(DATE实际上是double型)。
用这些数据类型大小乘于相应的参数数目,就是参数占用栈内存大小值。
比如图例的,两个文本型(char*)和一个整数型(int)。三个参数都是4字节的,栈大小 = 3 × 4 = 12
图中的@@12就是这么来的。
如果再多出一个双精浮点小数型(double)参数的。计算式就是 3 × 4 + 8 = 20
将这个计算值以“函数名@@栈大小值”格式填写入DLL声明,在黑月处理后,就能实现__cdecl调用方式。
(3)计算该值错误会发生事情?
这个不用担心,只是弹出以下错误提示,这个提示会帮助你直到填写正确的值并学会计算参数所用栈内存大小(注意:不要把易语言系统配置的“调用DLL命令后检查堆栈错误”关闭了)
用错调用方式也是这个提示,比如不是__cdecl的(比如__stdcall),而用__cdecl的,反之亦然。
(二)、使用静态库
如果说上面实现__cdecl可以调用C语言标准函数库msvcrt的函数,这还需要从C语言源码转译到易源码。但实现编译时链接静态库,直接将C语言编译的静态库,拿来黑月所用。那就不用转译C语言的源码了,先把C语言源码在C的IDE里编译成静态库(LIB),黑月编译时再合成静态库功能代码,那就省了很多事情。而且C语言的内联汇编功能比易的置入代码()更专业更方便出错率又低。
使用从网上下载别人编写好的静态库,或用C语言或汇编为黑月编写静态库(相当于易的模块),这又进一步提高了黑月的功能扩展。
注意:静态库必须是标准C语言或MASM32编译的,库文件为COFF格式的(OMF格式的需要转换成COFF),VC++编写的也可以,但不能带有用MFC类,接口函数名要用C语言风格修饰符(加 extern "C")。
1、静态库的__cdecl函数的调用
仍在DLL命令里声明函数,不同的是在DLL库文件名里不要填DLL文件名,全部都写上“静态库”三个字。黑月分析程序遇到时就会将这个函数做为静态库函数来链接编译。
命令名仍用上方面讲到的名称加栈大小的格式。
2、静态库的__stdcall函数的调用
也是在DLL文件名写上“静态库”三个字。不同DLL调用的是,静态库的__stdcall函数也要类似调用__cdecl方法,需要把参数栈大小写上(即使没有参数也要写上“@0”)。不同于__cdecl的是,黑月约定为一个“@”,注意__cdecl是“@@”,__stdcall是“@”,这是有区别的,也是为了区分种两种调用方式。如图:
3、静态库文件名
上面的DLL文件名已经填写“静态库”,那么怎么指示黑月编译器应该调用哪个静态库?
这就需要自己来编写编译参数的配置文件,以“水波特效演示”为例,它的编译配置文件为“水波特效演示.ini”,必须与源码同目录同名的。(如果是手动处理的,就与欲处理的易程序EXE或DLL同名同目录)
打开后可见内容为:
[Link]
Opt = WaveObjectLib.lib gdi32.lib
Opt=后面跟着的就是要加入的编译参数,可以看到静态库名:WaveObjectLib.lib,而gdi32.lib是这个静态库用到的系统API导入库,如果静态库编写得不规范,没有在编写时注明引用到哪些静态库,就需要我们在这里也同时再加上。库名要用空格分隔开。
黑月已经默认加入kernel32.lib、user32.lib和msvcrt.lib。其它的可看情况添加,不然会出现链接时失败提示,是一大串英文,中间提示有找不到库的函数名。
(三)、编译参数
黑月的编译参数是保存与源码文件或目标程序同目录同名的INI文件里的,黑月处理编译时就读取这个文件并添加到链接器的参数里,这些参数可以指示链接器生成特定的程序。
所以这个编译参数的配置文件,不只是用来写静态库名的,还可以写更专业的LINK选项,以“远程注入进程”为例。
它的原理就是将自身克隆到IE浏览器进程的内存里,再用远程线程启动这个打入IE内部的“克隆体”的子程序,但通常EXE可执行文件的基址都是0x400000,如果直接复制过去,地址是一样的、重叠了,肯定会失败。
所以要产生可以嵌入其它进程空间的特别程序,这个程序的基址就不能为0x400000。
通过编译参数,我们就可产生这样的程序。看“下载器.ini”:
[Link]
Opt =/BASE:0x13140000 /INCREMENTAL:NO /OPT:NOWIN98 /SECTION:.text,EWR /IGNORE:4078 /FILEALIGN:0x200 /MERGE:.rdata=.text /MERGE:.data=.text
其中重要的参数(颜色对应):
1、就是将EXE的基址设为0x13140000,错开0x400000
2、节并节到.text段
这样生成的下载器程序就能顺利的植入IE进程空间,并且逃避防火墙的检查,因为所有网络下载或其它操作都是以IE浏览器的名义进行,是合法的。
(四)、黑月处理程序路径
随着黑月程序的各种相关文件增多(资源脚本RC文件、编译参数INI文件和静态库LIB文件等等),有必要规范一下黑月程序的文件分布,最好同一个工程项目放在一个目录里,这样发布源码时连带所有文件一起打包。不能再像易源码那样,一个文件就包括全部,所有源码都放在同一个目录。
并且黑月处理时要寻找相关文件是有特定规则的。与源码文件或目标程序同名同目录。
1、自动处理时(推荐)
是以源码文件所在的目录为工程目录的,所用到的资源脚本RC文件、编译参数INI文件和静态库LIB都是从这个目录读取,并且RC文件和INI文件是必须与源码文件同名的。比如:
源码文件
黑月程序.e
那么相关工程文件就为
黑月程序.rc
黑月程序.ini
特别是INI文件是自己编写和命名,文件名要与源码文件统一。
2、手动处理时(系统不支持自动处理时用到的)
是以欲处理的易程序(EXE和DLL)的目录为工程目录,而不是以源码文件了,这点要注意,其它相关文件也要做相应改变。比如:
源码文件
C:\黑月程序.e
易编译后为
F:\BlackMoon.exe
那么相关工程文件就应变为
F:\BlackMoon.rc
F:\BlackMoon.ini
与源码文件无关了,因为手动选择的程序不一定就是当前编辑器里的源码文件生成的。所以为了工程项目编写方便,你就要把易程序编译到源码同目录,并且与源码同名。像上面的,编译到C:\黑月程序.exe,其它相关文件名就不用改变了,全部名称都统一,这样交流源码就方便了。
这就是手动处理不方便的地方。
|