脚本解释程序解释如果动态执行API函数

类别:编程语言 点击:0 评论:0 推荐:

本文介绍如何让你的脚本解释程序解释执行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

 

本文地址:http://com.8s8s.com/it/it24190.htm