admin 管理员组文章数量: 887032
2023年12月24日发(作者:switch包浆机是什么意思)
易语言置入代码
速度及完成一些易不好直接完成的操作,是追求置入代码的全部!如果你不同意,那基本上不用往下看。汇编功底只有靠自己,这里只是讲述在易语言中如何使用“置入代码()”来嵌入汇编及其注意要点。这是自己的学习体会,如有理解错误或bug请指出,谢!
我们先来了解一下置入代码是怎么的一回事
汇编中nop(10010000)是空操作指令,我们先使用8个空操作来给程序作个“置入代码”的标记。于是,在一个新建易程序中输入如下代码:
静态编译成可执行文件后,使用OllyDbg反编译一下:
PUSH EBP / MOV EBP,ESP 是例行的保存和设置 EBP 的代码,因为缺省对堆栈操作的寄存器有
ESP 和 EBP,而 ESP是堆栈指针,无法暂借使用(代码中若有 PUSH/POP指令会自动修改ESP值),所以一般使用 EBP 来存取堆栈。聪明的你在这时有没有想发言:“但我们置入代码中没有添加这个指令,它是如何来的?”告诉大家,这是易编译器添加上去的。易语言为所有子程序初始都添加这么的两句,在“返回()”时再使用MOV ESP,EBP/POP EBP 来平栈,这都是易语言在编译时所做的处理,平常我们都不用关心这些。但当使用置入代码来嵌入汇编指令,我们就不得不了解。现暂且放下,后面再来说明我们要了解的的东西。
跳出具体指令,看看反汇编子程序的框架。置入的代码(8个nop)“完全”的位于一个子程序内。对此应了解:我们置入代码()严格来说是置入汇编代码“片段”。不要希求能置入一个完整pe数据,易语言会在编译链接时写入易的文件头。置入代码()就不能再使用.data或invoke messagebox之类。因为易语言不会再为我们置入的代码去修改数据段及导入表。
辅助工具的选用
作为铺垫,我们先来了解一下易语言置入代码汇编工具。云外归鸟(大鸟)提供了一个功能完善、界面友好、使用免费的工具“易语言置入代码NASM汇编生成工具v1.0 ”
NASM指令简单明了,而且能将汇编代码片段直接编译成二进制文件,它不强求源码是一个完整的汇编程序。这也可能是大鸟选择使用NASM编译器的原因(个人猜想)。大鸟在易语言论坛提供了该工具的源码下载。在此感谢大鸟为广大易友所做的贡献!
相信使用汇编的人更多的是喜欢MASM,这从Aogo所写“MASMPLUS”被广泛下载使用可见一斑。本人在大鸟提供源码的前提下修改了小部分代码,让该工具支持MASM编译。若大鸟认为被侵权,请指出,本人QQ:109544089。本人当即删除下载链接及正式致歉!
MASM不支持直接编译成二进制文件,这当中要如何处理?答案是:链接成COM文件。因为DOS中COM文件是没有文件头,系统载入后直接从第一个语句运行。这提供了一个迂回办法来生成置入代码所需字节集数据。这样一来,我们就必须定义一个完整的汇编源码去编译链接,这就是为什么在易语言置入代码MASM汇编工具当中存在.586、.model、option等这样的几行语句,而在NASM汇编工具中没有的原因了。这教程均使用MASM汇编置入工具,请大家下载配合教程使用。下载地址:/?tid=271694
提供一个完整源码去编译,虽然多出了几行语句,但它却拥有NASM汇编工具所不能比拟的优势:定义函数(子程序)参数及局部变量供汇编代码引用,而免去使用如:[ebp+8]这样的书写,直接明瞭。当然代码是一致的,只是书写的引用形式不同。
第一个例子——“取颜色()”
例子实现与易语言自身所带“取颜色值()”同样功能,参数形式也一样。在这个例子中,我们一同来看看如何的在“置入代码MASM汇编工具”中定义参数供汇编代码引用。
打开“置入代码MASM汇编工具”,在注释行至"end"行之间,键入上述汇编代码(如上图7-17行代码)。代码中使用proc定义了一个含有“_red ”、“_green”、“_blue”三个参数的函数(子程序)。然后选择“编译”菜单→ “编译为置入代码”或右侧的蓝色三角按钮,这样就能生成我们置入代码所需的字节集数据。
粘贴生成的的代码入易程序,生成如下:
我们再来反汇编一下:
汇编代码“movzx eax,_red”编译后成“MOVZX EAX,BYTE PTR SS:[EBP+8]”
汇编代码“movzx eax,_green”编译后成“MOVZX EAX,BYTE PTR SS:[EBP+C]”
汇编代码“movzx eax,_blue”编译后成“MOVZX EAX,BYTE PTR SS:[EBP+10]”
这样一来,我们就可以完全省去对形如[ebp+8]这样的地址引用,而使用直观的“_reg”参数,余下的工作就让MASM编译器来帮忙,替换成“[ebp+8]”,这算得上一个大优势吧!可别高兴得太早,在子程序开始处怎么会看到两行重复语句“PUSH EBP / MOV EBP , ESP”?这是因为我们在汇编源码上定义了一个“MAIN"子程序,MASM编译时就会自动加上这么两句,同时易语言也会在编译“取颜色()”子程序时添加上这两句的缘故。为了修正堆栈,汇编源码在定义函数后第一个语句即为“LEAVE”。当然,如果大家不喜欢这样重复的代码(我自己就不喜欢,汇编为的就是精简),大可以手工删除第一个“LEAVE”(C9)(201)及之前的字节集(在本例为:{ 85 , 139 , 236 , 201 })即可,余下的就是我们真正需要执行的代码。大家要记住,这参数的引用是按顺序而不是名字!你可以将“红、绿、蓝”的名字改成“一、二、三”。
看看反汇编最后两句“LEAVE / RETN 0C”,而汇编源码中只有“ret”,怎么会~~?这是因为在编译时由MASM编译器根据参数个数进行修改,以便丢掉三个参数来平衡堆栈,这也算是另一个小优势——不用自己计算ret 后应当填写什么数值!
使用这种方法引用参数及局部变量必须与易子程序的参数、变量个数对齐。如果易语言子程序的参数有一个且为可空,此时要注意:实际上参数并不是一个,而是两个。第一个用于表示参数是否为空,第二个才是真正传递的参数。若不对齐引用,我还不知道会发生什么,多半蓝屏死机吧~~哈哈~~本人可不对会你硬盘及未保存数据负责哦。
最后,大家可以自己来试试使用置入代码编写的子程序“取颜色()”所得结果是否与易语言“取颜色值()”一致!
第二个例子——“九九乘法表()”
看到这个表格,有没有提起大家儿时背诵的情景~~
源码下载:/?tid=271756
不多说,直接上源码:
.586
.model flat,stdcall
option casemap:none
.code
;若没有特别需求请不要改动已有的几行。
;请在下面至'end'前输入相关代码:
mov ebx,[ebp+8]
mov ebx,[ebx]
mov eax,2573
mov [ebx],ax
add ebx,2
xor ecx,ecx
inc ecx
xor eax,eax
.while ecx <= 9
push ecx
xor edx,edx
inc edx
.repeat
push edx
;在下面输入双循环执行代码:
mov eax,ecx
add eax,30h
mov [ebx],al
mov eax,49569
mov [ebx+1],ax
mov eax,edx
add eax,30h
mov [ebx+3],al
mov eax,'='
mov [ebx+4],al
mov eax,ecx
mul dl
mov dl,10
div dl
add eax,12336
mov [ebx+5],ax
mov eax,32
mov [ebx+7],al
add ebx,8
pop edx
.break .if edx >= ecx
inc edx
.until edx > 9
mov eax,2573
mov [ebx],ax
add ebx,2
pop ecx
inc ecx
.endw
mov eax,0
mov [ebx],al
leave
ret 4
end
使用“置入代码MASM汇编工具”将上述代码转换成置入代码所需字节集:
置入代码 ({ 139, 93, 8, 139, 27, 184, 13, 10, 0, 0, 102, 137, 3, 131, 195, 2, 51, 201, 65, 51, 192,
235, 89, 81, 51, 210, 66, 82, 139, 193, 131, 192, 48, 136, 3, 184, 161, 193, 0, 0, 102, 137, 67, 1, 139, 194,
131, 192, 48, 136, 67, 3, 184, 61, 0, 0, 0, 136, 67, 4, 139, 193, 246, 226, 178, 10, 246, 242, 5, 48, 48, 0, 0,
102, 137, 67, 5, 184, 32, 0, 0, 0, 136, 67, 7, 131, 195, 8, 90, 59, 209, 115, 6, 66, 131, 250, 9, 118, 184,
184, 13, 10, 0, 0, 102, 137, 3, 131, 195, 2, 89, 65, 131, 249, 9, 118, 162, 184, 0, 0, 0, 0, 136, 3, 201, 194,
4, 0 })
易语言调用格式:
“九九乘法表()”调用如上图。调用前必须先“取空白文本()”这是易语言给“乘法表”这个文本分配内存空间。若不然“九九乘法表(乘法表)”给“乘法表”文本写入内容时必定强制退出!
我们再回过头来看看汇编源码:
“mov ebx,[ebp+8] / mov ebx,[ebx]”两句取得“乘法表”文本变量数据的内存地址。(要对易语言其他数据类型作操作,必须先了解该数据类型的具体保存形式,在此不讲述,要不跑得太远^_^没有这么多时间,而且我还没有完全了解易语言的所有数据。)
“mov eax,2573 /mov [ebx],ax”两句先给“乘法表”加上一个#换行符。
再通过“.while”及“.repeat”构造一个双循环。(这两个均是MASM的高级语法,它们不是CPU指令。MASM编译时会将它们换成相应的CPU指令,应当相信微软已经为这优化得相当好)循环语句内就是一些完成乘法表的具体操作。当中“ .break .if edx >= ecx"定义了在第二个循环记数器edx大于或等于第一个循环记数器ecx时跳出第二个循环,以生成阶梯式的乘法表。使用了这MASM高级语法,是不是让源码看起来更简单明了?!这也是本人喜欢MASM的亮点这一。
到了这,我们再接回开头说说提到要了解的东西——参数传递及堆栈平衡
大家先来看一个易语言程序及其反汇编后的代码。在易程序,新建子程序改名“测试子程序()”为子程序定义三个参数分别为:“参数一、参数二、参数三”然后在“_启动子程序()”调用“测试子程序()”为了便于确定位置,我们来插入一个“标记”置入“8个nop”,易代码如下:
反汇编,结果如下图:
看到我们在调用“测试子程序()”前置入的代码吗?没错,就是“8个nop”,然后就是三个“PUSH”指令,分别是“PUSH 3”、“PUSH 2”、“PUSH 1”子程序调用最右边的参数先入栈,最后是一个“CALL XXX”。在这里,我们可以看到存取参数是通过堆栈来定义的,实际上局部变量是同样使用堆栈存取,也就是通过EPB做指针来完成。
我们假定调用“测试子程序()”前堆栈地址为X,大家一起来看看EBP、参数、局部变量三者间的关系:
堆栈地址(ESP)
X
X-04h
X-08h
X-0Ch
堆栈数据(说明)
起始堆栈
参数三
参数二
参数一
堆栈生长↓
↓EBP值 指令
EBP+10h
EBP+0Ch
EBP+08h
(PUSH 参数三)
(PUSH 参数二)
(PUSH 参数一)
CALL XXX(由CALL引起将返回地址压入堆栈)
X-10h EBP+04H
子程序返方向回地址
X-14h
EBP(这时EBP=X-14h,由此计算其他各值)
EBP-04H
EBP-08H
PUSH EBP / MOV
EBP,ESP
(若子程序有局部变量)
(若子程序有局部变量)
原EBP值
X-18h
X-1Ch
局部变量1
局部变量2
上表是针对易语言子程序编出,但并不是所有程序都是这样的。这要看编辑语言约定使用的类型。易语言堆栈平衡的事情是由子程序使用“ret 0c”来实现的,ret指令后面加一个操作数表示在ret后把堆栈指针esp加上该操作数,以实现将参数丢掉。
在置入代码()时使用参数及局部变量就可以参考上表所示关系引用。如果置入代码中包含返回指令,就要多注意堆栈平衡的问题,记紧要根据参数个数来正确使用ret,要不会引起堆栈错误,要么你交给易语言去做返回处理。
在源程序中,参数、局部变量和esp的关系是由编译器自动维护的,所以大家不必关心它们的具体关系。就是为我们易语言MASM汇编工具定义函数实现参数及局部变量的直接引用提供便捷。建议汇编源码较长,对参数及局部变量引用较多时使用此方法,能有效减少地址引用错误。
版权声明:本文标题:易语言置入代码 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/jishu/1703356716h448253.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论