windows核心编程 DLL技术 【转】

注:本文章转载于网络,源地址为:http://blog.csdn.net/ithzhang/article/details/7051558

本篇文章将介绍DLL显式链接的过程和模块基地址重定位及模块绑定的技术。

第一种将DLL映射到进程地址空间的方式是直接在源代码中引用DLL中所包含的函数或是变量,DLL在程序运行后由加载程序隐式的载入,此种方式被称为隐式链接。

第二种方式是在程序运行时,通过调用API显式的载入所需要的DLL,并显式的链接所想要链接的符号。换句话说,程序在运行时,其中的一个线程能够显式的将该DLL调用到进程地址空间中,并得到DLL中某函数的在进程地址空间的虚拟地址,然后调用该函数。此种方式被称为显式链接。

注意:显式载入某DLL时,不需要该dll的Lib文件,且exe文件中并不包含该dll的导入表。

显示载入DLL模块的步骤:

线程可以调用LoadLibrary将一个DLL映射到进程地址空间。

[cpp] view plaincopyprint?

  1. HMODULE LoadLibrary(PCTSTR pszDLLPathName);

[cpp] view plaincopyprint?

  1. HMODULE LoadLibrary(PCTSTR pszDLLPathName);

该函数会试图对程序想载入的DLL进行定位,并试图将该DLL映射到调用进程的地址空间中。返回是DLL在调用进程的虚拟地址。即模块的句柄。如果无法将DLL载入到进程地址空间中返回值为NULL.

与它类似的另一个函数

[cpp] view plaincopyprint?

  1. HMODULE LoadLibraryEx(PCTSTR pszDLLPathName,HANDLE hFile,DWORD dwFlags);

[cpp] view plaincopyprint?

  1. HMODULE LoadLibraryEx(PCTSTR pszDLLPathName,HANDLE hFile,DWORD dwFlags);

也可以实现将DLL载入到进程地址空间的目的。具体请参考MSDN。

加载后如果程序不再需要该DLL,可以调用FreeLibrary将DLL从进程地址空间中卸载:

[cpp] view plaincopyprint?

  1. BOOL FreeLibrary( HMODULE  hInstDll );

[cpp] view plaincopyprint?

  1. BOOL FreeLibrary( HMODULE  hInstDll );

也可以调用FreeLibraryEx卸载某DLL。

以下函数不仅具有从进程地址空间卸载某DLL的功能,还能退出调用线程:

[cpp] view plaincopyprint?

  1. VOID FreeLibraryAndExitThread(HMODULE hInstDll,DWORD dwExitCode)
  2. {
  3. FreeLibrary(HMODULE hInstDll);
  4. ExitThread(dwExitCode);
  5. }

[cpp] view plaincopyprint?

  1. VOID FreeLibraryAndExitThread(HMODULE hInstDll,DWORD dwExitCode)
  2. {
  3. FreeLibrary(HMODULE hInstDll);
  4. ExitThread(dwExitCode);
  5. }

刚见到时或许你会觉得它很多余。考虑下面的情形:

我们调用一个DLL,该DLL中的代码会创建一个线程,当此线程完成工作后,可以调用FreeLibrary和ExitThread将DLL从进程地址空间中卸载,并终止自己。由于线程是由DLL创建的,线程执行的代码也在DLL中,当线程调用FreeLibrary将它所在的DLL卸载的时候,它后续要执行的代码已不再进程地址空间中了,试图执行不存在的代码可能会导致访问违规,导致进程被终止。

如果线程调用FreeLibraryAndExitThread,此函数在Kernel32.dll中,FreeLibraryAndExitThread函数调用FreeLibrary将线程函数所在的DLL卸载后,其所属DLL Kernel32.dll仍在进程地址空间内,FreeLibraryAndExitThread函数继续执行调用ExitThread,后续代码仍然存在,不会导致访问违规。

每个DLL在进程中都有一个使用计数。LoadLibrary(Ex)会增加其计数,FreeLibrary(Ex)和FreeLibraryAndExitThread会递减其计数。例如:当程序第一次调用LoadLibrary来载入一个DLL时,系统会将此DLL映射到进程地址空间中,并将此DLL的使用计数加一。如果线程后来再次调用LoadLibrary(Ex)时,系统不会将此DLL再次映射到进程地址空间,仅仅递增此DLL的使用计数。为了从进程地址空间中撤销对该DLL的映射,线程必须调用FreeLibrary(Ex)两次。第一次是将此DLL的使用计数减为1,第二次减为0。当系统发现某DLL的使用计数已经为0时,会从进程地址空间卸载此DLL。此时如果线程试图显式调用DLL中的函数将会导致访问违规。

