¤¤¤解释执行stdcall规范的API函数¤¤¤

蓝色光芒 2004-11-26 01:09:39
本文介绍如何让你的脚本解释程序解释执行stdcall规范的API函数
你需要有汇编语言基础,在编写动态调用API程序的时候才用得到,
不废话了开始!
调用API的关键所在就是接口的整理,比如我们在Delphi里面调用API
如:
TSendMessage = Function(hWnd: HWND;
Msg: UINT;
wParam: WPARAM;
lParam: LPARAM): LRESULT; stdcall;

Var
SMG : TSendMessage;

SMG := GetProcAddress(...);

然后我们就可以调用SendMessage函数了,但是我们想一想,如果我们
要写自己的解释程序,难道什么接口我们都在Delphi里定义好,这显然
不正确,因此就有了本文的研究。
本文的核心:让你的程序适应在不需要事先定义任何接口的情况下,根
据用户自定义的接口调用几乎所有形式的stdcall规范的API函数。

//定义API最多的参数个数,如果换动态数组就不需要了
Const
APIParamaMax = 10;
APIParamaSize = 8;

Type
//API参数描述
TAPIParama = Record
ParamType : LongWord; //参数类型
Address : Pointer; //参数地址
end;
PAPIParama = ^TAPIParama;

TAPIParamList = array of TAPIParama; //参数列表
PAPIParamList = ^TAPIParamList; //列表指针

一看TAPIParama的定义,估计很多朋友就明白了,我们需要分两步走,
第一步,整理用户的调用数据,第二步根据这个列表调用API函数


我们再定义一些常量
{API Param Types}
atNULL = $000; //作为返回值类型时,舍弃返回值

atByte = $001;
atShortint = $002;
atSmallint = $003;
atWord = $004;
atInteger = $005;
atLongWord = $006;
atLongint = $007;
atCardinal = $008;

atInt64 = $009;

atReal = $00A;
atSingle = $00B;
atReal48 = $00C; //放弃
atDouble = $00D;
atComp = $00E;
atCurrency = $00F;

atExtended = $010;

atVarPointer = $011;
atBoolean = $012;
atLongBOOL = $013;
atObject = $014;

atProgram = $100; //保存的是API函数地址
atPointer = $101;

OK,我们开始弄程序
调用API的主程序
procedure DOAPI(ParamList : PAPIParamList ; //一个参数列表
APIProc , //保存API地址的
ReturnPara : PAPIParama); stdcall;
//ReturnPara 如果API是函数并且需要返回值 否则设置成NIL

implementation

//ParamList参数整理过程,这里是处理各种API参数的
//根据TAPIParama.ParamType的值来处理参数
//EBX 为参数的指针地址,即PAIPParama
//ECX,EDX 在过程中被使用,使用前请保护ECX,EDX的值,如果ECX,EDX的值需要保护
procedure InitParam;stdcall;
asm
CMP EBX , $0; //参数是否=NIL
JZ @ExitThisProc;

CMP DWORD PTR [EBX] , atPointer; //atPointer模式
JZ @JPPointer;
CMP DWORD PTR [EBX] , atInteger; //atInteger模式
JZ @IntegerMode;
CMP DWORD PTR [EBX] , atReal; //Real模式
JZ @RealMode;
CMP DWORD PTR [EBX] , atByte; //Byte模式 处理过程和Integer一样
JZ @IntegerMode;
CMP DWORD PTR [EBX] , atShortInt; //ShortInt模式 处理过程和Integer一样
JZ @IntegerMode;
CMP DWORD PTR [EBX] , atSmallInt; //SmallInt模式 处理过程和Integer一样
JZ @IntegerMode;
CMP DWORD PTR [EBX] , atWord; //Word模式 处理过程和Integer一样
JZ @IntegerMode;
CMP DWORD PTR [EBX] , atLongWord; //LongWord模式 处理过程和Integer一样
JZ @IntegerMode;
CMP DWORD PTR [EBX] , atCardinal; //Cardinal模式 处理过程和Integer一样
JZ @IntegerMode;
CMP DWORD PTR [EBX] , atInt64; //Int64模式 处理过程和Real一样
JZ @RealMode;
CMP DWORD PTR [EBX] , atSingle; //Single模式 处理过程和Integer一样
JZ @IntegerMode;
CMP DWORD PTR [EBX] , atDouble; //Double模式 处理过程和Real一样
JZ @RealMode;
CMP DWORD PTR [EBX] , atComp; //Comp模式 处理过程和Real一样
JZ @RealMode;
CMP DWORD PTR [EBX] , atCurrency; //Currency模式 处理过程和Real一样
JZ @RealMode;
CMP DWORD PTR [EBX] , atExtended;
JZ @ExtendedMode;
CMP DWORD PTR [EBX] , atVarPointer; //VarPointer模式 处理过程和Integer一样
JZ @IntegerMode;
CMP DWORD PTR [EBX] , atBoolean; //Boolean模式 处理过程和Byte一样
JZ @IntegerMode;
CMP DWORD PTR [EBX] , atLongBool; //LongBool模式 处理过程和Integer一样
JZ @IntegerMode;
CMP DWORD PTR [EBX] , atObject; //Object模式 处理过程和Integer一样
JZ @IntegerMode;


