CodeInjection使用CreateRemoteThread和WriteProcessMemory

时间:2015-07-24 08:32:35

标签: delphi winapi

在CodeProject(Link)上,我读到了使用CreateRemoteThreadWriteProcessMemory在另一个进程中注入代码。

  
      
  1. 将HANDLE检索到远程进程(OpenProces)。
  2.   
  3. 在远程进程的地址空间中为注入数据分配内存(VirtualAllocEx)。
  4.   
  5. 将初始化的INJDATA结构的副本写入分配的内存(WriteProcessMemory)。
  6.   
  7. 在远程进程的地址空间中为注入的代码分配内存。
  8.   
  9. 将ThreadFunc的副本写入分配的内存。
  10.   
  11. 通过CreateRemoteThread启动ThreadFunc的远程副本。
  12.   
  13. 等到远程线程终止(WaitForSingleObject)。
  14.   
  15. 从远程进程(ReadProcessMemory或GetExitCodeThread)中检索结果。
  16.   
  17. 释放步骤#2和#4(VirtualFreeEx)中分配的内存。   块引用
  18.   
  19. 关闭步骤#6和#1(CloseHandle)中检索到的句柄。
  20.   

由于INJDATA是一个结构,我假设任何类型的参数/变量/记录都可以。

它写得非常好但是我很难将其转换为Delphi。

所以我想在notepad.exe中注入我的程序GetNumber,该程序显示我可以在记录中声明的数字。

type
  TInjectData = record
    FNumber: Integer;
  end; 


procedure GetNumber(var pData: TInjectData);
begin
  MessageBoxA(0, PChar(IntToStr(pData.FNumber)), '', 0);
end;      

我写了一个应该完成工作的函数InjectCode

  procedure InjectCode;
var
  hWindow: THandle;
  dwProcId: Cardinal;
  hProcess: THandle;
  hThread: THandle;
  myData: TInjectData;
  pDataRemote, pCodeRemote: Pointer;
  dwBytesWritten: DWORD;
  cbCodeSize: Integer;
  dwThreadId: DWORD;

begin
  myData.FNumber := 42;

  hWindow := FindWindow('Notepad', nil);
  ShowMessage(Format('hWindow: %d', [hWindow]));

  GetWindowThreadProcessId(hWindow, dwProcId);
  ShowMessage(Format('dwProcId: %d', [dwProcId]));

  // 1. Retrieve a HANDLE to the remote process (OpenProces)
  hProcess := OpenProcess(PROCESS_CREATE_THREAD or PROCESS_QUERY_INFORMATION or
    PROCESS_VM_OPERATION or PROCESS_VM_WRITE, False, dwProcId);

  ShowMessage(Format('hProcess: %d', [hProcess]));


  // 2. Allocate memory in the remote process's address space for injected data (VirtualAllocEx)
  // pDataRemote = the address (in the remote process) where myData will be copied to
  pDataRemote := VirtualAllocEx(hProcess, nil, SizeOf(myData), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  ShowMessage(Format('pDataRemote: %d', [Integer(pDataRemote)]));


  // 3. Write a copy of the initialised INJDATA structure to the allocated memory (WriteProcessMemory)
  WriteProcessMemory(hProcess, pDataRemote, @myData, SizeOf(myData), dwBytesWritten);
  ShowMessage(Format('dwBytesWritten pDataRemote: %d', [dwBytesWritten]));


  // Calculate the number of bytes that ThreadFunc occupies
  // cbCodeSize = LPBYTE(AfterGetNumber) - LPBYTE(GetNumber);
  cbCodeSize := 2500; // correct calculation not yet implemented


  // 4. Allocate memory in the remote process's address space for injected code
  pCodeRemote := VirtualAllocEx(hProcess, nil, cbCodeSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  ShowMessage(Format('pCodeRemote: %d', [Integer(pCodeRemote)]));


  // 5. Write a copy of GetNumber to the allocated memory
  WriteProcessMemory(hProcess, pCodeRemote, @GetNumber, cbCodeSize, dwBytesWritten);
  ShowMessage(Format('dwBytesWritten pCodeRemote: %d', [dwBytesWritten]));


  // 6. Start the remote copy of GetNumber via CreateRemoteThread
  hThread := CreateRemoteThread(hProcess, nil, 0, pCodeRemote, pDataRemote, 0, dwThreadId);
  ShowMessage(Format('hThread: %d', [hThread]));
  ShowMessage(Format('dwThreadId: %d', [dwThreadId]));



  // 7. Wait until the remote thread terminates (WaitForSingleObject)
  WaitForSingleObject(hThread, INFINITE);


  // 8. Retrieve the result from the remote process (ReadProcessMemory or GetExitCodeThread).
  // not needed in this example


  // 9. Free the memory allocated in Steps #2 and #4 (VirtualFreeEx)
  if Assigned(pDataRemote) then
    VirtualFreeEx(hProcess, pDataRemote, 0, MEM_RELEASE );

  if Assigned(pCodeRemote) then
    VirtualFreeEx(hProcess, pCodeRemote, 0, MEM_RELEASE );


  // 10. Close the handles retrieved in Steps #6 and #1 (CloseHandle).
  if hProcess <> 0 then
    CloseHandle(hProcess);

  if hThread <> 0 then
    CloseHandle(hThread);
end;  

但每当我调用该过程时,都不会出现MessageBox。 我没有代码注入的经验,所以请耐心等待,因为我已经花了几天时间试图解决这个问题。

感谢任何帮助。

2 个答案:

答案 0 :(得分:3)

您尚未执行任何错误检查。所以你不知道代码是如何失败的。你需要养成检查错误的习惯。

您还应该知道,在注入远程线程时,调用约定很重要。您必须提供具有此签名的线程函数:

IntToStr

另一个问题是目标进程是记事本,肯定是64位进程。你无法注入。来自32位进程的64位进程。当然不是32位代码!

但是,一旦修复了所有错误,您的代码仍然无法正常工作。那是因为你注入的函数依赖于存在的Delphi RTL。它链接到Windows API函数。它调用PChar()。它执行public void Print() { Database db = new Database(); ResultSet rs = null; rs = db.getData("SELECT * FROM my_table" + "INTO OUTFILE 'E:\\kurtcobain.csv'" + "FIELDS TERMINATED BY ','" + "ENCLOSED BY '"'" + "LINES TERMINATED BY '+\n+'")";演员表演。所有这些都需要Delphi RTL。更重要的是,它们需要RTL驻留在它在注入器中的地址,因为它是静态链接的。

要以这种方式注入代码,代码必须独立存在。如果你想了解注射,你应该首先注入一个更容易的DLL。

答案 1 :(得分:0)

目标进程不需要具有相同的kernel32.MessageBoxA地址,可能就是这样。在使用之前我会在notepad.exe中获取地址。