断点为何失效的简单分析 -电脑资料

电脑资料 时间:2019-01-01 我要投稿
【www.unjs.com - 电脑资料】

    标 题:关于Delphi/BCB程序中GetWindowTextA/GetDlgItemTextA断点为何失效的简单分析 (7千字)

    发信人:blowfish

    时 间:2001-12-28 17:02:39

    详细信息:

    VCL中标准的单行编辑控件TEdit和多行编辑控件TMemo在类树中的派生关系如下:

    TObject -> TPersistent -> TComponent -> TControl -> TWinControl -> TCustomEdit -> TEdit

    TObject -> TPersistent -> TComponent -> TControl -> TWinControl -> TCustomEdit -> TCustomMemo -> TMemo

    以TEdit为例,来看一下它是如何获取控件中的字符串的,把这个过程(也就是VCL内部处理消息的过程)搞清楚了,就能够知道该设什么样的断点来监视这个获得字符串的过程,从而找到我们输入的注册码在内存中的存放位置,进而可以通过BPM/BPR断点来跟踪对注册码的处理过程,

断点为何失效的简单分析

    TEdit用来存放字符串的属性是Text,该属性继承自TControl。读取该属性的函数是私有函数GetText( )。

    (以下代码来自VCL的源码stdctrls.pas、control.pas、system.pas、sysutils.pas)

    //----------------------------------------------------------------------------------------------------

    TWndMethod = procedure(var Message: TMessage) of object;

    TControl = class(TComponent)

    private

    FWindowProc: TWndMethod;

    FText: PChar;

    function GetText: TCaption;

    procedure SetText(const Value: TCaption);

    ......

    protected

    property Text: TCaption read GetText write SetText;

    procedure WndProc(var Message: TMessage); virtual;

    ......

    public

    function GetTextBuf(Buffer: PChar; BufSize: Integer): Integer;

    function GetTextLen: Integer;

    procedure SetTextBuf(Buffer: PChar);

    function Perform(Msg: Cardinal; WParam LParam: Longint): Longint;

    property WindowProc: TWndMethod read FWindowProc write FWindowProc;

    ......

    end;

    //----------------------------------------------------------------------------------------------------

    GetText( )函数及相关函数的定义如下。可见,TControl最终是以WM_GETTEXTLENGTH和WM_GETTEXT消息为参数直接调WndProc( )的。

    //----------------------------------------------------------------------------------------------------

    function TControl.GetText: TCaption;

    var

    Len: Integer;

    begin

    Len := GetTextLen;

    SetString(Result PChar(nil) Len);

    if Len <> 0 then GetTextBuf(Pointer(Result) Len + 1);

    end;

    function TControl.GetTextLen: Integer;

    begin

    Result := Perform(WM_GETTEXTLENGTH 0 0);

    end;

    function TControl.GetTextBuf(Buffer: PChar; BufSize: Integer): Integer;

    begin

    Result := Perform(WM_GETTEXT BufSize Longint(Buffer));

    end;

    procedure TControl.SetTextBuf(Buffer: PChar);

    begin

    Perform(WM_SETTEXT 0 Longint(Buffer));

    Perform(CM_TEXTCHANGED 0 0);

    end;

    procedure TControl.SetText(const Value: TCaption);

    begin

    if GetText <> Value then SetTextBuf(PChar(Value));

    end;

    function TControl.Perform(Msg: Cardinal; WParam LParam: Longint): Longint;

    var

    Message: TMessage;

    begin

    Message.Msg := Msg;

    Message.WParam := WParam;

    Message.LParam := LParam;

    Message.Result := 0;

    if Self <> nil then WindowProc(Message);

    Result := Message.Result;

    end;

    constructor TControl.Create(AOwner: TComponent);

    begin

    inherited Create(AOwner);

    FWindowProc := WndProc;

    ......

    end;

    //----------------------------------------------------------------------------------------------------

    WndProc( )是个virtual函数。TEdit和TCustomEdit未重载这个函数,而TWinControl重载了这个函数,但它并未处理WM_GETTEXT消息,而是直接把该消息传递给了父类的函数TControl.WndProc( )。

    //----------------------------------------------------------------------------------------------------

    procedure TWinControl.WndProc(var Message: TMessage);

    var

    Form. TCustomForm;

    KeyState: TKeyboardState;

    WheelMsg: TCMMouseWheel;

    begin

    ......

    inherited WndProc(Message);

    end;

    //----------------------------------------------------------------------------------------------------

    TControl.WndProc( )直接用Dispatch( )来分发该消息,

