在其它进程中建立线程

原创 2003年09月17日 09:02:00

  此方法不适用于9x系统
  我们知道在NT及以上操作系统提供了一个函数VirtualAllocEx,利用这个函数我们可以在其它进程中申请一块内存,其定义如下
  function VirtualAllocEx(hProcess: THandle; lpAddress: Pointer; dwSize, flAllocationType: DWORD; flProtect: DWORD): Pointer; stdcall;
  其中hProcess为要申请内存的进程的句柄,可以用如下方法得到指定的窗口所属的进程的进程句柄.
Function GetProcessHandle: THandle;
var
  WndHandle, PID: THandle;
begin
  WndHandle := FindWindow(nil, '窗口名');
  {得到其进程和线程ID}
  GetWindowThreadProcessId(WndHandle, PID);
  {以完全访问权限打开进程句柄}
  Result := OpenProcess(PROCESS_ALL_ACCESS, False, PID);
end;

  lPAddress为地址指针,指向需要分配的某地址范围内的页面的起始地址,可以设为nil,由系统确定分配空间的地址.dwSize为分配内存区域的大小.flAllocationType为分配类型,在这儿我们设为MEM_COMMIT.flProtect为新分配内存的存取保护类型,可设为PAGE_EXECUTE_READWRITE来定义其为可执行可读写.
  函数执行成功后,将会返回所分配页面的基址.

  在成功申请内存后,我们就可以用WriteProcessMemory函数来把自己进程中的线程函数的代码写入到目标进程中了,然后再调用CreateRemoteThread函数来建立远程线程.其定义和参数类型类似于CreateThread.

  现在看来似乎就一切OK了,其实还有一个麻烦的问题,如果在远程线程中调用了API函数,就会出现调用错误,因为在调用API时,编译器并不生成直接调用API的指令,而是在进程装入时在调用地址中写入对应API的地址,CALL指令再根据这个地址调用真正的API函数,但是每个进程中放有的相应API地址并不相同,因此我们要自己找出API的真实地址(用LoadLibrary和GetProcAddress),再写到目标进程中就可以了.然而这并不是很容易的事,因为在线程函数中新定义了变量的话,都要重定位变量对于函数基址的位移,十分麻烦.在逃了二节课来研究了DELPHI的CPU窗口后,我终于找到了一种易于扩展的方法.就是利用结构变量.

  先看一下下面的例子吧.
unit unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

const
  WM_HOOKED = WM_USER + 3221;  {Hook安装成功的消息}

type
  TThreadProVarList = record  {变量列表}
    SendMessage: DWORD;
    ExitProcess: DWORD;
    ExitThread: DWORD;        {上面用来保存API真实地址}
    WndHandle: DWORD;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    {在目标进程中申请的内存地址}
    ThreadAdd: Pointer;
    PID, PHandle: DWORD;   {目标窗口进程ID,句柄和线程ID}
    ThreadHandle, ThreadID: Thandle;  {新的远程线程的ID和句柄}
    procedure WMHOOKED(var Msg: TMessage);message WM_HOOKED;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure ThreadPro;
var
  VarList: TThreadProVarList;
begin
  asm
    mov eax, $FFFFFFFF   {到$FFFFFFFF的偏移是7}
    mov VarList.SendMessage, eax
    mov eax, $FFFFFFFF   {这个$FFFFFFFF是在上一个偏移位置加8}
    mov VarList.WndHandle, eax
    mov eax, $FFFFFFFF
    mov VarList.ExitProcess, eax
    mov eax, $FFFFFFFF
    mov VarList.ExitThread, eax
    push 0
    push 0
    push 4245            {4245就是自定义的WM_HOOKED}
    push VarList.WndHandle
    call VarList.SendMessage
    push 0
    call VarList.ExitThread
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  {要注入线程的窗口句柄和临时存放的句柄}
  WndHandle, TmpHandle: THandle;
  DllModule, SendPro, WriteCount: DWORD;
  ExitPro, ExitTPro: DWORD;
