好友
阅读权限25
听众
最后登录1970-1-1
|
fllc
发表于 2014-3-20 13:15
线程初始化过程 PK 加载DLL过程 :
//-------------------------------------------------------------------------------------------------------
//线程初始化过程:
//-------------------------------------------------------------------------------------------------------
7C92E450 ; __stdcall KiUserApcDispatcher(x, x, x, x, x)
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里面LoadLibrary会死锁,因为在DllMain未完成时,LdrpLoaderLock全局锁没释放,
LoadLibrary内部又会调RtlEnterCriticalSection(&LdrpLoaderLock);完事之后就互相卡住了,
LoadLibrary返回不了,DllMain就结束不了;反之DllMain结束不了,LoadLibrary也返回不了,
缠缠绵绵,无限死锁!!!(MSDN没有解释死锁的原因)
② 如果想在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(如果有)。
感觉有用的给个评分吧,谢谢鼓励。
|
-
|