JMP @ExitThisProc;

@IntegerMode: //整数模式
MOV EBX , [EBX+$4]; //TAPIParama.Address的地址
ADD EBX , -$4; //减4,因为在后面加了4
JMP @JPPointer;

@RealMode :
ADD EBX , $4; //TAPIParama.Address的地址
MOV EBX , [EBX] //得到实际的浮点数的地址
POP ECX; //取出RET后的EIP
PUSH DWORD PTR [EBX+4]; //API参数Real入伐
PUSH DWORD PTR [EBX]; //API参数Real入伐
PUSH ECX; //RET后的EIP入伐
JMP @ExitThisProc;

@ExtendedMode:
ADD EBX , $4;
MOV EBX , [EBX]
POP ECX; //取出RET后的EIP
MOV DX , [EBX+6];
PUSH EDX; //API参数入伐
PUSH DWORD PTR [EBX+4]; //API参数入伐
PUSH DWORD PTR [EBX]; //API参数入伐
PUSH ECX; //RET后的EIP入伐
JMP @ExitThisProc;


@JPPointer:
ADD EBX , $4;

@ParamStart:
POP ECX; //取出RET后的EIP
PUSH [EBX] //API需要的参数入伐
PUSH ECX; //RET后的EIP入伐
@ExitThisProc:
end;
...全文
220 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
Eastunfail 2004-11-28
  • 打赏
  • 举报
回复
我以前无聊的时候也写过类似的东西的,支持stdcall/fastcall/cdecl/pascal四种调用约定,还支持成员函数调用,函数原形和指针在编译期间也可以是未知的
http://www.yixel.com/files/LexLib.rar

不过如ehom所说,设计的并不好,需要重构,不过这东西本来就是无聊的时候做的,也懒的重构了
蓝色光芒 2004-11-27
  • 打赏
  • 举报
回复
完成的目标程序贴
http://community.csdn.net/Expert/topic/3580/3580156.xml?temp=.228039
yinzhiw 2004-11-26
  • 打赏
  • 举报
回复
好贴收藏
  • 打赏
  • 举报
回复
qiume 2004-11-26
  • 打赏
  • 举报
回复
Very good.
halfdream 2004-11-26
  • 打赏
  • 举报
回复
收藏。。

楼主顺便把
cdecl,
PASCAL,
SAFECALL,
register,
这几种调用都做了吧:)
zzlingaaa 2004-11-26
  • 打赏
  • 举报
回复
mark
beyondtkl 2004-11-26
  • 打赏
  • 举报
回复
MK
蓝色光芒 2004-11-26
  • 打赏
  • 举报
回复
//API调用过程

procedure DOAPI(ParamList : PAPIParamList ;
APIProc , ReturnPara : PAPIParama); stdcall;
begin
asm
PUSH EDX;
PUSH EBX;
PUSH ECX;
PUSH EAX; //保护现场

MOV EBX , ParamList;
MOV EBX , [EBX]; //TAPIParamList
MOV EAX , [EBX-4]; //High Array
DEC EAX;
@Loop1: //循环从High((ParamList^) DownTo 0
CMP EAX , -1;
JZ @ParamaOver;
MOV EBX , ParamList;
MOV EBX , [EBX];
MOV ECX , EAX;
IMUL ECX , APIParamaSize
ADD EBX , ECX;
Call InitParam; //整理参数
DEC EAX;
JMP @Loop1;

@ParamaOver:
MOV EBX , [APIProc]; //从APIProc参数那里得到API的入口地址
MOV EBX , [EBX + $4];

Call EBX; //调用API函数

//处理函数返回结果
MOV EBX , ReturnPara;
CMP EBX , $0; //是否是NIL
JZ @ExitThisProc;