begin
  {先查找到要注入远程线程的窗口}
  WndHandle := FindWindow(nil, '记事本');
  {得到其进程和线程ID}
  GetWindowThreadProcessId(WndHandle, PID);
  {以完全访问权限打开进程句柄}
  PHandle := OpenProcess(PROCESS_ALL_ACCESS, False, PID);
  {在目标进程中分配内存}
  ThreadAdd := VirtualAllocEx(PHandle, nil, 4096, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  {把自定义函数写入到目标进程中}
  WriteProcessMemory(PHandle, ThreadAdd, @ThreadPro, 4096, WriteCount);
  {以挂起方式建立远端线程,以便修改}
  ThreadHandle := CreateRemoteThread(PHandle, nil, 0, ThreadAdd, nil, CREATE_SUSPENDED, ThreadID);
  {得到API真实的地址}
  DllModule := LoadLibrary('User32.dll');
  SendPro := DWORD(GetProcAddress(DllModule, 'SendMessageW'));
  DllModule := LoadLibrary('Kernel32.dll');
  ExitPro := DWORD(GetProcAddress(DllModule, 'ExitProcess'));
  ExitTPro := DWORD(GetProcAddress(DllModule, 'ExitThread'));
  {把API真实地址和数据写入到在目标进程中的函数中}
    TmpHandle := Self.Handle;
    WriteProcessMemory(PHandle, Pointer(LongInt(ThreadAdd)+7), @SendPro, SizeOf(DWORD), WriteCount);
    WriteProcessMemory(PHandle, Pointer(LongInt(ThreadAdd)+15), @TmpHandle, SizeOf(DWORD), WriteCount);
    WriteProcessMemory(PHandle, Pointer(LongInt(ThreadAdd)+23), @ExitPro, SizeOf(DWORD), WriteCount);
    WriteProcessMemory(PHandle, Pointer(LongInt(ThreadAdd)+31), @ExitTPro, SizeOf(DWORD), WriteCount);
  {开始运行远端线程}
  ResumeThread(ThreadHandle);
  CloseHandle(ThreadHandle);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  {释放在目标进程中分配的内存}
  VirtualFreeEx(PHandle, ThreadAdd, 4096, MEM_DECOMMIT);
  {关闭不用的句柄}
  CloseHandle(PHandle);
end;

procedure TForm1.WMHOOKED(var Msg: TMessage);
begin
  MessageBox(self.Handle, '建立远端线程成功', '!!!', MB_OK);
end;

end.


  要在线程函数中新定义变量的话,在TThreadProVarList类型中添加就可以了,然后再添加一条类似于
    mov eax, $FFFFFFFF
    mov VarList.ExitProcess, eax
的指今,并用WriteProcessMemory写入新变量的值就可以了.如果是在var中申请变量的话,到函数第一条指今的偏移地址会改变,源程序也要相应改变,可以利用CPU窗口来查看.大家如果有更好的变量传递的方法也请告诉我。
  注意,在线程函数中调用VCL函数也会有问题,因为指向的是自己的进程中的函数地址.如果使用Pchar类型的字串的话,必须先用VirtualAllocEx函数申请内存,再用WriteProcessMemory写字串到目标进程中并保存下来字串地址,再按传送API地址的方法传送给线程函数就可以使用了.
  最后记得使用VirtualFreeEx函数来释放在目标进程中分配的内存.

  利用VirtualAllocEx函数还可以实现不需要DLL文件的HOOK技术等,有兴趣的朋友可以自己试着扩展.

【C#】进程中最大线程创建数量分析

【C#】进程中最大线程创建数量分析作者:凡星 QQ:184167125 一、 简介 多线程在当今软件开发中应用非常广泛,在实际开发过程中,发现很多不正确使用线程的现象,其中一点就是线程使用过于...
  • easydw
  • easydw
  • 2016年01月04日 13:44
  • 3272

遍历创建进程、创建线程、加载模块的回调函数

今天我们首先来看一下最简单的,关于遍历PspCreateProcessNotifyRoutine数组,PspLoadImageNotifyRoutine也同理 这两个数组保存了两组函数地址,它们...
  • kingswb
  • kingswb
  • 2016年05月18日 21:58
  • 1016

在其它进程中建立线程

转自 剑神一笑 的Blog http://blog.csdn.net/pankun/此方法不适用于9x系统  我们知道在NT及以上操作系统提供了一个函数VirtualAllocEx,利用这个函数我们可...
  • passos
  • passos
  • 2005年04月18日 01:04
  • 959

进程互斥锁

进程间共享数据的保护,需要进程互斥锁。与线程锁不同,进程锁并没有直接的C库支持,但是在Linux平台,要实现进程之间互斥锁,方法有很多,大家不妨回忆一下你所了解的。下面就是标准C库提供的一系列方案。 ...
  • educast
  • educast
  • 2013年11月12日 17:15
  • 2999

关于MFC DLL CWinApp::InitInstance()中创建线程的问题 易造成死锁

1、创建线程会调用应用程序已经加载的所有DLL的DLLMain(参见DLLMain的文档中关于reason参数的说明),而DLLMain是不可以重入的。 2、.exe加载Dll的方法是先调用每一个...
  • tianjian789
  • tianjian789
  • 2016年05月20日 10:12
  • 1515

线程

在传统操作系统中,每个进程有一个地址空间和一个控制线程,这是进程的定义,经常存在在一个地址空间中准并行运行多个控制线程的情形,这些线程像分离的进程。...
  • qq_33102061
  • qq_33102061
  • 2017年01月09日 16:22
  • 285

进程、线程 及其在 linux中的实现

1       进程,线程是怎么来的? 抛开技术细节,从使用角度来讲: 1.     在单核计算机里,有一个资源是无法被多个程序并行使用的: cpu Cpu,承担计算任务,单个cpu一次只能运行一个任...
  • u013933870
  • u013933870
  • 2016年06月16日 17:31
  • 1423

第一次作业(1.线程和进程 )

线程和进程定义和区别             进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。它可以申请和拥有系统资源,是一个动态的概念。它不只是程序的代码,还包括当前的活动,通过程序计...
  • heizi93
  • heizi93
  • 2016年04月17日 01:34
  • 382

进程、线程、作业、程序

进程与线程的区别与联系 1.定义 进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位. 线程是进程的一个实体,是CPU调度和...
  • CareChere
  • CareChere
  • 2016年07月04日 10:23
  • 820

进程与线程

一 、进程概念在计算机上所有可以运行的软件,通常也包括操作系统,被组织成若干顺序进程,一个进程就是一个正在执行程序的实例,包括内存地址空间,程序技术器、寄存器和变量的当前值。1.1 进程的创建:有四种...
  • jingdong19911120
  • jingdong19911120
  • 2016年07月14日 11:28
  • 766
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:在其它进程中建立线程
举报原因:
原因补充:

(最多只允许输入30个字)