`
fnay15fnay
  • 浏览: 13198 次
社区版块
存档分类
最新评论

DLL, 线程本地存储

 
阅读更多

DLL, 线程本地存储
2010年12月08日
  ref : http://blog.chinaunix.net/u3/95713/showart_2399623 .html 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 
  建议永远不要使有这个值,它的存在仅仅是为了向后兼容、
  (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中包含的一个符号时才进行加载,它与动态加载不同。
  4.已知的DLL (Known DLLs)
  位置: HKEY_LOCAL_MACHINE"SYSTEM"CurrentControlSet"Contro l"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注射到另一个进程的地址空间去,我们就可以在那个被注入的进程里为所欲为了。
  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
  ??
  在创建线程时,进程会为当前创建的线程分配一个 void *的数组作为 TLS用。它用于存储只限当前线程可见的全局变量。
  从而使进程中的每个线程都可以有自已的 (不能其它线程访问的 )全局变量。
  TlsAlloc在返回时会先把槽中的值置为 0。每个线程至少有 64个槽。
  2.静态TLS
  __declspec(thread)关键字用于声明,线程本地的全局变量。
  要求声明的变量必须是全局变量或静态变量。
分享到:
评论

相关推荐

    如何查杀运行状态下的EXE、DLL病毒

    也可以用工具IceSword(冰刃)中"文件/设置/禁止进线程创建",来停掉其中一个进程,再停掉另一个进程,杀掉病毒。  3、对于像被"熊猫烧香"感染的EXE文件,上述两种手工处理无效,因为无法手工清除受病毒感染的文件...

    Windows 资源查看器 MiTeC EXE Explorer 2.7.1.0 + x64 绿色中文.zip

    该应用程序还显示有关资源(例如光标,位图,图标,字符串,版本),字符串,标志,线程本地存储,版本信息(产品版本,文件,描述,版权,注释,产品和公司名称)的详细信息作为十六进制信息。 而且,您可以执行...

    VB端口扫描器.rar

    VB端口扫描器,基于FM20.DLL,多线程搜索端口,可指定线程的多少,可自定义端口的搜索范围,支持网络端口检索,比如下边一些常用端口也会扫描:  Case 7  plus = "ECHO服务"  Case 21  plus = "FTP服务★★...

    《Windows高级编程指南(第三版)》(含PASCAL例子)

    - 显示本地逻辑驱动器信息 MultInst -- 使用EXE中带有共享属性的PE节存储数据 TInjLib & -- 远程线程装载指定DLL到指定进程空间 ImgWalk LISLab -- 实验"局部输入状态" (Local Input State) TLSStat -- 在...

    Windows 内核情景分析--采用开源代码ReactOS (上册) part01

    5.11 线程本地存储TLS 421 5.12 进程挂靠 434 5.13 Windows的跨进程操作 442 5.14 Windows线程间的相互作用 450 第6章 进程间通信 467 6.1 概述 467 6.2 共享内存区(Section).. 469 6.3 线程的等待/唤醒...

    操作系统面试之??程序、进程、线程

    它包含所有可执行模块或DLL模块的代码和数据。它还包含动态内存分配的空间。如线程堆栈和堆分配空间。     定义 使用系统运行资源情况 程序 计算机指令的集合,它...

    C/C++笔试题(附答案,华为面试题系列)

    全局变量储存在静态数据库,局部变量在堆栈。 5.什么是平衡二叉树? 左右子树都是平衡二叉树 且左右子树的深度差值的绝对值不大于1。 6.堆栈溢出一般是由什么原因导致的? 没有回收垃圾资源。 7.什么函数不能...

    用C语言开发手机软件-Windows CE 6.0开发者参考

    8.5 线程本地存储 8.6 同步 8.6.1 事件 8.6.2 等待 8.6.3 信号量 8.6.4 互斥量 8.6.5 复制同步旬柄 8.6.6 临界区 8.6.7 互锁变量访问 8.7 进程间通信 8.7.1 查找其他进程 8.7.2 WM-COPYDATA 8.7.3 命名内存映射对象 ...

    用C语言开发手机软件 -Windows+CE+6.0开发者参考

    8.5 线程本地存储 8.6 同步 8.6.1 事件 8.6.2 等待 8.6.3 信号量 8.6.4 互斥量 8.6.5 复制同步旬柄 8.6.6 临界区 8.6.7 互锁变量访问 8.7 进程间通信 8.7.1 查找其他进程 8.7.2 WM-COPYDATA 8.7.3 命名内存映射对象 ...

    Windows编程循序渐进.part2

    16.3 线程本地存储器(TLS) 301 16.3.1 静态TLS和动态TLS 301 16.3.2 实例:使用静态TLS示例 303 16.3.3 实例:使用动态TLS示例 304 第17章 结构化异常处理 306 17.1 SEH的概念、特性 306 17.2 SEH的基本...

    Windows编程循序渐进.part3

    16.3 线程本地存储器(TLS) 301 16.3.1 静态TLS和动态TLS 301 16.3.2 实例:使用静态TLS示例 303 16.3.3 实例:使用动态TLS示例 304 第17章 结构化异常处理 306 17.1 SEH的概念、特性 306 17.2 SEH的基本...

    vc++ 开发实例源码包

    系统硬件信息、存储设备管理、鼠标及键盘、声音和视频、图形和图像、网络、数据库) 《远程控制编程技术》源代码 内含(重启、图片操作、ip操作、键盘与鼠标、客户端以及服务端、文件传输等实例源码) 多个VC++...

    cmd操作命令和linux命令大全收集

    23. regsvr32 /u *.dll----停止dll文件运行 24. drwtsn32------ 系统医生 25. rononce -p----15秒关机 26. dxdiag---------检查DirectX信息 27. regedt32-------注册表编辑器 28. Msconfig.exe---系统配置实用...

    深入解析Windows操作系统中文.part2.rar

    环境子系统和子系统DLL 53 硬件抽象层(HAL) 67 设备驱动程序 69 系统进程 75 2.5 本章总结 84 第3章 系统机制 85 3.1 陷阱分发 85 中断分发 87 异常分发 109 系统服务分发 119 3.2 对象管理器 124 执行体对象 126 ...

    Windows系统进程详解

    grovel.exe 扫描零备份存储(SIS)卷上的重复文件,并且将重复文件指向一个数据存储点,以节省磁盘空间。(系统服务) SCardSvr.exe 对插入在计算机智能卡阅读器中的智能卡进行管理和访问控制。(系统服务) snmp.exe ...

    Visual C++通用范例开发金典(第三卷/共三卷)

     8.1.1 本地信息操作  8.1.2 获取网络信息  8.2 高级网络操作  8.3 基于串口的通信  8.4 本章小结  第9章 打印程序设计  9.1 基本打印操作  9.1.1 打印的基本框架  9.1.2 单文档打印  9.1.3 对话框打印  ...

    Visual C++通用范例开发金典(分卷二/共三卷)

     8.1.1 本地信息操作  8.1.2 获取网络信息  8.2 高级网络操作  8.3 基于串口的通信  8.4 本章小结  第9章 打印程序设计  9.1 基本打印操作  9.1.1 打印的基本框架  9.1.2 单文档打印  9.1.3 对话框打印  ...

    Visual C++通用范例开发金典(第一卷、共两卷)

     8.1.1 本地信息操作  8.1.2 获取网络信息  8.2 高级网络操作  8.3 基于串口的通信  8.4 本章小结  第9章 打印程序设计  9.1 基本打印操作  9.1.1 打印的基本框架  9.1.2 单文档打印  9.1.3 对话框打印  ...

    C#全能速查宝典

    1.1.4 变量——存储特定类型的数据 4 1.1.5 Console类——控制台中的输入流、输出流和错误流 6 1.1.6 Convert类——类型转换 8 1.1.7 常量——值不改变的量 9 1.1.8 Dispose方法——释放资源 10 1.1.9 迭代器——...

Global site tag (gtag.js) - Google Analytics