本文介绍如何让你的脚本解释程序解释执行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是函数并且需要返回值 否则设置成NILimplementation//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;

解决方案 »

  1.   

    //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
      

  2.   

    收藏。。楼主顺便把
    cdecl,
    PASCAL,
    SAFECALL,
    register,
    这几种调用都做了吧:)
      

  3.   

    完成的目标程序贴
    http://community.csdn.net/Expert/topic/3580/3580156.xml?temp=.228039
      

  4.   

    我以前无聊的时候也写过类似的东西的,支持stdcall/fastcall/cdecl/pascal四种调用约定,还支持成员函数调用,函数原形和指针在编译期间也可以是未知的
    http://www.yixel.com/files/LexLib.rar不过如ehom所说,设计的并不好,需要重构,不过这东西本来就是无聊的时候做的,也懒的重构了