关于跨进程使用回调函数的研究:以跨进程获取Richedit中RTF流为例。

本文探讨了如何在不同进程中使用回调函数,以实现跨进程获取Richedit控件中的RTF流。文章建议读者首先阅读作者之前关于跨进程获取Richedit中Text的博文,作为理解的基础。
摘要由CSDN通过智能技术生成

建议先参考我上次写的博文跨进程获取Richedit中Text:原创 获得QQ聊天输入框中的内容

    拿到这个问题,我习惯性地会从VCL内核开始分析。找到TRichEdit声明的单元,分析TRichEdit保存为RTF流的代码。(分析VCL内核代码方便了解Windows标准API的封装和使用)     打开声明TRichEdit的ComCtrls.pas单元。搜索"TRichEditStrings"(保存流使用TRichEdit.Lines.SaveToStream方法,TRichEditStrings为TRichEdit.Line的类型)   TRichEditStrings = class(TStrings)
  private
    RichEdit: TCustomRichEdit;
    FPlainText: Boolean;
    FConverter: TConversion;
    procedure EnableChange(const Value: Boolean);
  protected
    function Get(Index: Integer): string; override;
    function GetCount: Integer; override;
    procedure Put(Index: Integer; const S: string); override;
    procedure SetUpdateState(Updating: Boolean); override;
    procedure SetTextStr(const Value: string); override;
  public
    destructor Destroy; override;
    procedure Clear; override;
    procedure AddStrings(Strings: TStrings); override;
    procedure Delete(Index: Integer); override;
    procedure Insert(Index: Integer; const S: string); override;
    procedure LoadFromFile(const FileName: string); override;
    procedure LoadFromStream(Stream: TStream); override;
    procedure SaveToFile(const FileName: string); override;
    procedure SaveToStream(Stream: TStream); override;
    property PlainText: Boolean read FPlainText write FPlainText;
  end; 寻找到SaveToStream的方法  procedure TRichEditStrings.SaveToStream(Stream: TStream); var    EditStream: TEditStream;
   TextType: Longint;    StreamInfo: TRichEditStreamInfo;    Converter: TConversion; begin    if FConverter <> nil then Converter := FConverter    else Converter := RichEdit.DefaultConverter.Create;    StreamInfo.Stream := Stream;    StreamInfo.Converter := Converter;    try      with EditStream do      begin        dwCookie := LongInt(Pointer(@StreamInfo));        pfnCallBack := @StreamSave;        dwError := 0;      end;      if PlainText then TextType := SF_TEXT      else TextType := SF_RTF;      SendMessage(RichEdit.Handle, EM_STREAMOUT, TextType, Longint(@EditStream));      if EditStream.dwError <> 0 then        raise EOutOfResources.Create(sRichEditSaveFail);    finally      if FConverter = nil then Converter.Free;    end; end;
看关键的一句:“ SendMessage(RichEdit.Handle, EM_STREAMOUT, TextType, Longint(@EditStream));” 这下明白了,获取RTF关键的是向Richedit发送EM_STREAMOUT消息。   关于EM_STREAMOUT消息想了解更多可以查阅MSDN:   EM_STREAMOUT      wParam = (WPARAM) (UINT) uFormat ;      lParam = (LPARAM) (EDITSTREAM FAR *) lpStream ; 进程间的内存地址是相对的。 A进程$00450000内存地址值为34,那么B进程$00450000内存地址就不一定是34了。  在发送EM_STREAMOUT消息时,lParam参数表示的地址就是相对于目标进程的。  跨进程访问内存主要用到如下API函数:  GetWindowThreadProcessId -- 根据窗体句柄获得其所在的线程、进程ID OpenProcess -- 打开进程并返回访问句柄 VirtualAllocEx -- 分配进程虚拟内存空间,返回所分配的内存地址。 VirtualFreeEx -- 释放进程虚拟内存空间 ReadProcessMemory、WriteProcessMemory -- 读写进程内存数据。 和以往不一样,这个消息用到了回调函数“ pfnCallBack := @StreamSave;” 函数也是存放在内存中的数据(一些机器指令),访问函数同样会碰到进程间不能直接访问内存的问题。 也就是说:需要将函数数据写入到目标进程中,才能被正常调用。  【如何获得函数的数据?】
function MyFunction(A, B: Integer): Integer;
begin
  Result := A + B;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Edit1.Text := IntToStr(Integer(@MyFunction));