系统会在每个进程中为DLL维护一个使用计数,在本进程调用LoadLibrary仅仅是增加DLL在本进程的使用计数。如果进程A中的一个线程执行了LoadLibrary("Mydll.dll");进程B的某一线程也调用LoadLibrary("Mydll.dll");那么该DLL会被映射到A,B两个进程空间中去,且在A和B进程的使用计数都为1。

调用FreeLibrary("Mydll.dll");也仅仅是递减DLL在本进程内的使用计数。

[cpp] view plaincopyprint?

  1. HMODULE  GetModuleHandle(PCTSTR pszModuleName);

[cpp] view plaincopyprint?

  1. HMODULE  GetModuleHandle(PCTSTR pszModuleName);

该函数可以用来检测某DLL是否被映射到了进程地址空间。如果返回值为NULL,则此DLL未被载入。

当给pszModuleName传NULL时,函数会返回应用程序可执行文件的句柄。

显式链接导出符号

显式载入某个DLL后,线程可以通过调用以下函数来得到它要引用的符号的地址。

[cpp] view plaincopyprint?

  1. FARPROC GetProcAddr(HMODULE hInstDll, PCSTR pszSymbolName);

[cpp] view plaincopyprint?

  1. FARPROC GetProcAddr(HMODULE hInstDll, PCSTR pszSymbolName);

hInstDll标识导出符号所在的DLL的句柄。它是LoadLibrary(Ex),或是GetModuleHandle所返回的句柄。

pszSymbolName用于标识导出符号。

pszSymbolName可以有两种形式:

第一种:用符号名来指定我们想要得到哪个符号的地址。

如:FARPROC pfn=GetProcAddress(hInstDll,"MyProc");

它是以0结尾的字符串。要注意此字符串是ANSI类型的。因为编译器、链接器始终都是将符号的名称以ANSI字符串的形式保存在DLL的导出段。

第二种:用序号来指定我们想要那个符号的地址。

如:FARPROC pfn=GetProcAddress(hInstDll,MAKERESOURCE(2));

这种方法假定我们知道某个导出符号在某DLL中的序号为2。应该明确的是Microsoft强烈反对使用序号。

使用序号的形式要比使用字符串速度慢,因为系统需要对一字符串标识的符号名进行字符串比较。使用第二种方法即使该序号并没有与任何导出函数相对应,GetProcAddress也会返回非NULL值。其实这个地址是无效的,访问此地址可能会导致访问违规。

注意:使用GetProcAddress返回的函数指针来调用函数之前,需要将它转换成与函数签名相匹配的类型。

例如:

[cpp] view plaincopyprint?

  1. typedef void (CALLBACK *PFN_DUM_MOUDLE)(MODULE hModule);

[cpp] view plaincopyprint?

  1. typedef void (CALLBACK *PFN_DUM_MOUDLE)(MODULE hModule);

它是与void DynamicDumpModule(HMODULE hModule)函数相对应的函数相同。

动态调用某DLL导出函数的例子:

[cpp] view plaincopyprint?

  1. <SPAN style="FONT-SIZE: 18px"> PFN_DUMPMODULE pfnDumpModule=(PFN_DUMPMODULE)GetProcAddress(hDll,"DumpModule");
  2. If(pfnDumpModule!=NULL)
  3. {
  4. pfnDumpModule(hDll);
  5. }
  6. PAN>

[cpp] view plaincopyprint?

  1. <span style="font-size:18px;"> PFN_DUMPMODULE pfnDumpModule=(PFN_DUMPMODULE)GetProcAddress(hDll,"DumpModule");
  2. If(pfnDumpModule!=NULL)
  3. {
  4. pfnDumpModule(hDll);
  5. }
  6. pan>

DLL的入口点函数

一个DLL可以有一个入口点函数,系统会在不同的时候调用这个函数。这些调用是通知性质的,通常被DLL用来执行与进程或线程有关的初始化和清理工作。

如果不需要执行这些操作,可以不必再源代码中不实现此函数。

如果需要DLL接受这些通知,就应该按照如下的格式来实现该函数。

