(okwary) 小叹的学习园地

与天斗?不够高~ 与地斗?不够阔 与人斗? 脸皮不够厚

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

Delphi关键字【内联汇编】

 
   DELPHI内联汇编好象有得天独厚的优势,尤其是API的调用,处理好各成员参数后,可以直接CALL API名称。利用内联汇编我们可以为程序添加各种异常,添加反调试代码,添加花指令,还可以令某些难写的注册机简单化等.我把DELPHI内联汇编的一点点使用心得写出来,目的是想与大家交流,挖掘更多的这方面的领域。也希望大家能够指正一些错误。
一、 数据格式
整型数据:8位的用AL返回,16位的用AX返回,32位的用EAX返回。
BYTE(8位):BYTE、CHAR、SHORTINT、BOOLEAN 
WORD(16位): SMALLINT、WORD
DWORD(32位):INTEGER、LONGWORD、ANSISTRING、POINTER、CLASS、LONGINT、STRING
ST(0):SINGLE、DOUBLE、EXTENDED、COM

如:
var
  ByteVar: Byte;
  WordVar: Word;
  IntVar: Integer;
begin
asm
  MOV     AL,ByteVar
  MOV     BX,WordVar
  MOV     ECX,IntVar
end;
end;

实型:用ST(0)返回
  指针:用EAX返回
  长字符串:用EAX返回其所在地址
  变量:可用@Result返回

   16进制的表示方式:
   如果是0-9开头的16进制值直接在后面加H或在前面加$号,如:1AH,$1A
   如果是字母开头的前面加0再在后面跟H,或者直接用$号,如:0AH,$A

  几个修饰符:
  OFFSET   返回内存地址中的立即数
  [....]   返回内存地址,与OFFSET相反,如:MOV EAX,OFFSET [XXXX]=MOV  EAXX,[OFFSET XXXX]=MOV EAX,XXXX
  HIGH     返回高8位的立即数
  LOW      返回低8位的立即数
  &        防止变量与汇编中的寄存器同名而在前面加& 号,如:
         EAX:INTEGER;  ...
         MOV &EAX,10H  这里的&EAX不是EAX寄存器
  .        .号的一种用法:
         var       
         STR: Word;  ..
         MOV     DL,STR.Byte或DL,Byte(STR)


二、 嵌入式汇编的格式
  Delphi是使用ASM……END来标志汇编语句
   如:
         ASM
   mov al,1
   mov bl,al
       END;

 一个简单加法函数:
FUNCTION SUM(X,Y:INTEGER):INTEGER;
BEGIN
  ASM
     MOV EAX,X
     ADD EAX,Y
     MOV @RESULT,EAX
  END;
END;

   Byte转换为16进制字符串:
function ByteToHex(Src: Byte): String;
begin
  SetLength(Result, 2);
  asm
    MOV         EDI, [Result]
    MOV         EDI, [EDI]
    MOV         AL, Src
    MOV         AH, AL          // Save to AH
    SHR         AL, 4           // Output High 4 Bits
    ADD         AL, '0'
    CMP         AL, '9'
    JBE         @@OutCharLo
    ADD         AL, 'A'-'9'-1
@@OutCharLo:
    AND         AH, $f
    ADD         AH, '0'
    CMP         AH, '9'
    JBE         @@OutChar
    ADD         AH, 'A'-'9'-1
@@OutChar:
    STOSW
  end;
end;

三、 可用的寄存器
  32位寄存器EAX EBX ECX EDX ESP EBP ESI EDI
 16位寄存器AX BX CX DX SP BP SI DI
 8位寄存器AL BL CL DL(低8位) AH BH CH DH(高8位)
 16位段寄存器CS DS SS ES
  32位段寄存器 FS GS
 以及协处理器寄存器堆栈 ST
    一个ASM statement 必须保护EDI,ESI,ESP,EBP和EBX寄存器,但是可以自由的修改EAX,ECX和EDX寄存器。
   默认情况下,delphi使用“register”方式,若参数在3个以内, 将分别使用eax、edx和ecx,如果超过三个,则用堆栈传递。返回参数的存放视长度而定,例如8位用al返回,16位用ax,32位用eax,64位用用两个32位寄存器edx:eax,其中eax是低位。如果你想用EBX寄存器,接得这样写:
asm
  push ebx
  mov ebx,2
  mov IntVar,ebx
  pop ebx
end;

    如:FUNCTION(T1,T2,T3:INTEGER):INTEGER,可以默认为T1存在EAX中,T2存在EDX中,T3存在ECX中。
    DELPHI的标签名一般都以@开头,比如@exit、@001等