电脑资料

断点为何失效的简单分析》(https://www.unjs.com)。

    //----------------------------------------------------------------------------------------------------

    procedure TControl.WndProc(var Message: TMessage);

    var

    Form. TCustomForm;

    begin

    ......

    Dispatch(Message);

    end;

    //----------------------------------------------------------------------------------------------------------

    由于WM_GETTEXT在TEdit中没有对应的专门的处理函数,所以Dispatch( )最终会调用TEdit.DefaultHandler( ),这个方法是从TCustomEdit继承来的,如此层层向上往父类分发,直到TControl.DefaultHandler( )对WM_GETTEXT消息进行了处理,它只是简单地把TControl的私有变量FText拷贝到软件提供的缓冲区中,所用到的函数只有StrLen( )、StrLCopy( )。

    //----------------------------------------------------------------------------------------------------------

    procedure TControl.DefaultHandler(var Message);

    var

    P: PChar;

    begin

    with TMessage(Message) do

    case Msg of

    WM_GETTEXT:

    begin

    if FText <> nil then P := FText else P := '';

    Result := StrLen(StrLCopy(PChar(LParam) P WParam - 1));

    end;

    WM_GETTEXTLENGTH:

    if FText = nil then Result := 0 else Result := StrLen(FText);

    WM_SETTEXT:

    begin

    P := StrNew(PChar(LParam));

    StrDispose(FText);

    FText := P;

    SendDockNotification(Msg WParam LParam);

    end;

    end;

    end;

    //----------------------------------------------------------------------------------------------------------

    看一下sysutils.pas中的StrLen( )、StrLCopy( )函数的实现,是用汇编写的,没有调用任何API函数。这就说明从进入TControl.GetText( )开始,一直到WM_GETTEXT成功返回为止,没有调用过任何Win32 API函数。所以常用的GetDlgItemTextA、GetWindowTextA断点不生效是当然的。

    //----------------------------------------------------------------------------------------------------------

    function StrLen(const Str: PChar): Cardinal; assembler;

    asm

    MOV EDXEDI

    MOV EDIEAX

    MOV ECX0FFFFFFFFH

    XOR ALAL

    REPNE SCASB

    MOV EAX0FFFFFFFEH

    SUB EAXECX

    MOV EDIEDX

    end;

    function StrLCopy(Dest: PChar; const Source: PChar; MaxLen: Cardinal): PChar; assembler;

    asm

    PUSH EDI

    PUSH ESI

    PUSH EBX

    MOV ESIEAX

    MOV EDIEDX

    MOV EBXECX

    XOR ALAL

    TEST ECXECX

    JZ @@1

    REPNE SCASB

    JNE @@1

    INC ECX

    @@1: SUB EBXECX

    MOV EDIESI

    MOV ESIEDX

    MOV EDXEDI

    MOV ECXEBX

    SHR ECX2

    REP MOVSD

    MOV ECXEBX

    AND ECX3

    REP MOVSB

    STOSB

    MOV EAXEDX

    POP EBX

    POP ESI

    POP EDI

    end;

    //-------------------------------------------------------------------------------------------------------

    那么如何才能在VCL将用户输入的字符串拷贝到软件的缓冲区中时使SoftICE弹出来呢?一种办法是先在软件的内存中搜索StrLCopy( )这个函数的机器码(即其Signature),找到之后在其入口处设个断点即可。实际上,你也可以在TControl.DefaultHandler( )、TControl.GetText( )等函数的入口处设断点,只要VCL在获取TEdit.Text的过程中调用了该函数,当然调用的次数越少越好。

    另外一种方法就是跟踪FText何时被赋值,实际上就在上面的WM_SETTEXT处。

    blowfish

    --------------------------------------------------------------------------------

最新文章