zoukankan      html  css  js  c++  java
  • 线程插入

    此方法不适用于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技术等,有兴趣的朋友可以自己试着扩展.
  • 相关阅读:
    我要好offer之 二叉树大总结
    我要好offer之 字符串相关大总结
    楼层扔鸡蛋问题[转]
    Linux System Programming 学习笔记(十一) 时间
    Linux System Programming 学习笔记(十) 信号
    Linux System Programming 学习笔记(九) 内存管理
    Linux System Programming 学习笔记(八) 文件和目录管理
    Linux System Programming 学习笔记(七) 线程
    Linux System Programming 学习笔记(六) 进程调度
    APUE 学习笔记(十一) 网络IPC:套接字
  • 原文地址:https://www.cnblogs.com/94YY/p/2043509.html
Copyright © 2011-2022 走看看