欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

[原创]线程初始化过程 PK 加载DLL过程(详细)

[原创]线程初始化过程 PK 加载DLL过程(详细)

线程初始化过程 PK 加载DLL过程 :
//-------------------------------------------------------------------------------------------------------
//线程初始化过程:
//-------------------------------------------------------------------------------------------------------
7C92E450 ; __stdcall KiUserApcDispatcher(x, x, x, x, x)

7C92E455 FF D0 call eax //这里eax是LdrInitializeThunk函数地址
//-------------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------------

7C939962 E9 07 FF FF FF jmp __LdrpInitialize@12 ; _LdrpInitialize(x,x,x) //具体的实现代码在里面
//-------------------------------------------------------------------------------------------------------
__LdrpInitialize@12的大体流程:
__LdrpInitialize@12
{
  ...
  RtlEnterCriticalSection(&LdrpLoaderLock);
  if (Ldr == NULL)
  {
  call _LdrpInitializeProcess@20
  }
  else
  {
  call _LdrpInitializeThread@4 //↓↓↓解释
  }
  RtlLeaveCriticalSection(&LdrpLoaderLock);
  ...
}
//-------------------------------------------------------------------------------------------------------
_LdrpInitializeThread@4的大体流程:
_LdrpInitializeThread@4
{
  ...
  for (遍历Ldr链表)//以InMemoryOrderModuleList顺序
  {
  if ( !(Ldr->Flags & LDRP_DONT_CALL_FOR_THREADS) )//LDRP_DONT_CALL_FOR_THREADS被置位,就不会调用dll的oep和tls
  {
  if (Dll 有 tls)
  {
  LdrpCallTlsInitializers(LdrEntry->DllBase, DLL_THREAD_ATTACH);
  }
  LdrpCallInitRoutine(Ldr->EntryPoint, Ldr>DllBase, DLL_THREAD_ATTACH, NULL); //call dll的oep
  }
  }
  if (exe 有 tls)
  {
  LdrpCallTlsInitializers(Peb->ImageBaseAddress, DLL_THREAD_ATTACH);
  }
  ...
}
//-------------------------------------------------------------------------------------------------------
__LdrpInitialize@12 的分析:
⒈RtlEnterCriticalSection(&LdrpLoaderLock); //LdrpLoaderLock是一个全局锁,此锁还在LdrpLoadDll函数里面使用,这会造成一些有意思的情况↓
如果想在DllMain里面CreateThread创个线程,并且不让DllMain返回(白利用的节奏,怎么白利用自己玩
   吧,说出来被开...),还是会出现被LdrpLoaderLock全局锁卡主的情况。不想被卡主可以自己主动释放掉
   这把锁,锁的位置在PEB+0x0A0偏移处(+0x0a0 LoaderLock : Ptr32 Void),是在进程初始化的时候由
   全局变量LdrpLoaderLock赋给PEB+0x0A0的!!!

⒉在__LdrpInitialize@12 里面既有LdrpInitializeProcess,也有LdrpInitializeThread
系统是根据PEB+0x0C(+0x00c Ldr : Ptr32 _PEB_LDR_DATA)是否有指针,来决定调用LdrpInitializeProcess还是LdrpInitializeThread,
进程初始化(第一个线程)时Ldr为NULL,此时会调用LdrpInitializeProcess;之后再创建线程时Ldr就不为NULL了,会调用LdrpInitializeThread。
⒊LdrpInitializeThread分析
①在LdrpInitializeThread里面,会遍历Ldr链表(以InMemoryOrderModuleList顺序)call每个Dll的tls回调(如果有)和EntryPoint,EntryPoint最终
会call DllMain的DLL_THREAD_ATTACH,这里发现了一个有意思的Flags,LDRP_DONT_CALL_FOR_THREADS(0x0040000),
  在_LDR_DATA_TABLE_ENTRY.flags & LDRP_DONT_CALL_FOR_THREADS就不会调用指定Dll的DllMain(这是DisableThreadLibraryCalls 函数的基本原理)。
②call 完每个Dll的tls回调(如果有)和EntryPoint之后,会判断exe是否有tls,有的话会根据数据目录表的tls找到callback数组,
按顺序call每个callback。
ps:①有tls的exe,每创一个新线程都会调二次tls的回调(DLL_THREAD_ATTACH一次,DLL_THREAD_DETACH一次;新线程永不结束例外...);
②并且经测试,最初的进程初始化时,如果tls的第一次DLL_PROCESS_ATTACH失败(失败原因一般是进程还没初始化完成,某些dll的IAT未完成,
call未完成的IAT会失败),会在DLL_PROCESS_DETACH的时候补一次tls的回调执行(进程初始化的tls只执行一次,DLL_PROCESS_ATTACH或
DLL_PROCESS_DETACH;之后的新线程初始化时的tls会执行两次!!!)
   ③LdrpCallTlsInitializers内部call tls callback数组;LdrpCallInitRoutine 这个... (都是LdrpInitializeThread内部调的)