CMP DWORD [EBX] , atPointer;
JZ @IntegerMode;
CMP DWORD [EBX] , atInteger;
JZ @IntegerMode;
CMP DWORD [EBX] , atReal;
JZ @RealMode;
CMP DWORD [EBX] , atByte;
JZ @ByteMode;
CMP DWORD [EBX] , atShortInt; //ShortInt 和 Byte处理方法一样
JZ @ByteMode;
CMP DWORD [EBX] , atSmallInt;
JZ @SmallIntMode;
CMP DWORD [EBX] , atWord; //Word 和 SmallInt处理方法一样
JZ @SmallIntMode;
CMP DWORD [EBX] , atLongWord; //Word 和 SmallInt处理方法一样
JZ @IntegerMode;
CMP DWORD [EBX] , atCardinal; //Cardinal 和 SmallInt处理方法一样
JZ @IntegerMode;
CMP DWORD [EBX] , atInt64;
JZ @Int64Mode;
CMP DWORD [EBX] , atSingle;
JZ @SingleMode;
CMP DWORD [EBX] , atDouble; //Double 和 Real处理方法一样
JZ @RealMode;
CMP DWORD [EBX] , atComp;
JZ @CompMode;
CMP DWORD [EBX] , atCurrency; //Currency 和 Comp处理方法一样
JZ @CompMode;
CMP DWORD [EBX] , atVarPointer;//Currency 和 Integer处理方法一样
JZ @IntegerMode;
CMP DWORD [EBX] , atBoolean; //Boolean 和 Byte处理方法一样
JZ @ByteMode;
CMP DWORD [EBX] , atLongBool; //LongBool 和 Integer处理方法一样
JZ @IntegerMode;
CMP DWORD [EBX] , atObject; //Object 和 Integer处理方法一样
JZ @IntegerMode;

JMP @ExitThisProc; //无效模式就退出

@IntegerMode:
MOV EBX , [EBX+$4];
//整数模式,就直接设置TAPIParama.Address指向的Integer的值
MOV [EBX] , EAX;
JMP @ExitThisProc;

@RealMode:
MOV EBX , [EBX+$4];
fstp qword ptr [EBX]; //接收浮点数
Wait;
JMP @ExitThisProc;

@ByteMode:
MOV EBX , [EBX+$4];
MOV [EBX] , AL;
JMP @ExitThisProc;

@SmallIntMode:
MOV EBX , [EBX+$4];
MOV [EBX] , AX;
JMP @ExitThisProc;

@Int64Mode:
MOV EBX , [EBX+$4];
MOV [EBX] , EAX;
MOV [EBX+4] , EDX;
JMP @ExitThisProc;

@SingleMode:
MOV EBX , [EBX+$4];
fstp dword ptr [EBX];
Wait;
JMP @ExitThisProc;

@CompMode:
MOV EBX , [EBX+$4];
fistp qword ptr [EBX];
Wait;
JMP @ExitThisProc;


@ExitThisProc:
POP EAX; //恢复现场
POP ECX;
POP EBX;
POP EDX;
end;
end;

补充几句:API参数调用时,如果是Var就是传Var的指针,
如果是结构也是传结构的指针,PChar也是
如果是整数或者Smallint那几个,就是直接传值
如果是浮点数,Var就是传指针,值的话就直接传值


到此为止动态调用API的过程已经全部写完。
蓝色光芒原创,转载请保留这些信息。:)
http://www.1285.net/
zgf@1285.net

下面我们来看看怎么使用这个函数


下面是调用SendMessage的过程
var
//参数的值,也可以不要这几个,因为SendMessage没有传指针的参数
IntList : array [1..4] of integer;
ReturnInt : integer;
ParamList : TAPIParamList;
ReturnPara : TAPIParama;
APIProc : TAPIParama;
i : integer;
begin
IntList[1] := Handle;
IntList[2] := WM_Close;
IntList[3] := 0;
IntList[4] := 0;
SetLength(ParamList,4);
for i:=Low(ParamList) to High(ParamList) do begin
ParamList[i].ParamType := atinteger;
ParamList[i].Address := @IntList[i];
end;
APIProc.ParamType := atProgram;
APIProc.Address := @SendMessage; //API函数的入口地址
ReturnPara.ParamType := atinteger;
ReturnPara.Address := @ReturnInt;
DOAPI(@ParamList,@APIProc,@ReturnPara);
//返回值被保存到ReturnInt里了。
end;


不难看出,我们在解释程序里面只需要解释用户定义的API函数地址,
API的参数类型,API的返回值类型,API参数的值,并整理这些数据,
放到一个Array of Integer 或者Array of Real..里面,传给DOAPI就
可以了。取回结果,并交给解释器处理


结构调用的例子:
var
SD : TSystemTime;
ParamList : TAPIParamList;
APIProc : TAPIParama;
begin
SetLength(ParamList,1);
ParamList[0].ParamType := atPointer;
ParamType[0].Address := @SD;
APIProc.ParamType := atProgram;
APIProc.Address := @GetSystemTime;
DOAPI(@ParamList,@APIProc,NIL);
//SD这个结构里就有当前的日期和时间了
end;

如果参数或者结果是对象
var
OBJ : TObject;
....
ParamList[0].ParamType := atObject;
ParamType[0].Address := @OBJ;
....

看起来好麻烦,调用一个API需要作这么复杂的事情,直接调用
GetSystemTime(SD)不就好了吗?写解释程序好象就这个样:(
从字符串翻译成程序来运行就这么回事,如:VB,VF

1,183

社区成员

发帖
与我相关
我的任务
社区描述
Delphi Windows SDK/API
社区管理员
  • Windows SDK/API社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