CreateThread和CreateRemoteThread

CreateThread和CreateRemoteThreadCreateThread函数功能创建一个线程以在调用进程的虚拟地址空间内执行。函数原型HANDLECreateThread(LPSECURITY_ATTRIBUTESlpThreadAttributes,SIZE_TdwStackSize,LPTHREAD_START_ROUTINElpStartAddress,__drv_aliasesMemLPVOIDlpParameter,DWORD

大家好,欢迎来到IT知识分享网。

CreateThread

函数功能

创建一个线程以在调用进程的虚拟地址空间内执行。

函数原型

HANDLE CreateThread(
  LPSECURITY_ATTRIBUTES   lpThreadAttributes,
  SIZE_T                  dwStackSize,
  LPTHREAD_START_ROUTINE  lpStartAddress,
  __drv_aliasesMem LPVOID lpParameter,
  DWORD                   dwCreationFlags,
  LPDWORD                 lpThreadId
);

第一个参数:

指向SECURITY_ATTRIBUTES 结构的指针,该结构确定子进程是否可以继承返回的句柄。如果 lpThreadAttributes为NULL,则不能继承该句柄。

结构的lpSecurityDescriptor成员为新线程指定一个安全描述符。如果lpThreadAttributes为NULL,则线程获取默认的安全描述符。线程的默认安全描述符中的ACL来自创建者的主要令牌。

第二个参数:

堆栈的初始大小,以字节为单位。系统将此值舍入到最接近的页面。如果此参数为零,则新线程将使用可执行文件的默认大小。

第三个参数:

指向要由线程执行的应用程序定义的函数的指针。该指针表示线程的起始地址。

第四个参数:

指向要传递给线程的变量的指针。

第五个参数:

控制线程创建的标志。

含义
0 线程在创建后立即运行。
CREATE_SUSPENDED 0x00000004 该线程以挂起状态创建,并且直到调用ResumeThread函数后才运行
STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 所述dwStackSize参数指定堆栈的初始保留大小。如果未指定此标志,则dwStackSize指定提交大小。

第六个参数:

指向接收线程标识符的变量的指针。如果此参数为 NULL,则不返回线程标识符。

返回值:

如果函数成功,则返回值是新线程的句柄。

如果函数失败,则返回值为NULL。要获取扩展的错误信息,请调用 GetLastError。

请注意,即使lpStartAddress指向数据,代码或不可访问,CreateThread也可能成功 。如果线程运行时起始地址无效,则会发生异常,并终止线程。由于无效的起始地址而导致的线程终止将被视为线程进程的错误退出。此行为类似于CreateProcess的异步性质,在该过程中,即使进程引用无效或丢失的动态链接库(DLL),也会创建该进程。

补充:

进程可以创建的线程数受可用虚拟内存的限制。默认情况下,每个线程都有一兆的堆栈空间。因此,您最多可以创建2,048个线程。如果减小默认堆栈大小,则可以创建更多线程。但是,如果您为每个处理器创建一个线程并构建应用程序为其维护上下文信息的请求队列,则您的应用程序将具有更好的性能。在处理下一个队列中的请求之前,线程将处理队列中的所有请求。

使用THREAD_ALL_ACCESS访问权限创建新的线程句柄。如果在创建线程时未提供安全描述符,则使用创建线程的进程的主要令牌为新线程构造默认的安全描述符。当调用者尝试使用OpenThread函数访问线程时,将根据此安全描述符评估调用者的有效令牌,以授予或拒绝访问。

调用GetCurrentThread 函数时,新创建的线程对其具有完全访问权限。

Windows Server 2003: 通过针对线程构造的默认安全描述符评估线程创建过程的主令牌,从而计算出线程对其自身的访问权限。如果线程是在远程进程中创建的,则使用远程进程的主令牌。结果,新创建的线程在调用GetCurrentThread时可能会减少对其自身的访问权限。某些访问权限(包括THREAD_SET_THREAD_TOKEN和THREAD_GET_CONTEXT)可能不存在,从而导致意外故障。因此,不建议在模拟其他用户的同时创建线程。

如果线程是在可运行状态下创建的(即,如果未使用CREATE_SUSPENDED标志),则该线程可以在CreateThread返回之前(尤其是在调用者接收到所创建线程的句柄和标识符之前)开始运行。

线程执行从lpStartAddress参数指定的函数开始。如果此函数返回,则DWORD返回值用于在对ExitThread函数的隐式调用中终止线程 。使用 GetExitCodeThread函数获取线程的返回值。

创建线程的线程优先级为THREAD_PRIORITY_NORMAL。使用 GetThreadPriority和 SetThreadPriority函数来获取和设置线程的优先级值。

当线程终止时,线程对象将达到信号状态,从而满足在该对象上等待的所有线程。

线程对象将保留在系统中,直到线程终止并通过调用CloseHandle关闭了它的所有 句柄。

的 ExitProcess的, 了ExitThread, CreateThread的, CreateRemoteThread的功能,以及正在启动的处理(如通过一个调用的结果 的CreateProcess)的一个进程中彼此之间串行化。这些事件一次只能在一个地址空间中发生。这意味着以下限制成立:

  • 在进程启动和DLL初始化例程期间,可以创建新线程,但是直到对该进程完成DLL初始化之后,它们才开始执行。
  • 一个进程中只有一个线程可以同时处于DLL初始化或分离例程中。
  • 直到DLL初始化或分离例程中没有线程时,ExitProcess才会完成。

可执行文件中的调用C运行时库(CRT)的线程应将_beginthreadex和_endthreadex函数用于线程管理,而不是 CreateThread和 ExitThread;这需要使用CRT的多线程版本。如果使用CreateThread创建的线程调用CRT,则CRT可能会在内存不足的情况下终止进程。

Windows Phone 8.1: Windows Phone 8.1及更高版本上的Windows Phone Store应用程序支持此功能。

Windows 8.1和Windows Server 2012 R2:Windows 8.1,Windows Server 2012 R2和更高版本上的Windows Store应用程序支持此功能。

代码实现

#include <iostream>
#include<Windows.h>

void fun() { 
   
    for (int i = 0; i < 15;i++) { 
   
        printf("fun%d\n", i);
        
    }
}
DWORD WINAPI  ThreadProc(LPVOID lpParameter) { 
   
    fun();
    return 0;
}

int main()
{ 
   
    HANDLE handle = CreateThread(NULL, 0, ThreadProc,NULL,0,NULL);
    CloseHandle(handle);
    
    system(" pause");

}

在这里插入图片描述

注意:

千万不要忘了 system(" pause");,否则主线程return结束了 进程就结束了,进程结束,所有线程都没了,输出也就没了,如下图
在这里插入图片描述

CreateRemoteThread

函数功能

创建一个在另一个进程的虚拟地址空间中运行的线程。
使用CreateRemoteThreadEx函数可创建在另一个进程的虚拟地址空间中运行的线程,并可以选择指定扩展属性

函数原型:

HANDLE CreateRemoteThread(
  HANDLE                 hProcess,
  LPSECURITY_ATTRIBUTES  lpThreadAttributes,
  SIZE_T                 dwStackSize,
  LPTHREAD_START_ROUTINE lpStartAddress,
  LPVOID                 lpParameter,
  DWORD                  dwCreationFlags,
  LPDWORD                lpThreadId
);

第一个参数

要在其中创建线程的进程的句柄。该句柄必须具有PROCESS_CREATE_THREAD,PROCESS_QUERY_INFORMATION,PROCESS_VM_OPERATION,PROCESS_VM_WRITE和PROCESS_VM_READ访问权限,并且在某些平台上没有这些权限可能会失败

第二个参数

指向SECURITY_ATTRIBUTES结构的指针,该 结构为新线程指定安全描述符,并确定子进程是否可以继承返回的句柄。如果lpThreadAttributes为NULL,则线程获取默认的安全描述符,并且该句柄无法继承。线程的默认安全描述符中的访问控制列表(ACL)来自创建者的主要令牌。

Windows XP: 线程的默认安全描述符中的ACL来自创建者的主令牌或模拟令牌。对于带有SP2的Windows XP和Windows Server 2003,此行为已更改

第三个参数

堆栈的初始大小,以字节为单位。系统将此值舍入到最接近的页面。如果此参数为0(零),则新线程将使用可执行文件的默认大小

第四个参数

指向由线程执行的,类型为LPTHREAD_START_ROUTINE的应用程序定义的函数的指针,该指针表示远程进程中线程的起始地址。该功能必须存在于远程进程中

第五个参数

指向要传递给线程函数的变量的指针

第六个参数

控制线程创建的标志。

含义
0 线程在创建后立即运行。
CREATE_SUSPENDED 0x00000004 该线程以挂起状态创建,并且直到调用ResumeThread函数后才运行
STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 所述dwStackSize参数指定堆栈的初始保留大小。如果未指定此标志,则dwStackSize指定提交大小。

第七个参数

指向接收线程标识符的变量的指针。

如果此参数为NULL,则不返回线程标识符

返回值

如果函数成功,则返回值是新线程的句柄。

如果函数失败,则返回值为NULL。要获取扩展的错误信息,请调用 GetLastError。

请注意,即使lpStartAddress指向数据,代码或不可访问, CreateRemoteThread也可能成功。如果线程运行时起始地址无效,则会发生异常,并终止线程。由于无效的起始地址而导致的线程终止将被视为线程进程的错误退出。此行为类似于CreateProcess的异步性质 ,在该过程中,即使进程引用了无效或丢失的动态链接库(DLL),也会创建该进程

备注:

该 远程线程函数会导致一个新的执行线程指定进程的地址空间开始。线程有权访问该进程打开的所有对象。

终端服务通过设计隔离每个终端会话。因此,如果目标进程与调用进程不在同一个会话中,则 CreateRemoteThread将失败。

创建具有新线程完全访问权限的新线程句柄。如果未提供安全描述符,则该句柄可以在需要线程对象句柄的任何函数中使用。提供安全描述符后,将在授予访问权限之前对句柄的所有后续使用执行访问检查。如果访问检查拒绝访问,则请求进程无法使用该句柄获取对该线程的访问。

如果线程是在可运行状态下创建的(即,如果未使用CREATE_SUSPENDED标志),则该线程可以在CreateThread返回之前(尤其是在调用者接收到所创建线程的句柄和标识符之前)开始运行。

创建线程的线程优先级为THREAD_PRIORITY_NORMAL。使用 GetThreadPriority和 SetThreadPriority函数来获取和设置线程的优先级值。

当线程终止时,线程对象将达到信号状态,该状态满足正在等待该对象的线程。

线程对象将保留在系统中,直到线程终止并通过调用CloseHandle关闭了所有 句柄。

的 ExitProcess的, 了ExitThread, CreateThread的, CreateRemoteThread的功能,以及正在启动一个过程(作为结果 的CreateProcess呼叫)的过程中彼此之间串行化。这些事件一次仅在地址空间中发生一次。这意味着以下限制成立:

  • 在进程启动和DLL初始化例程期间,可以创建新线程,但是直到对该进程完成DLL初始化之后,它们才开始执行。
  • 一个进程中只有一个线程可以同时处于DLL初始化或分离例程中。
  • 所有线程完成其DLL初始化或分离例程后,ExitProcess返回。

此功能的常见用法是将线程注入正在调试的进程中以发出中断。但是,不建议使用此方法,因为多余的线程会使调试应用程序的人感到困惑,并且使用此技术有一些副作用:

  • 它将单线程应用程序转换为多线程应用程序。
  • 它更改了进程的时间和内存布局。
  • 它导致对进程中每个DLL的入口点的调用。

此功能的另一个常见用法是将线程注入到进程中以查询堆或其他进程信息。这可能会导致前段所述的副作用。同样,如果线程尝试获取另一个线程正在使用的锁的所有权,则应用程序可能会死锁。

代码实现

test.cpp代码:

#include <iostream>

void add() { 
   
    for (int i = 0; i < 15; i++)
    { 
   
        printf("add%d\n", i);
    }
}

int main()
{ 
   
    getchar();
}

CreatRemote.cpp

#include <iostream>
#include<Windows.h>

void fun() { 
   
    for (int i = 0; i < 15;i++) { 
   
        printf("fun%d\n", i);
        
    }
}
DWORD WINAPI  ThreadProc(LPVOID lpParameter) { 
   
    fun();
    return 0;
}

int main()
{ 
   
   HANDLE handle1=OpenProcess(PROCESS_ALL_ACCESS, FALSE, 7348);

    HANDLE handle = CreateRemoteThread(handle1,NULL, 0, (LPTHREAD_START_ROUTINE)0x00EC1EE0,NULL,0,NULL);
 
   
    CloseHandle(handle);  
    CloseHandle(handle1);
    system(" pause");

}

需要获取两个值,一个是PID值,一个是test中线程函数地址值
在这里插入图片描述

PID值7348
在这里插入图片描述
线程函数地址值0x00EC1EE0

运行CPP后,显示启动成功
在这里插入图片描述
然后按下test.cpp继续运行
在这里插入图片描述
在这里插入图片描述

GAMEOVER

在这里插入图片描述
调用成功,GAMEOVER

未开启远程线程时,

运行结果:
在这里插入图片描述

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/10609.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信