//-------------------------------------------------------------------------------------------------------
//Dll加载过程:
//-------------------------------------------------------------------------------------------------------
_LdrLoadDll@16的大体流程:_LdrLoadDll@16
{
  ...
  call _LdrLockLoaderLock@12 //内部调用RtlEnterCriticalSection(&LdrpLoaderLock);
  call _LdrpLoadDll@24 //↓↓↓解释
  call _LdrUnlockLoaderLock@8//内部调用RtlLeaveCriticalSection(&LdrpLoaderLock);
  ...
}
//-------------------------------------------------------------------------------------------------------
_LdrpLoadDll@24的大体流程:
_LdrpLoadDll@24
{
  ...
  call _LdrpMapDll@24//内部调NtCreateSection、NtMapViewOfSection;插入Ldr
  call _LdrpRunInitializeRoutines@4//初始化;理论上只会调用新load的dll的tls(如果有)和oep,但是代码太风骚了,可以做手脚↓↓↓解释
  ...
}
//-------------------------------------------------------------------------------------------------------
_LdrpRunInitializeRoutines@4的大体流程:
_LdrpRunInitializeRoutines@4
{
  ...
  for(遍历Ldr链表) //InInitializationOrderModuleList顺序
  {
  if ( !(Ldr->Flags & LDRP_ENTRY_PROCESSED) )//没有LDRP_ENTRY_PROCESSED这个标志位的就放入数组中
  {
  存到一个数组中ArrayBug; //理论上如果我们自己没改过Ldr->Flags,那么这个遍历最终只会有一个Ldr放入数组中,那就是最新Load的
   //那个dll的Ldr;但是我们可以把自己的dll(防别人注入的dll)去掉这个标志位!!!
  
}
  Ldr->Flags |= LDRP_ENTRY_PROCESSED;
  }
  for(循环遍历ArrayBug数组)
  {
  LdrpCallInitRoutine(EntryPoint, LdrEntry->DllBase, DLL_PROCESS_ATTACH, Context);
  }
  ...
}
//-------------------------------------------------------------------------------------------------------
LoadLibraryExW --->>> LdrLoadDll
⒈_LdrLoadDll@16的分析:
①LdrLockLoaderLock(1, 0, &v11); 内部调用了RtlEnterCriticalSection(&LdrpLoaderLock); 跟__LdrpInitialize@12里面共用一把锁
②_LdrLoadDll@16 -> call _LdrpLoadDll@24 ; LdrpLoadDll(x,x,x,x,x,x) //主要功能在_LdrpLoadDll@24内部实现
⒉_LdrpLoadDll@24 的分析:
①call _LdrpMapDll@24 ; LdrpMapDll(x,x,x,x,x,x) //总体的作用就是把dll映射进进程的地址空间
  //内部的LdrpCreateDllSection是创建NtCreateSection创建区块对象;之后映射NtMapViewOfSection;
  //再然后是调用_LdrpAllocateDataTableEntry@4、_LdrpInsertMemoryTableEntry@4等操作把这个dll的_LDR_DATA_TABLE_ENTRY结构插入到Ldr链表里
②在映射完之后,就是对dll进行初始化,call _LdrpRunInitializeRoutines@4 ; LdrpRunInitializeRoutines(x)
  内部会调用LdrpCallTlsInitializers(调用dll的tls回调,如果有的话)、LdrpCallInitRoutine(调用dll的EntryPoint)
  代码很风骚,具体自己结合od、ida、reactos看吧,风骚大了就出问题了,有一个有意思的flags,LDRP_ENTRY_PROCESSED(0x0004000),如果把
  某个dll这个标志位去掉,那么当有dll加载时,被去掉LDRP_ENTRY_PROCESSED标志位的dll的oep就会先于要加载的dll的oep执行!!!
  这完全可以风骚的做到用自己的dll拦截其他人加载dll(XP下通过;Win7下如果只这样做,退出程序时会崩,发现是在ExitProcess前有 没有释放的内存,
  因为我们的dll等于是多次进入DLL_PROCESS_ATTACH初始化,办法我已经想到了,并且测试通过了,自己想办法吧,就是不让他申请那没用的内存就可以)
ps:LdrpRunInitializeRoutines函数的参数系统默认会传NULL,此时加载dll过程就不会调用dll的tls也不会调用exe的tls;
如果传非NULL,代码貌似会执行dll的tls(如果有)和exe的tls(如果有)。
//-------------------------------------------------------------------------------------------------------



Release.zip
↑↑↑先于要加载的dll弹框
xp,win7都通过,当然win7下还要改一下  

上传的附件 1.jpg
2.jpg
3.jpg
first.dll测试代码.jpg
second.dll测试代码.jpg
exe测试代码.jpg
Release.zip

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途。
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功