小弟用delphi编程 打算内嵌入一段汇编 主要的意思是把esi所指向的字符传传递到str里
小弟用 mov str,esi不成功 求教高手 我应该如何做?

  • 标 题:答复
  • 作 者:gzgzlxg
  • 时 间:2007-04-08 08:22

代码:
procedure TForm1.Button1Click(Sender: TObject);
var
  str:String;
  s:String;
begin
  // 从Edit1.Text 中取一个字符串
  //我不知到你esi中的字符串是从那里飞来的,所以只能自己凭想象弄一个
  s:=Edit1.Text;
  asm
    mov  esi, s

    // LStrLAsg 的入口参数
    // ->    EAX     pointer to dest
    //       EDX     source
    lea  eax, str
    mov  edx, esi

    call system.@LStrLAsg
  end;
  // 将 str 里的字符串送到 Edit2中,证明字符串被传递到 str 中了
  // 哈哈,是不是太简单了。
  Edit2.Text:=str;
end;

下面再给出一个可能是你想要的方法
代码:
procedure TForm1.Button2Click(Sender: TObject);
type
  StrRec = packed record
    allocSiz: Longint;
    refCnt: Longint;
    Length: Longint;
  end;
const
  skew = sizeof(StrRec);
var
  str:string;
  s:string;
  D:Dword;
begin
  s:=Edit1.Text;
  asm
    mov  esi, s

    // 使用var 声名的变量 str 只是一个空的指针,在实际使用时必须初始化。
    // LStrSetLength 入口参数
    // ->    EAX     Pointer to str
    //       EDX new length
    lea  eax, str
    mov  edx, [esi-skew].StrRec.Length
    push edx
    call system.@LStrSetLength

    mov  edi,str
    pop  ecx
    cld
    rep  movsb
  end;
  Edit2.Text:=str;
end;

  • 标 题:答复
  • 作 者:精灵猪
  • 时 间:2007-04-08 15:27

感谢gzgzlxg大哥的回答,不过我不是要把exit1.text的内容传递给str 由于我是HOOK了一段程序的内存 类似打补丁那样 我是要把ESI寄存器执行到这里的字符串传递给str 请问这样应该如何做?

  • 标 题:答复
  • 作 者:gzgzlxg
  • 时 间:2007-04-08 16:21

就使用第二个方法,关键你需要自己先得到这个在esi中的字符串的长度,然后使用类似上面的方法,你先删除第一句:
  mov  esi, s   //esi已经有你字符串的地址了
然后修改下面这句:
//  mov  edx, [esi-skew].StrRec.Length
  mov  edx, esi中字符串的长度
然后就OK了。
再学不会,我也无法教你了,这么简单的修改一下都做不到,前面给出的方法是给你一个思路,程序还是要自己写的,这个思路的关键,就是要知道Delphi内部有一套自己管理字符串的方法,你不能简单的用 rep movsb 这种指令将字符串传递到 Delphi 的字符串中去,必须按照 Delphi 的内部机制去处理,其中第一个方法的前提是,这两个字符串必须是 Delphi 格式的字符串,你那个esi中的字符串显然是不符合这个要求的,所以要使用第二个方法,第一个方法中,Delphi 的那条命令已经自动为目标字符串分配了内存,第二个方法中你就必须自己去为目标字符串分配内存,否则就会发生不可估计的错误。LStrSetLength函数就是为那个字符串分配内存,这样你就可以将esi中的字符串传递到Delphi中了。

  • 标 题:答复
  • 作 者:ldsjlm
  • 时 间:2007-04-08 17:47

发过简单的例子,看对你有没有帮助
   
        push    0
        push    ecx
        lea     edx, dword ptr [esi+8]
        push    edx                               
        call    005C3350                        
        add     esp, $0C
        
  假入上面的代码是一个加密过程,edx指向需要加密的数据,现在要在加密过程执行之前把数据读出来
  
      