end; 函数地址容易得到。调试如上代码,点击按钮获得函数地址,打开CPU查看器(Ctrl+Alt+C),定位函数地址。 这样将看到如图: 03C2 --------add eax,edx
7105 --------jon +$05
E81f43FBFF --call @IntOver
C3 --------- ret 前面就是十六进制数据,后面就是该数据表示的机器指令。 这些就是函数数据,将它写入到目标进程就可以调用了!!!
两个进程载入相同的DLL那么DLL的函数地址则是相同的,也就是说API函数SendMessage在A、B两个进程的地址一致。有了这点,利用系统API函数SendMessage发送WM_COPYDATA消息就可以交互数据了。 当然,如果指令里有相对地址的访问也得克隆才成,比如上面的" E81f43FBFF --call @IntOver "就用到了相对地址。囧  可以将编译条件中“溢出、范围、IO"检查都关掉,减少相对地址的访问。 跨进程之前,先在本进程实验通过再说:  【第一个实验:正常调用回调函数】  function MySendMessage(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
  Result := SendMessage(hWnd, Msg, wParam, lParam);
end;

type
  TMySendMessage = function (hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 18
    评论
//官方网站:www.feiyuol.com //郁金香灬老师 //QQ 150330575 //个人网站:www.yjxsoft.com 进程调用CALL 进程调用带多个的参数CALL // myInject_dll.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include #include"RWA.h" //PVOID 进程分配内存(WORD nSize ); //1、获取进程句柄 //2、读写 分配内存 创建线程 //3、进程分配内存 void mycall() { PVOID p1=进程分配内存(1000); printf("分配的内存地址=%p\n",p1); printf("按回车键 释放内存\n"); getchar(); 进程释放内存(p1,1000); printf("已经 释放内存\n"); } LPTHREAD_START_ROUTINE a; BOOL 进程调用CALL(PVOID pcall地址,PVOID plst参数 ); //LoadLibraryA(dll名字指针) //MessageBeep(1) void Test远程调用MessageBeep() { 进程调用CALL(MessageBeep,(PVOID)0x12768); } //注入my022MFC.dll到目标进程 void Test3() { char szDllName[]="my022MFC.dll"; //全路径 // char szDllName[]="C:\\Users\\yjxsoft\\Documents\\visual studio 2010\\Projects\\my022\\Debug\\my022MFC.dll"; PVOID p1=进程分配内存(1000); printf("分配的内存地址=%p\n",p1); WN((DWORD)p1,szDllName,sizeof(szDllName));//WriteProcessMemory /*进程调用CALL(LoadLibraryA,(PVOID)szDllName);*/ 进程调用CALL(LoadLibraryA,(PVOID)p1); } void Test4() { // char szDllName[]="my022MFC.dll"; //全路径 char szDllName[]="E:\\1905\\代码\\my022-24\\Debug\\my022MFC.dll"; PVOID p1=进程分配内存(1000); printf("分配的内存地址=%p\n",p1); WN((DWORD)p1,szDllName,sizeof(szDllName));//WriteProcessMemory /*进程调用CALL(LoadLibraryA,(PVOID)szDllName);*/ 进程调用CALL(LoadLibraryA,(PVOID)p1); } int _tmain(int argc, _TCHAR* argv[]) { //mycall(); Test3(); Test4(); return 0; } //作业 //1、练习进程注入DLL //2、进程分配的内存内存 使用完后 用VirtualFreeEx释放掉 //3、进程句柄使用完后用CloseHandle释放句柄资源

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值