从外部应用程序将数据写入Delphi TStringGrid

时间:2012-09-17 07:22:02

标签: delphi delphi-2009 dll-injection tstringgrid

我有一个用Delphi编写的遗留应用程序,需要为

构建一个机制
  1. 阅读和
  2. 写作
  3. 来自/到TStringGrid的数据。

    我没有应用程序的源代码,没有自动化界面,供应商不太可能提供。

    因此我创造了

    1. 一个C ++ DLL,它注入了
    2. 一个Delphi DLL(由我编写)到
    3. 旧版应用程序的地址空间。
    4. DLL 2可以访问遗留应用程序中的TStringGrid实例,读取单元格值并将它们写入调试日志。

      阅读工作正常。但是,当我尝试使用类似

      的调用将数据写入网格单元格时
      realGrid.Cells[1,1] := 'Test';
      

      发生访问冲突。

      以下是代码:

      procedure DllMain(reason: integer) ;
      type
        PForm = ^TForm;
        PClass = ^TClass;
        PStringGrid = ^TStringGrid;
      var
      [...]
      begin
        if reason = DLL_PROCESS_ATTACH then
        begin
          handle := FindWindow('TForm1', 'FORMSSSSS');
      
          formPtr := PForm(GetVCLObjectAddr(handle) + 4);
      
          if (not Assigned(formPtr)) then
          begin
            OutputDebugString(PChar('Not assigned'));
            Exit;
          end;
      
          form := formPtr^;
      
          // Find the grid component and assign it to variable realGrid
          [...]
      
          // Iterate over all cells of the grid and write their values into the debug log
          for I := 0 to realGrid.RowCount - 1 do
            begin
              for J := 0 to realGrid.ColCount - 1 do
                begin
                  OutputDebugString(PChar('Grid[' + IntToStr(I) + '][' + IntToStr(J) + ']=' + realGrid.Cells[J,I]));
                  // This works fine
                end;
            end;
      
          // Now we'll try to write data into the grid
          realGrid.Cells[1,1] := 'Test'; // Crash - access violation
        end;
      end; (*DllMain*)
      

      如何在不出现访问冲突问题的情况下将数据写入TStringGrid?

2 个答案:

答案 0 :(得分:1)

这种方法根本不起作用。目标可执行文件中有两个VCL实例。一个由目标应用程序拥有,另一个由DLL拥有。这是一个VCL实例太多了。如果使用完全相同版本的Delphi来构建目标应用程序和DLL,那么你可能会侥幸成功。

但是,您仍然可以使用两个堆管理器。并且您的代码在不同的VCL实例之间传递堆分配的内存。您将在一个堆中分配并在另一个堆中解除分配。这不起作用,将导致访问冲突。

您正在将DLL堆中分配的字符串传递给使用目标应用程序堆的字符串网格对象。那是行不通的。

我认为访问冲突将发生在DLL代码尝试释放由目标应用程序的堆管理器分配的先前Cells[i,j]值的位置。

基本上你正在尝试的东西不会起作用。您可以找到目标应用程序TStringGrid.SetCell的实现的地址,并伪造一个调用。但是你还需要找到目标应用程序GetMemFreeMem等的实现,并确保从目标应用程序分配和释放从DLL到目标应用程序的所有动态内存。堆。你将有一个工作的魔鬼使这项工作。当然,如果目标应用程序和DLL都使用了共享内存管理器,那么您可能只能使这种方法飞行。

更简单的是伪造键盘输入。我个人会使用AutoHotKey来确定其可行性。

答案 1 :(得分:1)

与堆使用相关的一切都存在非常大的风险。 您可以尝试使用Jedi CodeLib合并对象树并确保EXE和DLL中的相同单个堆,但这将是完全脆弱的解决方案。

希望VMT调用或多或少是安全的,并且试图阻止编译器释放字符串,这样草图就是这样:

type TSGCracker = class(Grids.TStringGrid); // access to protected functions.
....
var s: string;
function dummy(s: string);  // not-inline! pretend we need and use the value!
   begin Result := s + '2'; end; 
begin
   ...
   s := 'Test';   
   TSGCracker(realGrid).SetEditText(1,1, s);
   dummy( s+'1');
   ...
end;

但是如果主机EXE使用它,那可能会调用TStringGrid.OnSetEditText。