本文介绍如何让你的脚本解释程序解释执行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;
//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/ [email protected]
下面我们来看看怎么使用这个函数
下面是调用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

|