[cpp] view plaincopyprint?

  1. <SPAN style="FONT-SIZE: 18px">Bool WINAPI DllMain(HINSTANCE hInsDll,DWORD fdwReason,PVOID fImpLoad)
  2. {
  3. Swith(fdwReason)
  4. {
  5. Case DLL_PROCESS_ATTACH:
  6. //DLL被映射到进程地址空间是,执行此处代码。
  7. Break;
  8. Case DLL_THREAD_ATTACH:
  9. //线程被创建的时候执行。
  10. Break;
  11. Case DLL_THREAD_DETACH:
  12. //线程终止运行时执行。
  13. Break;
  14. Case DLL_PROCESS_DETACH:
  15. //DLL被卸载的时候执行。
  16. Break;
  17. }
  18. }
  19. </SPAN>

[cpp] view plaincopyprint?

  1. <span style="font-size:18px;">Bool WINAPI DllMain(HINSTANCE hInsDll,DWORD fdwReason,PVOID fImpLoad)
  2. {
  3. Swith(fdwReason)
  4. {
  5. Case DLL_PROCESS_ATTACH:
  6. //DLL被映射到进程地址空间是,执行此处代码。
  7. Break;
  8. Case DLL_THREAD_ATTACH:
  9. //线程被创建的时候执行。
  10. Break;
  11. Case DLL_THREAD_DETACH:
  12. //线程终止运行时执行。
  13. Break;
  14. Case DLL_PROCESS_DETACH:
  15. //DLL被卸载的时候执行。
  16. Break;
  17. }
  18. }
  19. </span>

hInstDll是该DLL实例的句柄。它是DLL文件被映射到进程地址空间的虚拟地址。通常将这个参数保存在全局变量中。这样在DLL的其他导出函数中就可以使用。

如果DLL是被隐式载入的,fImpLoad为非零值,显式的话fImportLoad为0。

fdwReason表示系统调用入口点函数的原因。它是switch语句的参数。可以是上述四个值。分别表示四种情况。后续将会详细介绍每一种情况。

注意:DLL使用DllMain对自己进行初始化。DllMain执行的时候,其他DLL的可能还未被初始化。这意味着我们应该避免在DllMain中调用从其他DLL中导出的函数。

DLL_PROCESS_ATTACH通知

当系统第一次将一个DLL映射到进程地址空间是,会调用DllMain函数,并给fdwReason传入DLL_PROCESS_ATTACH。注意:只有在该DLL是第一次被调用到进程地址空间中时,才会调用DllMain。如果以后再次调用LoadLibrary(Ex)时,OS仅仅是递增该DLL在此进程的使用计数,并不会再次调用DllMain。

当DLL在处理DLL_PROCESS_ATTACH时,应该根据需要执行与进程相关的初始化。如DLL中包含一些函数,需要使用自己的堆,可以在进程加载时执行一些堆的初始化工作。

处理DLL_PROCESS_ATTACH时,DllMain的返回值表示DLL的初始化是否成功。如初始化成功,应返回TRUE,否则应返回false。

下面来看看DllMain调用的时机:

创建新进程时,系统为该进程分配地址空间,并将exe可执行文件和所需要的DLL映射到进程地址空间。然后创建主线程,并用主线程来调用每个DLL的DllMain函数,同时传入DLL_PROCESS_ATTACH。当所有已映射的DLL完成对该通知的处理后,系统会让主进程执行可执行模块的C/C++运行库的启动代码。然后执行可执行模块的入口点函数(_tmain或_tWinMain)。如果任意一个DLL的DllMain返回false,就说明初始化失败,系统会将所有文件映像从地址空间中清除,向用户显示错误信息。

显式载入DLL的过程:

进程调用LoadLibrary(Ex),该函数对DLL进行定位,并将该DLL映射到进程地址空间。然后会让调用LoadLibrary(Ex)的线程调用DllMain函数,并传入DLL_PROCESS_ATTACH。当DLL的DllMain函数完成了对通知的处理后,系统会让LoadLibrary返回。这样线程就可以继续执行。

注意:DllMain是在进程调用LoadLibrary(Ex)的时候调用的。它返回到LoadLibrary(Ex)函数内。

DLL_PROCESS_ATTACH通知

当一个DLL从进程的地址空间中撤销的时候,会调用该DLL的DllMain函数,并在fdwReason传入DLL_PROCESS_DETACH。该case语句内一般是用来执行与进程相关的清理工作。如调用HeapDestroy清理堆。

注意:当DLL刚被映射到进程地址空间,执行DllMain并传入DLL_PROCESS_ATTACH时的返回值为false时,所有DLL将会被撤销映射,此时并不会调用DllMain并传入DLL_PROCESS_DETACH。