下面是一种处理方法:

        push    0
        push    ecx
        lea     edx, dword ptr [esi+8]
        push    edx
        call    GetEnPacket  //005C3350           把005C3350替换为GetEnPacket
        add     esp, 0C
        
var
  SaveDataAddr:DWORD;
  temp:DWORD;
  funcaddr:DWORD;

procedure ReadData;
var
  TempData: DWORD;
  Len: DWORD;
  str: string;
  Mem:PChar;
begin
  TempData := SaveDataAddr - 4;
  Len := PWORD(TempData)^;   //得到数据长度
  GetMem(Mem,Len);
  CopyMemory(Mem,pointer(TempData),Len);//数据复制到mem
  //一些处理
  FreeMem(Mem);
end;


procedure GetEnPacket;
begin
  asm
    mov SaveDataAddr,edx    //edx指向加密前的数据
  end;
  asm 
    pushad
  end;
  ReadData; //读取数据
  asm
    popad
  end;
  asm
    pop temp  //为了堆栈平衡
    mov funcaddr, $005C32F0 
    CAll funcaddr  //call原来的加密过程 .如果果005C32F0
    push Temp   //为了堆栈平衡
  end;
end;

  • 标 题:答复
  • 作 者:Sam.com
  • 时 间:2007-04-09 10:03

不知道这个行不行,我也经常要在Delphi中加入汇编,Delphi中有很多类似的函数,有些直接就用汇编写的,我都是自己改的,下面这个是改自Move函数,本来我这个函数里的变量都是局部的,所以对于Source,Str和Count都是用的Mov,如果是全局的话有可能要改成Lea,具体调试下就知道

代码:
//      LEA     ESI,Source    {假设此时ESI已经指向了你的源Str,此步忽略}
        LEA     EDI,str       {假设你的Str是全局变量的话}
                              {如果是局部变量的话应该要Mov Edi,Str,自己试下}
        
        MOV     ECX,count     {Count为你要复制的长度,全局或局部好像也要处理下}
        MOV     EAX,ECX

        CMP     EDI,ESI
        JA      @@down
        JE      @@exit

        SAR     ECX,2           { copy count DIV 4 dwords       }
        JS      @@exit

        REP     MOVSD

        MOV     ECX,EAX
        AND     ECX,03H
        REP     MOVSB           { copy count MOD 4 bytes        }
        JMP     @@exit

@@down:
        LEA     ESI,[ESI+ECX-4] { point ESI to last dword of source     }
        LEA     EDI,[EDI+ECX-4] { point EDI to last dword of dest       }

        SAR     ECX,2           { copy count DIV 4 dwords       }
        JS      @@exit
        STD
        REP     MOVSD

        MOV     ECX,EAX
        AND     ECX,03H         { copy count MOD 4 bytes        }
        ADD     ESI,4-1         { point to last byte of rest    }
        ADD     EDI,4-1
        REP     MOVSB
        CLD
@@exit:

  • 标 题:答复
  • 作 者:gzgzlxg
  • 时 间:2007-04-09 10:20

Sam.com的方法其实就是Delphi Vcl System.pas中 Move 函数的源代码,把这个源代码抄过来不如直接使用这个源代码,在Delphi中对Vcl中的内部函数的调用需要指明该函数所在文件的文件名,这样你就可以直接使用这个 Move 函数了,没有必要将已经现成的东西抄过来使用。

代码:
// ->EAX     Pointer to source
//   EDX     Pointer to destination
//   ECX     Count  
  mov  eax,esi
  mov  edx,str
  mov  ecx,长度
  call system.MOVE

问题的关键还在于没有为字符串分配内存。这样运行结果难以预料。

  • 标 题:答复
  • 作 者:Sam.com
  • 时 间:2007-04-09 10:36