四、 CALL的应用
    在汇编中写代码要保存寄存器现场(保存使用前的寄存器状态,使用Push压栈和Pop从栈中弹出),不过这一切对于Delphi的嵌入式汇编是没有必要的(除非你自己要使用Push和Pop),因为Delphi已经帮你做了,不必担心会使数据丢掉。
   DELPHI内联汇编中同样可以用汇编中CALL的功能。
比如:
asm
   CALL @1
   JMP @EXIT
@1:
   MOV EAX,1
   MOV SN,EAX
   RETN
@exit:
     end;

   call的第二种用法,在汇编代码中直接调用FUNCTION函数,如:
   function cacl(eax: integer):integer;
 var
      ...
  begin
     ....
     Result:=code;
  end; 
 调用时可以直接CALL CACL:
    ASM
      CALL CACL
      ...
      END

    编语句中的Call语句,可以用于调用其它过程,既可以是其它汇编程序段也可以是Delphi中的标准过程:
  例如:假设新建一个窗体并在上面加了一个按钮,在Click事件中写入以下代码
  procedure TForm1.Button1Click(Sender: TObject);
  begin
   showmessage(`ok');
  end;
  再写一个过程_X
  function TForm1._x(var i:smallint):integer;
  asm
   call button1click
  end;
  执行_x的结果就可以显示消息框。

五、调用API函数
     DELPHI内联汇编中调用API函数函数非常简单,比如调用PostQuitMessage:
Asm
Push 0
Call PostQuitMessage(或Call SYSTEM.PostQuitMessage)
End

调用MessageBox函数:
procedure TForm1.Button2Click(Sender: TObject);
  var
   szTitle:string;
   szCaption:string;
  begin
   szTitle:='您好!';
   szCaption:='这是一个在内嵌汇编中调用stdcall类型函数的例子.';
   asm
    PUSH MB_OK+MB_ICONINFORMATION
    PUSH szTitle
    PUSH szCaption
    PUSH 0
    CALL MessageBox
   end;
  end;

调用GetFileSize函数
function stdcalldemo: Integer;
var
  FH: THandle;
begin
  FH:= FileOpen(’c: oot.ini’,fmOpenRead);
  asm
    push 0
    push FH
    call GetFileSize
    mov @Result,eax
  end;           
  Result:= GetFileSize(FH,0);//此句就相当于上面的汇编调用方式
  FileClose(FH);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
  showmessage(IntToStr(stdcalldemo));
end;

六、汇编的调试。
    如果发现汇编通不过,就要注意停下后的位置的代码:如变量的跟踪、断点、堆栈查看……可以在关键代码处下断,停下后打开View菜单的Debug Windows的CPU VIEW窗口,F8单步跟踪。如:
Unit1.pas.62:     PUSH MB_OK+MB_ICONINFORMATION
00455E8F 6A40             push $40
Unit1.pas.63:     PUSH szTitle
00455E91 FF75FC           push dword ptr [ebp-$04]
Unit1.pas.64:     PUSH szCaption
00455E94 FF75F8           push dword ptr [ebp-$08]
Unit1.pas.65:     PUSH 0
00455E97 6A00             push $00
Unit1.pas.66:     CALL MessageBox
00455E99 E8FA0DFBFF       call MessageBox
Unit1.pas.69:   end;
00455E9E 33C0             xor eax,eax

七、常见易错语句。
MOV ESI,DOWRD PTR SS:[TEXT]     对(TEXT为STRING)
MOV ESI,DOWRD PTR [TEXT]         对(段寄存器CS,DS,SS,ES可省略)
MOV AL,DOWRD PTR SS:[TEXT]      错(DWORD为32位)
MOV AL,BYTE PTR [TEXT]             对
MOV EAX,BYTE PTR [TEXT]           错(DWORD为8位)
MOV AL,WORD PTR [TEXT]            错(DWORD为16位)
MOV EAX,WORD PTR [TEXT]          错
MOV AX,WORD PTR [TEXT]            对
MOV [A],EAX
MOV BYTE PTR[EDI],'A'
MOV AL,[EDI]
MOV EAX,X         //X指向的值赋值给EAX
MOV EAX,[EAX]     //X指向的地址赋值给EAX
MOV DOWRD PTR[ESP],EAX          对
MOV DOWRD PTR[ECX],EAX          对
MOV DOWRD PTR[ECX+4],EAX        对
MOV DOWRD PTR[ECX+EDI],EAX      对
MOV DOWRD PTR[ESI],EAX          错
MOV DOWRD PTR[EDX],EAX          错
MOV DOWRD PTR[EBX],EAX          错

posted on 2008-12-21 13:15  okwary  阅读(563)  评论(0编辑  收藏  举报
ggg