下面谈谈调用DllMain并传入DLL_PROCESS_DETACH的时机:

1:当进程又由于某线程调用ExitProcess而终止时,映射到该进程的所有DLL都会被撤销。调用 ExitProcess的线程将负责执行DllMain。一般情况下,此线程就是主线程。

2:如果DLL被撤销的原因是因为进程中的线程调用了FreeLibrary或是FreeLibraryAndExitThread,那么执行上述函数的线程将负责对DllMain的调用。调用完成后线程返回,继续执行其他代码。

注意:如果进程终止是因为某个线程调用TerminateProcess,此时DllMain并不会被调用。这意味着在进程终止前,已经映射到进程的任何DLL将没有任何机会执行清理工作,这可能导致数据丢失或是已被该进程占用的信号量不能得到释放。因此不到万不得已,应该避免使用TerminageProcess。

DLL_THREAD_DEATTACH通知

当进程创建一线程的时候,系统会检查已映射到此进程的所有DLL,并用DLL_THREAD_ATTACH调用每个DLL的DllMain。一般在此时执
行与线程有关的初始化。DllMain的代码是由新创建的线程执行。当该线程完成了所有DllMain之后,才会执行它的线程函数。

注意:仅仅是让新建的线程执行已经被映射到进程地址空间的DLL的DllMain函数。而不会让已经存在的线程调用DllMain。当系统的主线程被创建
的时候,并不会调用DllMain并传入DLL_PROCESS_ATTACH。它已经在进程被创建的时候调用DllMain并传入
DLL_PROCESS_ATTACH。

DLL_THREAD_DETACH通知

当线程调用ExitThread将要终止的时候,系统会让该线程用DLL_THREAD_DETACH调用所有已映射到进程地址空间的所有DLL的DllMain函数。这一般被用来执行与线程相关的清理工作。

注意:如果线程终止是因为其他线程调用TerminateThread而终止的话,系统不会用DLL_THREAD_DETACH让线程调用各DLL的
DllMain。因此与TerminateProcess一样,除非万不得以,应避免使用TerminateThread函数。

下面来总结下调用DllMain的过程:

进程中的一个线程调用LoadLibrary来映射一个DLL,系统使该线程用DLL_PROCESS_ATTCH调用该DLL的DllMain函数(该
线程不会得到DLL_THREAD_ATTACH)通知。当此线程退出时,系统让此线程再次调用所有DLL的DllMain函数,但此次传入的是
DLL_THREAD_DETACH。虽然在该DLL映射的时候,不会向该DLL发送DLL_THREAD_ATTACH通知。但是当该线程退出时,会向
DLL发送DLL_THREAD_DETACH通知。

之所以不发送DLL_PROCESS_DETACH通知,是因为DLL仍在进程中。只有当DLL被卸载时,才会发送此通知。

前面我们提到过DllMain函数并不是必须的。在链接DLL的时候,如果链接器无法在obj文件中发现DllMain函数,它会链接C/C++运行库的
DllMain函数。如果我们不提供DllMain函数,C/C++运行库会认为我们不关系DLL的各种通知。它会调用
DisableThreadLibraryCalls函数。

[cpp] view plaincopyprint?

  1. <SPAN style="FONT-SIZE: 18px">     BOOL DisableThreadLibraryCalls(HMODULE hInstDll);
  2. </SPAN>

[cpp] view plaincopyprint?

  1. <span style="font-size:18px;">     BOOL DisableThreadLibraryCalls(HMODULE hInstDll);
  2. </span>

该函数告诉系统  我们不想让系统向某个指定的DLL发送DLL_THREAD_ATTACH和DLL_THREAD_DETACH通知。

C/C++运行库中实现的DllMain函数如下所示:

[cpp] view plaincopyprint?

  1. <SPAN style="FONT-SIZE: 18px">    BOOL WINAPI DllMain(HINSTANCE hInstDll,DWORD fdwReason,PVOID fImpLoad)
  2. {
  3. if(fdwReason==DLL_PROCESS_ATTACH)
  4. DisableThreadLibraryCalls(hInstDll);
  5. return true;
  6. }
  7. </SPAN>

[cpp] view plaincopyprint?

  1. <span style="font-size:18px;">    BOOL WINAPI DllMain(HINSTANCE hInstDll,DWORD fdwReason,PVOID fImpLoad)
  2. {
  3. if(fdwReason==DLL_PROCESS_ATTACH)
  4. DisableThreadLibraryCalls(hInstDll);
  5. return true;
  6. }
  7. </span>

