奇淫怪巧之在Delphi中调用不申明函数
前一阵子,研究了一段时间的Win32Asm,研究到后来发现Win32的ASM实际上还是和C版的介绍的一样。甚至还封装了一个简版的类似VCL库结构框架的32ASM结构库,不过搞着搞着就没兴趣了,也没继续往下深入,唉!发现年龄越来越大,人也越来越懒。
休息了好长一阵子,在乱七八糟的东西乱弄一堆之后,总算发现了一个能有点用处的东西,于是就欣欣然跑来记录一下日志博客以为备份。
我们都知道在Delphi,VC等这类静态检测形的语言,如果要使用一个函数,必须要先申明一下此函数结构,然后调用的时候,编译器才会根据申明的函数结构进行编译产生数据以做调用。比如MessageBox这类函数,都是在Windows中申明过了,调用的时候可以根据声明来编辑代码。对于使用Delphi这类使用静态预先编译检查的来说,这种方法非常适用简单,但是如果假设,俺们需要自己设计一个脚本语言,然后在这个脚本语言中,我们需要可以调用DLL中的函数,于是我们也给设计一个类似于Delphi的这种预先声明的模式,然后脚本中调用,甚至,还可以不用预先申明,而只用根据参数以及函数名称来调用这个dll中的函数,那么此时我们在脚本中写的东西,我们在Delphi中就并没有预先声明的这个函数结构了。按照常规来调用自然不可行 ,如果说对应每个DLL中的函数给声明一个函数出来外部,这个更是不可能,因为DLL是不可控的,我们并不知道用户用脚本需要调用什么DLL,以及调用什么DLL中的函数。那么此时,我们如何来调用这个DLL中的函数,让脚本中的实现可用呢。我们先来分析一下,首先脚本调用肯定会要知道DLL名称,然后会输入函数名称,然后就是函数参数了,变相来说,就是我们有DLL名可以获得DllHandle,有 DLLHandle和函数名称我们可以获得函数地址CodeAddr。
就是说比如我们设计一个脚本,在脚本中可能输入如下:
dll.MessageBoxW(0,'asf','afd',64);
那么我们在Delphi中能获得的就是MessageBoxW这个函数的函数地址以及 传递进入的参数,但是我们并不会声明这个函数。我们现在的目的就是要在Delphi中把这个脚本给解析出来并正确调用MessageBoxW。也就如题说的通过未声明函数的地址调用函数。
实际上任何语言中调用某一个函数,模式都是差不多,无非是传参和调用,而传参又有好多模式,比如Delphi的就有Register,Stdcall,Safecall,cdecl等,C也有这些调用模式,Delphi的Register模式对应C中的fastcall。正是这些传参模式的不同而导致了参数的入栈 方式不一样,Register是用优先通过寄存器,然后才入栈,stdcall和cdecl等都是直接入栈,并且入栈顺序都是参数从右向左入栈,唯一的区别是stdcall是不用自己管理栈函数在调用完成之后会自动清理堆栈空间,而cdecl需要调用者我们自己来清理堆栈空间。SafeCall是stdcall模式中加上了对于Com的异常处理等。我们一般Dll中的传参模式都是stdcall和cdecl,所以,这里就只针对这两个来进行处理。
那么现在的任务就是将参数压入堆栈,然后call一下函数地址就OK了。于是重要的任何就是参数压栈 ,call地址这个是最简单的。
现在就需要来分析一下参数压栈,我们都知道在32位系统中,以4字节为单位进行传输的,所以说基本上传递的参数都是4字节模式,那么我们byte,char,wchar,boolean等类型都需要变成4字节来做传输,int64,double等占据8字节,就需要变成2个4字节进行压栈。所以此时我们就可以设计两个数据结构用来处理转换这些类型
end;
tva结构就专门用来将那些低于4字节的参数变成4字节然后进行Push,而 tpint64dbl就是将8字节的Int64和double变成2个4字节进行压栈, tpint64dbl在压栈时,先压low低字节,然后压入High高4字节。于是这些基本参数就可以一一压入堆栈。然后就是一些特殊类型的字段,比如说object,string,以及Record等复杂类型的数据的压栈模式,这个俺们只要懂一点Win32汇编的就可以知道,这些参数的压栈实际上都是偏移地址入栈,所以,取得其地址然后压入堆栈就行了。
那么处理模式可以如下:
如果参数是String,那么就直接
st := Params[i];
end;
如果是Object,那么在 Delphi中直接就是地址
然后传递参数的模式,我们就可以用for 尾部 downto 0按照规则进行压栈
如果是高版本的Delphi我们可以直接用array of TValue作为参数进行传递,那么函数的调用实现模式可以如下
var
i: Integer;
type
tva=record
case Integer of
0: (vi: Integer);
1: (vb: Boolean);
2: (vc: Char);
3: (vac: AnsiChar);
4: (vf: Single);
5: (vd: DWORD);
end;
tpint64dbl = record
case Integer of
0: (value: int64);
1: (vdouble: Double);
2:
(
High: DWORD;
low: DWORD
)
end;
var
p64: tpint64dbl;
v: tva;
tmp: Integer;
st: string;
begin
for i := High(Params) downto 0 do
begin
case Params[i].TypeInfo.Kind of
tkInteger:
begin
tmp := Params[i].AsInteger;
asm
push tmp
end;
end;
tkChar:
begin
v.vac := params[i].AsType<AnsiChar>;
asm
push v
end;
end;
tkWChar:
begin
v.vc := Params[i].AsType<Char>;
asm
push v
end;
end;
tkFloat:
begin
v.vf := Params[i].AsType<Single>;
asm
push v
end;
end;
tkInt64:
begin
p64.value := Params[i].AsInt64;
asm
lea ecx,p64
push [ecx][4] //先压低字节,再压高字节
push [ecx][0]
end;
end;
tkRecord,tkClass:
begin
tmp := Integer(Params[i].GetReferenceToRawData);
asm
push tmp
end;
end;
tkString,tkUString:
begin
st := Params[i].AsString;
tmp := Integer(PChar(st));
asm
push tmp
end;
end;
end;
end;
tmp := Integer(codeptr);
asm
call tmp
mov result,eax
end;
end;
使用方法
if h <> 0 then
fh := GetProcAddress(h,'MessageBoxW');
if fh <> nil then
begin
InvokeRtti(fh,[TValue.From(Handle),'asf','234',64])
end;
如果是低版本的,可以由Variant入手来实现如下
var
i: Integer;
type
tva=record
case Integer of
0: (vi: Integer);
1: (vb: Boolean);
2: (vc: Char);
3: (vac: AnsiChar);
4: (vf: Single);
5: (vd: DWORD);
end;
tpint64dbl = record
case Integer of
0: (value: int64);
1: (vdouble: Double);
2:
(
High: DWORD;
low: DWORD
)
end;
var
p64: tpint64dbl;
v: tva;
tmp: Integer;
st: string;
ast: AnsiString;
vtype: TVarType;
begin
for i := High(Params) downto 0 do
begin
vtype := VarType(Params[i]);
case vtype of
varUString:
begin
st := Params[i];
tmp := Integer(PChar(st));
asm
push tmp
end;
end;
varString:
begin
st := Params[i];
ast := AnsiString(st);
tmp := Integer(PAnsiChar(ast));
asm
push tmp
end;
end;
varInteger,varSmallint,varWord,varByte:
begin
v.vi := Params[i];
asm
push v
end;
end;
varLongWord:
begin
v.vd := Params[i];
asm
push v
end;
end;
varSingle:
begin
v.vf := Params[i];
asm
push v
end;
end;
varDouble:
begin
p64.vdouble := Params[i];
asm
lea ecx,p64
push [ecx][4] //先压低字节,再压高字节
push [ecx][0]
end;
end;
varInt64:
begin
p64.value := Params[i];
asm
lea ecx,p64
push [ecx][4] //先压低字节,再压高字节
push [ecx][0]
end;
end;
end;
end;
tmp := Integer(codeptr);
asm
call tmp
mov result,eax
end;
end;
用法类似如下:
begin
ShowMessage(msg+floattostr(tmp));
end;
var
tmp: Single;
begin
tmp := 13234.24;
Invoke(@Showmsg,['asfsf',tmp])
end;
至此基本上的功能就完成了,不过此种方法有一个不好的问题,那就是对于调用函数的返回结果,无法预测,返回的都是integer类型,而无法确定返回的结果是地址还是确实就是integer还是说是其他类型。另外,此方法并非全面的一些实现,如果要使用的全面请反汇编参考Delphi对应的函数调用的参数传递过程以对应进行修正。
奇淫怪巧之在Delphi中调用不申明函数相关推荐
- 奇淫怪巧之给Delphi的PrintDialog增加一个页码选定范围打印的Edit
在Delphi中使用PrintDialog打印对话框的时候,这个控件有三个选项,就是PrintRang那个属性的三个选项,其中有一个选项三,让我们自定义选择页码范围来打印.但是比较蛋疼的是,这个地方选 ...
- 你可能不知道的 docker 命令的奇淫怪巧
你可能不知道的 docker 命令的奇淫怪巧 Intro 分享一些可能会用到的一些简单实用却可能是你不知道的 docker 命令 dangling images build 自己的 docker 镜像 ...
- 在Delphi中调用外部DLL 之External DLL 导入DLL
调用一个DLL比写一个DLL要容易一些.首先给大家介绍的是静态调用方法,稍后将介绍动态调用方法,并就两种方法做一个比较.同样的,我们先举一个静态调用的例子. unit Unit1;interfaceu ...
- 如何在Delphi 中调用C#生成的DLL类库
最近需要写一个和给上位机和下位机通讯的接口,而上位机是用Delphi开发的,所以就需要用C#做一类库给Delphi调用 大概步骤: 1.首先在VS2008中新建一个类项目名为TestDelphi,然后 ...
- Delphi中的编译期函数Dec,Inc,Odd,Pred,Succ,Ord,Chr,Low,High,Sizeof【转】
原文:http://www.chinadmd.com/file/uouvp6rcxu6wasxrozvw66op_1.html Delphi中的编译期函数Dec,Inc,Odd,Pred,Succ,O ...
- Delphi中获取路径的函数
Delphi中获取路径的函数 //********获取当前路径*************** GetCurrentDir() //获取当前的Windows打开的目录,无斜'\' extractFile ...
- 在C#中调用windows API函数
Api函数是构筑Windws应用程序的基石,每一种Windows应用程序开发工具,它提供的底层函数都间接或直接地调用了Windows API函数,同时为了实现功能扩展,一般也都提供了调用Windows ...
- f2py支持在fortran语言中调用其他Fortran函数或C代码或Python代码
f2py支持在fortran语言中调用其他Fortran函数或C代码或Python代码 分类: Python Numpy_Scipy fortran MinGW_GCC_Boost f2py 2012 ...
- Js文件中调用其它Js函数的方法(转)
2019独角兽企业重金招聘Python工程师标准>>> Js文件中调用其它Js函数的方法 在项目开发过程中,也许你会遇这样的情况.在某一Js文件中需要完成某一功能,但这一功能的大部分 ...
最新文章
- Mongo报如下类似错误时的修改方法Cannot natively represent the long 1396367483000 on this platform...
- Codeforces Round #578 (Div. 2)
- bch怎么挖_BCH是什么?
- 新设计了自己博客的模板
- ofbiz mysql_ofbiz+mysql安装求教
- 【收集】腾讯AlloyTeam
- 【Unity开源项目精选】UniRx:Unity中的响应式编程
- substance design graph 不显示_每天1分钟背单词 词根 graph
- 免费java版我的世界下载教程,我的世界java版下载,我的世界java版下载教程
- STM32F107RBT6移植freeRTOS
- 数据解读 | 高考志愿慎重填,大学四年不留白
- google服务框架
- postfix反垃圾邮件
- Gentoo Linux+KDE Plasma桌面安装教程
- adb命令——简单常用命令介绍:将文件从手机上传输到电脑里:adb pull /sdcard/123.png c:\users\del\desktop...
- CNCF宣布TUF项目正式毕业
- 死锁简述(死锁产生的四个条件,预防死锁)
- 计算机如何访问苹果6s的相册,如何在电脑中打开苹果手机中的HEIC格式的照片?...
- 一看就懂的字符串匹配算法 之 BM算法
- 刷脸设备引进越来越多的人喜欢靠脸消费