引用:
Sam.com的方法其实就是Delphi Vcl System.pas中 Move 函数的源代码,把这个源代码抄过来不如直接使用这个源代码,在Delphi中对Vcl中的内部函数的调用需要指明该函数所在文件的文件名,这样你就可以直接使用这个 Move 函数了,没有必要将已经现成的东西抄过来使用 
嗯~~我这个只是把他源代码简单的拿出来用~如果直接Call system.Move的话,不知道会不会把Move这一段编译成如下,我试过在驱动中调用这个函数的话,他是按这个来编译了,驱动执行就死机,但我直接用汇编就没问题了,不知道是什么原因,对于字符串分配内存的问题直接来个Str:=''不就行了吗?:

代码:
procedure Move(const Source; var Dest; count : Integer);
{$IFDEF PUREPASCAL}
var
  S, D: PChar;
  I: Integer;
begin
  S := PChar(@Source);
  D := PChar(@Dest);
  if S = D then Exit;
  if Cardinal(D) > Cardinal(S) then
    for I := count-1 downto 0 do
      D[i] := S[i]
  else
    for I := 0 to count-1 do
      D[i] := S[i];
end;
{$ELSE}
asm
.......

  • 标 题:答复
  • 作 者:gzgzlxg
  • 时 间:2007-04-09 11:31

不会,如果没有定义纯种的Pascal,就不会发生这样的事。
你可以通过CPU调试窗口,看到这个MOVE的具体代码。

引用:
对于字符串分配内存的问题直接来个Str:=''不就行了吗?:
Delphi的字符串管理机制非常复杂,当选择 Huge String 模式,即支持巨型字符串时,对字符串进行操作,只要字符串的长度发生变化,都要重新分配内存来存放新的字符串:
  
代码:
Str:='';

这条指令进行时,Delphi调用的是LStrClr函数,LStrClr函数非常复杂,对于一个已经分配过内存的字符串,LStrClr将释放原来分配的内存,然后重新建立一个空字符串,对于没有初始化过的字符串,LStrClr将分配相应的内存,建立一个没有长度的字符串。
如果没有选择 Huge String 模式,所有字符串都是定长的,这时,Str:='';就直接分配了256字节长度的内存,而且在字符串操作过程中,不会再分配内存。
代码:
  StrRec = packed record
    allocSiz: Longint;
    refCnt: Longint;
    Length: Longint;
  end;

上面的结构是Huge String模式下Delphi的字符串结构,存放在一个字符串的前面,这样做的目的是兼容C格式的字符串,Delphi中的任何字符串指针都是指向这个字符串的起始位置,而不是指向这个字符串的结构位置,这样对一个Delphi的字符串可以直接当作C字符串来操作,但用户进行类似操作的时候,字符串操作的长度不要大于 allocSiz 的长度,这个长度是Delphi在初始化字符串是为该字符串分配的实际空间大小,Length是该字符串的实际长度,allocSiz >= Length。refCnt是一个字符串使用计数器,如果这个计数器等于0时,将释放这个字符串所占据的内存,所以如果对该字符串多次引用,即使你认为自己释放了该字符串,只要refCnt大于零,Delphi就不会释放该字符串所占据的内存。

  • 标 题:答复
  • 作 者:Sam.com
  • 时 间:2007-04-09 12:15

多谢提醒,忘记了String是不好控制,我平时是这样来做的,如果按楼主的要求,就先定义一个array of byte,先用Move把Esi指向的字符串复制到Buf去,然后再用下面这个函数提取String出来,这样应该能解决了吧~


代码:
var
  Buf:array [0..255] of byte;

str:=BytesToString(Buf,0,Count);



function BytesToString(Bytes: array of Byte; StartPos, Len: Integer): string;
var
  I: Integer;
  TmpStr: string;
begin
  TmpStr:='';
  for I:=StartPos to StartPos+Len-1 do
    if (Bytes[i]=0) then
    begin
      Result:=TmpStr;
      Exit;
    end
    else
      TmpStr:=TmpStr+Char(Bytes[i]);
  Result:=TmpStr;
end;