延迟载入DLL

所谓延迟载入DLL,就是在进程运行后加载程序加载各种DLL时,并不载入已经被设为延迟加载的DLL。直到该DLL中的某个导出函数被调用的时候,此DLL才会被加载到进程的地址空间中。该DLL是隐式链接的 。

这项特性非常有用,主要应用与以下各种情况下:

1:某进程使用了很多DLL,由于初始化时加载程序必须将所有DLL都映射到进程地址空间中,这会导致加载速度比较慢。如果使用延迟加载,某些DLL直到其导出符号被引用到的时候,该DLL才会被隐式加载到进程地址空间,这缩短了初始化时间。

2:当应用程序在代码上使用了一个新的函数,运行在不提供此函数的老版本的系统上时,如果该函数所在的DLL不使用延迟加载机制,加载程序会报告一个错
误:无法找到该函数。接着便会终止该应用程序的执行。如果我们使用延迟加载技术,当程序检测到此时是运行在老的系统中,程序就不会调用此函数,转而使用可
以在老的系统上使用的其它函数。程序仍然可以继续运行。由于不会在程序中引用在老系统中不支持的函数,该函数所在的DLL就不会被加载。

当然任何方法都有适用范围,延迟加载不适用于以下几种情况:

1:导出全局变量的DLL是无法延迟加载的。

2:Kernel32.dll是无法延迟加载的,LoadLibrary和GetProcAddress都在该模块中。必须加载该模块才可以调用它们。

3:不应该在DllMain中代用延迟加载函数,这样会导致程序崩溃。

要让延迟加载能够正常工作,首先要指定两个链接器开关。

/Lib:DelayImp.dl

/DelayLoad:要延迟加载的DLL名字。

它们不可以在代码中通过#pragma comment(linker,"")来设定。而要通过Configuration Properities属性页来设定。

/Lib:DelayImp.dll是通过Linker/Advanced/DelayLoadDLL开关来指定。它告诉链接器将函数_delay_LoadHelper2嵌入到我们的可执行文件中。

/DelayLoad开关可以通过Linker /input/DelayLoadDLLs开关来指定。要延迟载入的函数所在的DLL在该项的右侧指定。可以指定多个延迟载入DLL。

该开关告诉链接器::

1:将用户要延迟载入的DLL从可执行文件的导入段中去除,这样当进程初始化时该DLL就不会被隐式的载入。

2:在可执行文件中嵌入一个延迟载入段,来表示要从用户要延迟载入的DLL导入哪些函数。

3:当程序调用延迟载入DLL中的函数时,对该函数的调用会转到_delayLoadHelper2函数,来完成对延迟载入函数的解析。也就是说对延迟载
入段中的函数的调用,实际上会调用_delayLoadHelper2函数。此函数会引用延迟载入段,然后调用LoadLibrary和
GetProcAddress得到延迟载入函数的地址。一旦得到延迟载入函数的地址_delayLoadHelper2就会修复对该函数的调用(Windows核心编程的原话,至于如何修复不清楚。2011年12月8日注)。今后的调用将直接调用该延迟载入函数。注意:同一个DLL的其它函数仍然必须在第一次被调用的时候修复。对同一DLL中某一延迟函数的调用并不会对其他延迟函数的调用进行修复

关于延迟载入函数暂时介绍这么多。感兴趣的话可以参考其他文献。

时间: 2024-08-05 04:59:44

windows核心编程 DLL技术 【转】的相关文章

【windows核心编程】使用远程线程注入DLL

前言 该技术是指通过在[目标进程]中创建一个[远程线程]来达到注入的目的. 创建的[远程线程]函数为LoadLibrary, 线程函数的参数为DLL名字, 想要做的工作在DLL中编写.  示意图如下:  相关API 1.创建远程线程 //该函数除了第一个参数为目标进程句柄外 //其他参数均和CreateThread一样 HANDLE hThread = CreateRemoteThread( __in HANDLE hProcess, //目标进程句柄 __in_opt LPSECURITY_A

【windows核心编程】DLL相关(2)

