您现在的位置: 捷凌网安 >> 编程语言 >> Delphi编程 >> 正文
Delphi线程插入

作者:佚名 责任编辑:左决 点击数: 更新时间:2008-2-16 5:46: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技术等,有兴趣的朋友可以自己试着扩展.
  • 上一篇文章:

  • 下一篇文章:
  •  
    最进更新
    普通文章VC++设计超强仿QQ自动伸缩窗04-17
    推荐文章基于HOOK和MMF的Win密码渗透04-17
    推荐文章几种VC++数据库开发技术的相04-17
    普通文章多线程、Socket技术及委托技04-11
    推荐文章VB.Net连接各种数据库的几种04-11
    普通文章VB.NET中的多窗体编程:升级04-11
    普通文章用VB.NET定制Windows控件04-11
    普通文章VB.NET中监视文件夹的变化04-11
    普通文章VB.NET中对象的克隆04-11
    推荐文章VB.NET中的TextBox控件详解04-11
     
    推荐文章
    推荐文章基于HOOK和MMF的Win密码渗透04-17
    推荐文章几种VC++数据库开发技术的相04-17
    推荐文章VB.Net连接各种数据库的几种04-11
    推荐文章VB.NET中的TextBox控件详解04-11
    推荐文章在VB.NET中进行抓屏04-11
    推荐文章VB.Net开发的长内容自动分页04-11
    推荐文章VB.NET中快速访问注册表技巧04-11
    推荐文章PHP5手动最简安装方法03-07
    推荐文章完全讲解PHP+MySQL的分页显示03-07
    推荐文章Linux Shell元字符知识笔记02-21
     
    热点文章 
    普通文章VC++设计超强仿QQ自动伸缩窗04-17
    推荐文章基于HOOK和MMF的Win密码渗透04-17
    推荐文章几种VC++数据库开发技术的相04-17
    普通文章VB.NET中的多窗体编程:升级04-11
    普通文章用VB.NET定制Windows控件04-11
    普通文章VB.NET中对象的克隆04-11
    推荐文章VB.NET中的TextBox控件详解04-11
    普通文章VB/VB.NET/C#导出到Excel的方04-11
    普通文章如何通过VB.NET获取网卡地址04-11
    普通文章VB.NET中使用ListView控件的04-11

    | 设为首页 | 加入收藏 | 联系站长 | 广告服务 | 友情链接 | 版权申明 | 网站地图 |

    在线交流 捷凌网安主群:51649627
    Copyright 2007-2008 © 捷凌网安. All rights reserved.
    备案序号:蜀ICP备08001812号