分享
 
 
 

在其它进程中建立线程

王朝delphi·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

此方法不适用于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技术等,有兴趣的朋友可以自己试着扩展.

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有