关于DLL的延迟加载 延迟加载DLL,使用的是隐式加载方式,当为exe使用的DLL指定为延迟加载的时候,连接器会将exe的[导入段]中去除该DLL的相关信息,同时在exe中嵌入一个新的[延迟加载段]表示要从该DLL中导入哪些函数. 通过让对延迟加载函数的调用跳转到delayimp.lib中的__delayLoadHelper2函数,来完成对延迟加载的DLL的解析. 当exe中第一次调用了一个延迟加载的DLL中的某个导出函数时,加载器才会将该DLL加载到进程地址空间中.需要注意的是:虽然此时已经加

【windows核心编程】DLL相关

DLL相关的东西 1.DLL的加载方式 隐式: #pragma comment(lib, "XX.lib"); 编译器去查找名为XX.dll的DLL,除了名字相同,该DLL和该LIB的GUID也相同. 显式: HINSTANCE   hInst = LoadLibrary(TEXT("XX.dll")); if(NULL == hInst)  retrun; HINSTANCE hInst = LoadLibrary(TEXT("XX.dll")

【windows核心编程】DLL相关(3)

DLL重定向 因为DLL的搜索路径有先后次序,假设有这样的场景:App1.exe使用MyDll1.0.dll, App2.exe使用MyDll2.0.dll, MyDll1.0 和 MyDll2.0是同一个DLL的两个版本,1.0为旧版本,2.0为新版本. 而如果MyDll2.0.dll的存放路径的优先次序比较靠前时,那么App1.exe就会去加载MyDll2.0.dll,这就可能引发 DLL地狱问题,因此DLL重定向可解决这个问题. 加载程序总是先检查应用程序目录,我们所要做的就是如下: ①在

Windows核心编程---动态链接库(XX.dll)与静态库(XX.lib)

最近细读了Windows核心编程的内存管理与动态链接库部分,虽然有些人对Windows未来说三道四,但不得不承认微软windows系统的强大功能,以及其深邃的架构设计思想,令人钦佩.通过这次阅读对库文件有了更深刻的认识,总结于此! 动态链接库(Dynamic Linked Library): Windows为应用程序提供了丰富的函数调用,这些函数调用都包含在动态链接库中.其中有3个最重要的三个库为:Kernel32.dll.Use32.dll.GDI32.dll,介绍如下: Kernel32.d

【转】《windows核心编程》读书笔记

这篇笔记是我在读<Windows核心编程>第5版时做的记录和总结(部分章节是第4版的书),没有摘抄原句,包含了很多我个人的思考和对实现的推断,因此不少条款和Windows实际机制可能有出入,但应该是合理的.开头几章由于我追求简洁,往往是很多单独的字句,后面的内容更为连贯. 海量细节. 第1章    错误处理 1.         GetLastError返回的是最后的错误码,即更早的错误码可能被覆盖. 2.         GetLastError可能用于描述成功的原因(CreatEvent)

C++Windows核心编程读书笔记

转自:http://www.makaidong.com/%E5%8D%9A%E5%AE%A2%E5%9B%AD%E6%96%87/71405.shtml "C++Windows核心编程读书笔记": 关键词:c++windows 核心 编程 读书笔记 这篇笔记是我在读<windows核心编程>第5版时做的记录和总结(部分章节是第4版的书),没有摘抄原句,包含了很多我个人的思考和对实现的推断,因此不少条款和windows实际机制可能有出入,但应该是合理的.开头几章由于我追求简洁

《windows核心编程系列》十八谈谈windows钩子

windows应用程序是基于消息驱动的.各种应用程序对各种消息作出响应从而实现各种功能. windows钩子是windows消息处理机制的一个监视点,通过安装钩子能够达到监视指定窗体某种类型的消息的功能.所谓的指定窗体并不局限于当前进程的窗体,也能够是其它进程的窗体.当监视的某一消息到达指定的窗体时,在指定的窗体处理消息之前,钩子函数将截获此消息,钩子函数既能够加工处理该消息,也能够不作不论什么处理继续传递该消息.使用钩子是实现dll注入的方法之中的一个.其它经常使用的方法有:注冊表注入,远程线

【windows核心编程】线程局部存储TLS

线程局部存储TLS, Thread Local Storage TLS是C/C++运行库的一部分,而非操作系统的一部分. 分为动态TSL 和 静态TLS 一.动态TLS 应用程序通过调用一组4个函数来使用动态TLS, 这些函数实际上最为DLL所使用. 系统中的每个进程都有一组 正在使用标志(in-use flag), 每个标志可被设置为FREE 或者 INUSE, 表示该TLS元素是否正在使用. 微软平台保证至少有TLS_MINUMUM_AVALIABLE个标志位可供使用, TLS_MINUMU