DLL 线程本地存储
2010-02-25 19:17
387 查看
1.概览
.构造DLL
(1)仅导出函数
DLL可以导出全局变量和类,但我们不建议这么做,建议导出函数。
(2).lib
每个
DLL都有与之相对应的
.lib文件
,该文件中列出了
DLL中导出的函数和变量的符号名
(3)指定要导出的函数名
因为不同编译器的
Name mangle规则不同,这就导致
DLL不能跨编译器使用。
有以下两种方法可以解决这个问题:
1.在
.def文件中指定要导出的函数名
2.在编译指中指定要导出的函数名:
#pragma comment(linker, "/export:MyFunc=_MyFunc@8")
.DLL加载路径
当需要加载一个
DLL时,系统会依照下面的顺序去寻找所需
DLL直到找到为止,然后加载,否则加载失败。
(1)当前可执行文件路径
(2)GetWindowsDirectory返回的Windows系统路径
(3)16位系统的路径
windows"system
(4)GetSystemDirectory返回的Windows系统路径
(5)当前进程所在路径
(6)PATH环境中所指定的路径
.创建/使用动态链接库
首先必须创建一个包含需要导出的符号的头文件,以便其他程序链接到该dll上:
// dllexample.h
#ifdef DLLEXAMPLE_EXPORTS // 在编译命令中已定义,所以实际用的是 __declspec(dllexport)
#define DLLEXAMPLE_API __declspec(dllexport)
#else
#define DLLEXAMPLE_API __declspec(dllimport)
#endif
DLLEXAMPLE_API int fnDllexample(void);
当其他应用包含该头文件,意图使用该dll的导出符号时,因为没有定义DLLEXAMPLE_EXPORTS,所以使用的是
__declspec(dllimport),这样编译器编译时便知道这是从外部引入的函数。在链接时,链接程序将生成导入表
(ImportAddressTable),该表罗列了所有调用到的函数,以及一个空白的对应地址。在程序执行时,加载器将动态的填入每个函数符号在本进
程中的地址,使得程序能正确的调用到dll中的函数上。
这种通过dll提供的.h和.lib文件进行链接dll的使用方式,称为隐式链接
。用vc开发程序时,几乎所有的系统API调用都用了隐式链接。
.显式链接
在exe创建时不引用.lib文件中的符号,当然也不必包含.h头文件,而是由程序调用LoadLibrary(Ex)以及
GetProcAddress函数来获取每个需要使用的函数地址,从而进行dll中的函数调用,这种dll使用方法称为显式链接。显式链接时不生成对应
dll的IAT.
当决定不再使用该dll时,通过调用FreeLibrary来卸载。需要注意的是,同一个进程中共计调用LoadLibrary的次数要和调用FreeLibrary的次数相等,因为系统维护了一个使用计数,当计数为0时,才会真正的卸载该dll.
如果想确认一个dll是否已经被映射到进程空间中,尽量使用GetModuleHandle,最好不要冒然使用LoadLibrary(Ex).
GetProcAddress可以传递函数名或者序号(通过MAKEINTRESOURCE(2)来"制作"序号).
1.1动态加载DLL文件 LoadLibraryEx
HMODULE LoadLibraryEx( //返回
DLL加载到进程空间原首地址。
PCTSTR pszDLLPathName,
HANDLE hFile,
DWORD dwFlags
);
dwFlags
可以有以下几个值
(1) DONT_RESOLVE_DLL_REFERENCES
建议永远不要使有这个值,它的存在仅仅是为了向后兼容、
更多内容请访问: http://blogs.msdn.com/oldnewthing/archive/2005/02/14/372266.aspx
(2) LOAD_LIBRARY_AS_DATAFILE
把要加载的
DLL文件以数据文件的形式加载到进程中。
GetModuleHandle和
GetProcAddress返回
NULL
(3) LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
与前者相同,不同的时独占打开,禁止其它进程访问和修改该
DLL中的内容。
(4) LOAD_LIBRARY_AS_IMAGE_RESOURCE
不修改
DLL中的
RVA,以
image的形式加载到进程中。常与
LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE一起使用。
(5) LOAD_WITH_ALTERED_SEARCH_PATH
修改
DLL的加载路径
1.2 DLL
的加载与卸载
(1)加载
不要在同一进程中,同时使用
LoadLIbrary和
LoadLibraryEx加载同一
DLL文件。
DLL的引用计数是以进程为单位的。
LoadLibrary会把
DLL文件加载到内存,然后映射到进程空间中。
多次加载同一
DLL只会增加引用计数而不会多次映射。当所有进程对
DLL的引用计数都为
0时,系统会在内存中释放该
DLL。
(2)卸载
FreeLibrary,FreeLibraryAndExitThread对当前进程的
DLL的引用计数减
1
(3) GetProcAddress
取得函数地址。它只接受
ANSI字符串。
2.DLL的入口函数
2.1 DllMain
BOOL WINAPI DllMain(
HINSTANCE hInstDll, ""加载后在进程中的虚拟地址
DWORD fdwReason, ""系统因何而调用该函数
PVOID fImpLoad ""查看是隐工还是动态加载该
DLL
DLLs用
DllMain方法来初始化他们自已。
DllMain中的代码应尽量简单,只做一些简单的初始化工作。
不要在
DllMain中调用
LoadLibrary,FreeLibrary及
Shell, ODBC, COM, RPC, 和
socket 函数,从而避免不可预期的错误。
2.2 fdwReason的值
(1)DLL_PROCESS_ATTACH
系统在为每个进程第一次加载该
DLL时会,执行
DLL_PROCESS_ATTACH后面的语句来初始化
DLL,DllMain的返回值仅由它决定。
系统会忽略
DLL_THREAD_ATTACH等执行后
DllMain的返回值。
如果
DllMain返回
FALSE,系统会自动调用
DLL_PROCESS_DETACH的代码并解除
DLL文件中进程中的内存映射。
(2)DLL_PROCESS_DETACH
如果
DLL是因进程终止而卸载其在进程中的映射,那么负责调用
ExitProcess的线程会调用
DllMain中
DLL_PROCESS_DETACH所对应的代码。
如果
DLL是因
FreeLibrary或
FreeLibraryAndExitThread,而卸载其在进程中的映射,
那么
FreeLibrary或
FreeLibraryAndExitThread会负责调用
DllMain中
DLL_PROCESS_DETACH所对应的代码。
如果
DLL是因
TerminateProcess而卸载其在进程中的映射
,系统不会调用
DllMain中
DLL_PROCESS_DETACH所对应的代码。
(3) DLL_THREAD_ATTACH
若进程是先加载的
DLL,后创建的线程
那么在进程中创建新线程时
(主线程除外
),系统会执行该进程已载的所有
DLL的
DllMain中
DLL_THREAD_ATTACH对应的代码。
若进程是先创建的线程
,后加载的
DLL
那么系统不会调用
DLL的
DllMain中的代码。
(4) DLL_THREAD_DETACH
进程中的线程退出时,会先执行所有已加载
DLL的
DllMain中
DLL_THREAD_DETACH所对应的代码。若该代码中有死循环,线程不会退出。
2.3 同步化DllMain的调用
同一时间只能有一个线程调用
DllMain中的代码,所以下面的代码会导致死循环
BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, PVOID fImpLoad) {
HANDLE hThread;
DWORD dwThreadId;
switch (fdwReason) {
case DLL_PROCESS_ATTACH
:
// The DLL is being mapped into the process' address space.
// Create a thread to do some stuff.
hThread = CreateThread(NULL, 0, SomeFunction, NULL,
0, &dwThreadId);// CreateThread会
DLL_THREAD_ATTACH
中的代码,但是由于当前线程并未执行完毕
,
//所以
DLL_THREAD_ATTACH
中的代码不会被执行,且
CreateThread永无不会返回。
// Suspend our thread until the new thread terminates.
WaitForSingleObject(hThread, INFINITE);
// We no longer need access to the new thread.
CloseHandle(hThread);
break;
case DLL_THREAD_ATTACH
:
// A thread is being created.
break;
case DLL_THREAD_DETACH:
// A thread is exiting cleanly.
break;
case DLL_PROCESS_DETACH:
// The DLL is being unmapped from the process' address space.
break;
}
return(TRUE);
}
3.延时加载DLL
(1)延时加载
DLL的限制
延迟加载的D L L是个隐含链接的D L L,它实际上要等到你的代码试图引用D L L中包含的一个符号时才进行加载
,它与动态加载不同。
http://msdn2.microsoft.com/en-us/library/yx1x886y(VS.80).aspx
4.已知的DLL (Known DLLs)
位置:
HKEY_LOCAL_MACHINE"SYSTEM"CurrentControlSet"Control"Session Manager"KnownDLLs
LoadLibrary在查找
DLL会先去该位置查找有无相应的键值与
DLL要对应,若有则根据链值去
%SystemRoot%"System32加载键值对应的
DLL
若无则根据默认规去寻找
DLL
5.Bind and Rebase Module
它可以程序启动的速度。
ReBaseImage
DLL 注入和
API钩
(DLL Injection and API Hooking)
1.概览
每个进程都有自已独立的地址空间,一个进程不可能创建一个指向其它进程地址空间的指针。
然而如果我们把自已的
DLL注射到另一个进程的地址空间去,我们就可以在那个被注入的进程里为所欲为了。
Subclass同一进程中的窗体: http://msdn2.microsoft.com/en-us/library/ms649784.aspx
.
2.用注册表注入DLL
该方法适用于给
GUI的程序注入
DLL
所有的
GUI应用程序在启动时都会加载
User32.dll,而在
User32.dll的
DLL_PROCESS_ATTACH
代码根据注册表中的信息
来注入用户指定的DLL
注册表项 HKEY_LOCAL_MACHINE"Software"Microsoft"Windows NT"CurrentVersion"Windows"
中有两个值:
LoadAppInit_Dlls:键值中指定要注入的
DLL 如
:c:"inject.dll
AppInit_Dlls:若其键值为
1,则注入
LoadAppInit_Dlls中指定的
DLL,否则若为
0则不注入。
注
:
(1)LoadAppInit_Dlls中的值是以空格或分号分隔的,所以
DLL的路径中最好不要有空格,最后不指定路径,直接将
DLL放到
windows系统目录中。
(2) 用注册表注入
DLL的方式有很大的局限性
,Kernel32.dll和
Ntdll.dll中有的函数才能调用
一.注入dll
1
.通过注册表项
HKEY_LOCAL_MACHINE/Software/Microsoft/Windows
NT/CurrentVersion/Windows/AppInit_DLLs
来指定你的dll的路径,那么当一个GUI程序启动时就要加载User32.dll,而User32.dll将会检查这个值,如果有的话就
LoadLibrary该Dll。这个方法不好,因为大多数情况我们只需要针对性的注入,并且没办法注入到不使用User32.dll的进程中;
2
.用SetWindowsHookEx函数,并传递目标线程ID、需要挂载的Dll在本进程中的映射地址
(hInstance)、替换函数在本进程中的地址。这样,当被挂载进程的这个线程要执行相应的操作时(GETMESSAGE、键盘消息之类的),就会发
现已经安装了WH_XX,The system checks to see whether the DLL containing the GetMsgProc
function is mapped into Process B's address
space,如果还未映射该Dll,则强制LoadLibrary。然后系统调用hThisInstance + (GetMsgProc -
hInstance),从而实现了事件的通知。这种方法的好处是可以针对某个进程安装Hook,缺点是容易被目标进程发现、同样只适用于GUI进程。如果
不再想使用挂钩了,那么需要调用UnhookWindowsHookEx,卸载Hook。
3
.使用远程线程注入Dll(Injecting a DLL Using Remote Threads)
这个方法比较好。流程是这样的:
?调用VirtualAllocEx,在目标进程保留一块内存,并提交,其长度是你要注入Dll的全路径长度nLen + 1,返回地址pv;
?调用WriteProcessMemory,在目标进程的pv处写入Dll的全路径,注意要添加/0结束符;
?获取本进程
的LoadLibrary函数的地址,方法是调用pfn =
GetProcAddress(GetModuleHandle(TEXT("Kernel32")),
"LoadLibraryA")——之所以获取本进程的地址,是因为kernel32.dll在每个进程的映射地址都相同,倘若不同,那么此方法则无效;
?调用HANDLE hThread = CreateRemoteThread(hProcessRemote, NULL,
0, pfn, pv, 0,
NULL)来创建远程线程,其实这个线程函数就是LoadLibrary函数,因此将执行映射Dll到目标进程的操作;
?调用VirtuallFreeEx(hProcessRemote, pv)释放提交的内存;
这便完成了dll注入。
缺点是不能用在windows98上。但是对于xp都要被微软抛弃的年代,windows98地影响不大了。
4
.披着羊皮的狼:使用特洛伊Dll来注入Dll(Injecting a DLL with a Trojan DLL)
其实就是替换某个目标进程要加载的a.dll,并把a.dll的所有引出函数用函数转发器在自己的dll引出。
5
.用调试函数插入Dll
ReadProcessMemory和WriteProcessMemory是windows提供的调试函数。如果在方法3中调用
WriteProcessMemory写入的不是字串而是精心编排好的机器指令,并且写在目标进程特定的地址空间,那么这段机器指令就有机会执行——而这
段机器指令恰好完成了LoadLibrary功能;
6
.其他方法(略)
二.挂接API(API Hooking)
其实,这是许多注入的Dll都愿意做的事情。
所谓挂接API就是在目标进程调用windows API之前,先执行我们的仿API函数,从而控制系统API的行为,达到特殊的目的。
我们的仿造函数必须与要替换的系统API有相同的型参表以及相同的返回值类型.
1
.改写系统API代码的前几个字节,通过写入jmp指令来跳转到我们的函数。在我们的函数里执行操作,可以直接返回一个值,也可以将系统API的前几个字节复原,调用系统API,并返回系统API的值——随便你想怎么做。
此方法的缺点是对于抢占式多线程的系统不太管用。
2.通过改写目标进程IAT中要调用的函数地址来达到目的。具体操作见书中示例
线程本地存储
(Thread-Local Storage)
例子C / C + +运行期库要使用线程本地存储器( T L S)。由于运行期库是在多线程应用程序出现前的许多年设计的,因此运行期库中的大多数函数是用于单线程应用程序的。函数s t r t o k就是个很好的例子。
尽可能避免使用全局变量和静态变量
1.动态TLS
图21-1 用于管理T L S的内部数据结构
在创建线程时,进程会为当前创建的线程分配一个
void *的数组作为
TLS用。它用于存储只限当前线程可见的全局变量。
从而使进程中的每个线程都可以有自已的
(不能其它线程访问的
)全局变量。
TlsAlloc在返回时会先把槽中的值置为
0。每个线程至少有
64个槽。
2.静态TLS
__declspec(thread)关键字用于声明,线程本地的全局变量。
要求声明的变量必须是全局变量或静态变量。
3.Common API:
TlsAlloc TlsFree
TlsSetValue TlsGetValue
__declspec(thread)
.构造DLL
(1)仅导出函数
DLL可以导出全局变量和类,但我们不建议这么做,建议导出函数。
(2).lib
每个
DLL都有与之相对应的
.lib文件
,该文件中列出了
DLL中导出的函数和变量的符号名
(3)指定要导出的函数名
因为不同编译器的
Name mangle规则不同,这就导致
DLL不能跨编译器使用。
有以下两种方法可以解决这个问题:
1.在
.def文件中指定要导出的函数名
2.在编译指中指定要导出的函数名:
#pragma comment(linker, "/export:MyFunc=_MyFunc@8")
.DLL加载路径
当需要加载一个
DLL时,系统会依照下面的顺序去寻找所需
DLL直到找到为止,然后加载,否则加载失败。
(1)当前可执行文件路径
(2)GetWindowsDirectory返回的Windows系统路径
(3)16位系统的路径
windows"system
(4)GetSystemDirectory返回的Windows系统路径
(5)当前进程所在路径
(6)PATH环境中所指定的路径
.创建/使用动态链接库
首先必须创建一个包含需要导出的符号的头文件,以便其他程序链接到该dll上:
// dllexample.h
#ifdef DLLEXAMPLE_EXPORTS // 在编译命令中已定义,所以实际用的是 __declspec(dllexport)
#define DLLEXAMPLE_API __declspec(dllexport)
#else
#define DLLEXAMPLE_API __declspec(dllimport)
#endif
DLLEXAMPLE_API int fnDllexample(void);
当其他应用包含该头文件,意图使用该dll的导出符号时,因为没有定义DLLEXAMPLE_EXPORTS,所以使用的是
__declspec(dllimport),这样编译器编译时便知道这是从外部引入的函数。在链接时,链接程序将生成导入表
(ImportAddressTable),该表罗列了所有调用到的函数,以及一个空白的对应地址。在程序执行时,加载器将动态的填入每个函数符号在本进
程中的地址,使得程序能正确的调用到dll中的函数上。
这种通过dll提供的.h和.lib文件进行链接dll的使用方式,称为隐式链接
。用vc开发程序时,几乎所有的系统API调用都用了隐式链接。
.显式链接
在exe创建时不引用.lib文件中的符号,当然也不必包含.h头文件,而是由程序调用LoadLibrary(Ex)以及
GetProcAddress函数来获取每个需要使用的函数地址,从而进行dll中的函数调用,这种dll使用方法称为显式链接。显式链接时不生成对应
dll的IAT.
当决定不再使用该dll时,通过调用FreeLibrary来卸载。需要注意的是,同一个进程中共计调用LoadLibrary的次数要和调用FreeLibrary的次数相等,因为系统维护了一个使用计数,当计数为0时,才会真正的卸载该dll.
如果想确认一个dll是否已经被映射到进程空间中,尽量使用GetModuleHandle,最好不要冒然使用LoadLibrary(Ex).
GetProcAddress可以传递函数名或者序号(通过MAKEINTRESOURCE(2)来"制作"序号).
1.1动态加载DLL文件 LoadLibraryEx
HMODULE LoadLibraryEx( //返回
DLL加载到进程空间原首地址。
PCTSTR pszDLLPathName,
HANDLE hFile,
DWORD dwFlags
);
dwFlags
可以有以下几个值
(1) DONT_RESOLVE_DLL_REFERENCES
建议永远不要使有这个值,它的存在仅仅是为了向后兼容、
更多内容请访问: http://blogs.msdn.com/oldnewthing/archive/2005/02/14/372266.aspx
(2) LOAD_LIBRARY_AS_DATAFILE
把要加载的
DLL文件以数据文件的形式加载到进程中。
GetModuleHandle和
GetProcAddress返回
NULL
(3) LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE
与前者相同,不同的时独占打开,禁止其它进程访问和修改该
DLL中的内容。
(4) LOAD_LIBRARY_AS_IMAGE_RESOURCE
不修改
DLL中的
RVA,以
image的形式加载到进程中。常与
LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE一起使用。
(5) LOAD_WITH_ALTERED_SEARCH_PATH
修改
DLL的加载路径
1.2 DLL
的加载与卸载
(1)加载
不要在同一进程中,同时使用
LoadLIbrary和
LoadLibraryEx加载同一
DLL文件。
DLL的引用计数是以进程为单位的。
LoadLibrary会把
DLL文件加载到内存,然后映射到进程空间中。
多次加载同一
DLL只会增加引用计数而不会多次映射。当所有进程对
DLL的引用计数都为
0时,系统会在内存中释放该
DLL。
(2)卸载
FreeLibrary,FreeLibraryAndExitThread对当前进程的
DLL的引用计数减
1
(3) GetProcAddress
取得函数地址。它只接受
ANSI字符串。
2.DLL的入口函数
2.1 DllMain
BOOL WINAPI DllMain(
HINSTANCE hInstDll, ""加载后在进程中的虚拟地址
DWORD fdwReason, ""系统因何而调用该函数
PVOID fImpLoad ""查看是隐工还是动态加载该
DLL
DLLs用
DllMain方法来初始化他们自已。
DllMain中的代码应尽量简单,只做一些简单的初始化工作。
不要在
DllMain中调用
LoadLibrary,FreeLibrary及
Shell, ODBC, COM, RPC, 和
socket 函数,从而避免不可预期的错误。
2.2 fdwReason的值
(1)DLL_PROCESS_ATTACH
系统在为每个进程第一次加载该
DLL时会,执行
DLL_PROCESS_ATTACH后面的语句来初始化
DLL,DllMain的返回值仅由它决定。
系统会忽略
DLL_THREAD_ATTACH等执行后
DllMain的返回值。
如果
DllMain返回
FALSE,系统会自动调用
DLL_PROCESS_DETACH的代码并解除
DLL文件中进程中的内存映射。
(2)DLL_PROCESS_DETACH
如果
DLL是因进程终止而卸载其在进程中的映射,那么负责调用
ExitProcess的线程会调用
DllMain中
DLL_PROCESS_DETACH所对应的代码。
如果
DLL是因
FreeLibrary或
FreeLibraryAndExitThread,而卸载其在进程中的映射,
那么
FreeLibrary或
FreeLibraryAndExitThread会负责调用
DllMain中
DLL_PROCESS_DETACH所对应的代码。
如果
DLL是因
TerminateProcess而卸载其在进程中的映射
,系统不会调用
DllMain中
DLL_PROCESS_DETACH所对应的代码。
(3) DLL_THREAD_ATTACH
若进程是先加载的
DLL,后创建的线程
那么在进程中创建新线程时
(主线程除外
),系统会执行该进程已载的所有
DLL的
DllMain中
DLL_THREAD_ATTACH对应的代码。
若进程是先创建的线程
,后加载的
DLL
那么系统不会调用
DLL的
DllMain中的代码。
(4) DLL_THREAD_DETACH
进程中的线程退出时,会先执行所有已加载
DLL的
DllMain中
DLL_THREAD_DETACH所对应的代码。若该代码中有死循环,线程不会退出。
2.3 同步化DllMain的调用
同一时间只能有一个线程调用
DllMain中的代码,所以下面的代码会导致死循环
BOOL WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, PVOID fImpLoad) {
HANDLE hThread;
DWORD dwThreadId;
switch (fdwReason) {
case DLL_PROCESS_ATTACH
:
// The DLL is being mapped into the process' address space.
// Create a thread to do some stuff.
hThread = CreateThread(NULL, 0, SomeFunction, NULL,
0, &dwThreadId);// CreateThread会
DLL_THREAD_ATTACH
中的代码,但是由于当前线程并未执行完毕
,
//所以
DLL_THREAD_ATTACH
中的代码不会被执行,且
CreateThread永无不会返回。
// Suspend our thread until the new thread terminates.
WaitForSingleObject(hThread, INFINITE);
// We no longer need access to the new thread.
CloseHandle(hThread);
break;
case DLL_THREAD_ATTACH
:
// A thread is being created.
break;
case DLL_THREAD_DETACH:
// A thread is exiting cleanly.
break;
case DLL_PROCESS_DETACH:
// The DLL is being unmapped from the process' address space.
break;
}
return(TRUE);
}
3.延时加载DLL
(1)延时加载
DLL的限制
延迟加载的D L L是个隐含链接的D L L,它实际上要等到你的代码试图引用D L L中包含的一个符号时才进行加载
,它与动态加载不同。
http://msdn2.microsoft.com/en-us/library/yx1x886y(VS.80).aspx
4.已知的DLL (Known DLLs)
位置:
HKEY_LOCAL_MACHINE"SYSTEM"CurrentControlSet"Control"Session Manager"KnownDLLs
LoadLibrary在查找
DLL会先去该位置查找有无相应的键值与
DLL要对应,若有则根据链值去
%SystemRoot%"System32加载键值对应的
DLL
若无则根据默认规去寻找
DLL
5.Bind and Rebase Module
它可以程序启动的速度。
ReBaseImage
DLL 注入和
API钩
(DLL Injection and API Hooking)
1.概览
每个进程都有自已独立的地址空间,一个进程不可能创建一个指向其它进程地址空间的指针。
然而如果我们把自已的
DLL注射到另一个进程的地址空间去,我们就可以在那个被注入的进程里为所欲为了。
Subclass同一进程中的窗体: http://msdn2.microsoft.com/en-us/library/ms649784.aspx
.
2.用注册表注入DLL
该方法适用于给
GUI的程序注入
DLL
所有的
GUI应用程序在启动时都会加载
User32.dll,而在
User32.dll的
DLL_PROCESS_ATTACH
代码根据注册表中的信息
来注入用户指定的DLL
注册表项 HKEY_LOCAL_MACHINE"Software"Microsoft"Windows NT"CurrentVersion"Windows"
中有两个值:
LoadAppInit_Dlls:键值中指定要注入的
DLL 如
:c:"inject.dll
AppInit_Dlls:若其键值为
1,则注入
LoadAppInit_Dlls中指定的
DLL,否则若为
0则不注入。
注
:
(1)LoadAppInit_Dlls中的值是以空格或分号分隔的,所以
DLL的路径中最好不要有空格,最后不指定路径,直接将
DLL放到
windows系统目录中。
(2) 用注册表注入
DLL的方式有很大的局限性
,Kernel32.dll和
Ntdll.dll中有的函数才能调用
一.注入dll
1
.通过注册表项
HKEY_LOCAL_MACHINE/Software/Microsoft/Windows
NT/CurrentVersion/Windows/AppInit_DLLs
来指定你的dll的路径,那么当一个GUI程序启动时就要加载User32.dll,而User32.dll将会检查这个值,如果有的话就
LoadLibrary该Dll。这个方法不好,因为大多数情况我们只需要针对性的注入,并且没办法注入到不使用User32.dll的进程中;
2
.用SetWindowsHookEx函数,并传递目标线程ID、需要挂载的Dll在本进程中的映射地址
(hInstance)、替换函数在本进程中的地址。这样,当被挂载进程的这个线程要执行相应的操作时(GETMESSAGE、键盘消息之类的),就会发
现已经安装了WH_XX,The system checks to see whether the DLL containing the GetMsgProc
function is mapped into Process B's address
space,如果还未映射该Dll,则强制LoadLibrary。然后系统调用hThisInstance + (GetMsgProc -
hInstance),从而实现了事件的通知。这种方法的好处是可以针对某个进程安装Hook,缺点是容易被目标进程发现、同样只适用于GUI进程。如果
不再想使用挂钩了,那么需要调用UnhookWindowsHookEx,卸载Hook。
3
.使用远程线程注入Dll(Injecting a DLL Using Remote Threads)
这个方法比较好。流程是这样的:
?调用VirtualAllocEx,在目标进程保留一块内存,并提交,其长度是你要注入Dll的全路径长度nLen + 1,返回地址pv;
?调用WriteProcessMemory,在目标进程的pv处写入Dll的全路径,注意要添加/0结束符;
?获取本进程
的LoadLibrary函数的地址,方法是调用pfn =
GetProcAddress(GetModuleHandle(TEXT("Kernel32")),
"LoadLibraryA")——之所以获取本进程的地址,是因为kernel32.dll在每个进程的映射地址都相同,倘若不同,那么此方法则无效;
?调用HANDLE hThread = CreateRemoteThread(hProcessRemote, NULL,
0, pfn, pv, 0,
NULL)来创建远程线程,其实这个线程函数就是LoadLibrary函数,因此将执行映射Dll到目标进程的操作;
?调用VirtuallFreeEx(hProcessRemote, pv)释放提交的内存;
这便完成了dll注入。
缺点是不能用在windows98上。但是对于xp都要被微软抛弃的年代,windows98地影响不大了。
4
.披着羊皮的狼:使用特洛伊Dll来注入Dll(Injecting a DLL with a Trojan DLL)
其实就是替换某个目标进程要加载的a.dll,并把a.dll的所有引出函数用函数转发器在自己的dll引出。
5
.用调试函数插入Dll
ReadProcessMemory和WriteProcessMemory是windows提供的调试函数。如果在方法3中调用
WriteProcessMemory写入的不是字串而是精心编排好的机器指令,并且写在目标进程特定的地址空间,那么这段机器指令就有机会执行——而这
段机器指令恰好完成了LoadLibrary功能;
6
.其他方法(略)
二.挂接API(API Hooking)
其实,这是许多注入的Dll都愿意做的事情。
所谓挂接API就是在目标进程调用windows API之前,先执行我们的仿API函数,从而控制系统API的行为,达到特殊的目的。
我们的仿造函数必须与要替换的系统API有相同的型参表以及相同的返回值类型.
1
.改写系统API代码的前几个字节,通过写入jmp指令来跳转到我们的函数。在我们的函数里执行操作,可以直接返回一个值,也可以将系统API的前几个字节复原,调用系统API,并返回系统API的值——随便你想怎么做。
此方法的缺点是对于抢占式多线程的系统不太管用。
2.通过改写目标进程IAT中要调用的函数地址来达到目的。具体操作见书中示例
线程本地存储
(Thread-Local Storage)
例子C / C + +运行期库要使用线程本地存储器( T L S)。由于运行期库是在多线程应用程序出现前的许多年设计的,因此运行期库中的大多数函数是用于单线程应用程序的。函数s t r t o k就是个很好的例子。
尽可能避免使用全局变量和静态变量
1.动态TLS
图21-1 用于管理T L S的内部数据结构
在创建线程时,进程会为当前创建的线程分配一个
void *的数组作为
TLS用。它用于存储只限当前线程可见的全局变量。
从而使进程中的每个线程都可以有自已的
(不能其它线程访问的
)全局变量。
TlsAlloc在返回时会先把槽中的值置为
0。每个线程至少有
64个槽。
2.静态TLS
__declspec(thread)关键字用于声明,线程本地的全局变量。
要求声明的变量必须是全局变量或静态变量。
3.Common API:
TlsAlloc TlsFree
TlsSetValue TlsGetValue
__declspec(thread)
相关文章推荐
- DLL, 线程本地存储
- DLL, 线程本地存储
- [并发并行]_[C/C++]_[使用线程本地存储Thread Local Storage(TLS)调用复制文件接口的案例]
- 【Demo 0096】线程本地存储
- 线程本地存储linux, windows函数对应关系
- 几种线程本地存储变量和普通变量的性能比较
- linux中的线程本地存储pthread_key_t和pthread_once
- 线程本地存储TLS(Thread Local Storage)的原理和实现——分类和原理
- 利用线程本地存储降低服务器处理延时
- 线程本地存储(Thread Local Storage, TLS)分析与使用
- TLS 线程本地存储(转)
- 线程学习之线程本地存储
- 线程本地存储TLS(Thread Local Storage)的原理和实现——分类和原理
- Atitit usrqbg1821 Tls 线程本地存储(ThreadLocal Storage 规范标准化草案解决方案ThreadStatic
- 线程本地存储TLS(Thread Local Storage)的原理和实现——分类和原理
- [并发并行]_[C/C++]_[使用线程本地存储Thread Local Storage(TLS)调用复制文件接口的案例]
- [并发并行]_[C/C++]_[使用线程本地存储Thread Local Storage(TLS)-win32线程和pthread线程比较]
- 线程局部存储(或者叫线程本地存储)
- vc中线程的本地存储
- zthread学习 实例七 线程本地存储