找回密码
 加入计匠网
搜索
热搜: BIOS ACPI CPU Windows
查看: 13983|回复: 0

[转载]WINDOWS 2K Dll 加载过程

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url]
- H3 _. {0 D$ b/ H. ?8 g4 `1 ~7 A# w( V* R# y  c' d
WINDOWS 2K Dll 加载过程
" Y5 x8 T! {' J+ A2 U% h! Hjefong by 2005/03/30
9 U5 \, ~1 R' _( i7 Y: u" Z) |. Z这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。- U0 W& g8 Z. F+ R
在windows中exe可执行程序运行时都会调用一些DLL,例如KERNEL32.DLL和USER32.DLL等系统的dll。但是dll是怎么被加载的呢?通常,大家都知道在编写dll时会有一个DLLMain的入口函数,但是实际上这个函数并不是调用dll时最先的工作。首先dll需要被加载,然后要进行初始化分配,再之后才进入DLLMain。还有可能你的一个dll中还会调用另一各dll。那么dll到底是怎样加载和初始化的呢,我们来参考一下Platform SDK中的“Dynamic-Link Library Entry-Point Function”。
) P0 R9 @( a" f- y, Y你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。
3 Z1 ^" e; l8 R+ s! x5 ^初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。& L% a! S9 l$ [/ I2 f7 T' {: R
  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。& W. {: b- p5 m: T* ]
  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。
* I/ O. t9 M) T( A) y& z( I* y( u  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):6 b/ j, h, A% E( d
//=============================================================================
. B- B6 T- q$ }: _* W0 L: A// Matt Pietrek, September 1999 Microsoft Systems Journal
. |6 l1 ~( S/ b& F// 中文注释部分为jefong翻译$ ^" m$ O; V6 u6 _; e% m
//2 o0 A- K& Y7 j% T/ T4 [! R. t
// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3)
# Y/ w( J( _- z. f  b- n//
( i% s- u- C; L- Y; @+ {// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;
3 m/ Q4 `% }/ ]4 _! k" G8 R) A& o//=============================================================================
9 T" \; G! A+ o3 I1 k  y0 S7 ^1 O/ \+ M1 `: F
#include <ntexapi.h>    // For HardError defines near the end5 W& L/ l$ R  I

/ Q4 Q0 m( ]' ~9 |5 }3 }9 y& H// Global symbols (name is accurate, and comes from NTDLL.DBG)& Y! ~- f& `3 g7 W" ^' X' u
//  _NtdllBaseTag& F7 D4 w0 J6 z; ~6 X
//  _ShowSnaps
) |2 Y9 R' g: Q1 A//  _SaveSp2 q& n( q; _# F7 t
//  _CurSp
/ q5 f; |7 D1 u. ~//  _LdrpInLdrInit6 Y$ Z' H# G( `: r1 p
//  _LdrpFatalHardErrorCount& i" E1 S- e, P  e, ^7 C9 C
//  _LdrpImageHasTls5 c+ G$ i/ P: }; G
* C5 X$ W  {' X- S( @2 P* T
NTSTATUS7 J4 Y4 M% q5 k8 L4 j
LdrpRunInitializeRoutines( DWORD bImplicitLoad )/ m* ?7 C9 K8 M- w8 P
{
# A9 J2 r) t& R. T  n2 M    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了2 S( B( ?' B% t2 N, b
    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();
- F  h- H" o6 Q  G3 B* O* ]- X% C% R( [# u2 t
    if ( nRoutinesToRun )
4 n  w4 C) H2 L& D2 x    {
2 O; ^+ [' t- C8 R        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。
/ ?1 v$ D* F" E9 K# v2 W4 V        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),' `# Z) }8 Y& h7 A! t0 U! U' j, G$ P! k
                                            _NtdllBaseTag + 0x60000,
% ]( P1 s! R* D- Z9 ]" [3 K" p                                            nRoutinesToRun * 4 );
- E" Z( D! _$ x7 X) _, b3 k                            9 L2 `7 L3 T0 Q
        if ( 0 == pInitNodeArray )    // Make sure allocation worked- i2 G5 E( O9 h3 x2 o5 c4 e4 y
            return STATUS_NO_MEMORY;
3 x0 O# W: @& U# R' \7 q9 K5 s# k    }5 @+ ]5 H$ [, V" S2 B& j
    else
  m6 k& a6 T4 \! v, J4 u- H9 a        pInitNodeArray = 0;8 _9 T: f& k, a6 e: ]7 Z. A
2 ]- }; k3 w( ^% |
    //第二部分;8 p9 }. d% _( [
    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。0 j2 ?. m3 ^" D- i: x) ?
    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);% ?/ L5 X5 z, |* E, n8 j. L1 I
    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;7 ?2 y$ p( {5 g3 \" b
        
3 C4 x' e9 G/ O: @    if ( _ShowSnaps )
; B, Q3 j6 K' G" d    {
. _: h7 ]2 X% R/ R! v        _DbgPrint( "LDR: Real INIT LIST\n" );
, @1 Q$ S+ {6 U# ?3 v7 r    }. ~- k) V& s9 S; A) ?

8 q% ]& {" [* p    nModulesInitedSoFar = 0;- S" O" k! |% I1 |, x
. z5 u0 k; v6 K; B; w
    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块  v# N$ `% T$ i* F4 R
    {
4 F$ `2 `% a; z. T        
* k; D2 `" y& }! o        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块
0 h1 ^" M5 M4 W        {% H- [/ G' S  }1 r$ [$ C+ l
            ModuleLoaderInfo  pModuleLoaderInfo;
9 h# a5 M' C( z/ d; _0 W            
) n1 j+ J( c3 c$ |            //# b" ~9 I3 ~4 A& y
            //一个ModuleLoaderInfo结构节点的大小为0X10字节  W+ W) u+ [2 A. a! v8 [, S' D8 J- N
            pModuleLoaderInfo = &NextNode - 0x10;
% r9 X: \" C0 M7 x& S! @8 S2 e            
" h, D7 b( C7 y) W  s  f4 G            localVar3C = pModuleLoaderInfo;         
4 r0 ~8 y$ |  D! {* ~4 Q) e. }6 C5 O( i' s
            //6 U" S4 ?, a$ b4 R! l: E' ~
            // 如果模块已经被初始化,就忽略
; \$ y! x* U3 F0 ~, N0 K. e            // X_LOADER_SAW_MODULE = 0x40 已被初始化) ?6 X  u. g; a+ m+ [
            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )/ [" f3 Y& A) r& ~: q  H8 E" l
            {
) E& @3 n( b7 |                //
9 f! S$ @3 H9 y* p                // 模块没有被初始化,判断是否具有入口函数, s% q* F7 H. d' E9 v, o/ ?: q
                //, k- Z- Q' n+ q3 |' t% s
                if ( pModuleLoaderInfo->EntryPoint )
+ ~' R4 a7 [& ^                {4 _% l# [# L1 O* z. c3 v4 I
                    //
- P% q1 e; O, v                    // 具有初始化函数,添加到模块列表中,等待进行初始化
( c3 [0 U/ h* J  W+ ?$ W) j                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;
6 r$ ?8 q6 D6 v8 K4 H5 ?) j; S+ F/ n9 Y& t
                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址
  G, E0 g  G5 T* o) T0 N      // 例如:0 L& u3 ]6 d$ W8 u& B
                    // C:\WINNT\system32\KERNEL32.dll init routine 77f010002 `! O0 d7 t$ @6 Q$ [* b5 x
                    if ( _ShowSnaps )
/ e0 b* N! i6 H( @- x, L! o. ?                    {
8 w+ q) f: N2 [. p! x* J                        _DbgPrint(  "%wZ init routine %x\n",
5 x7 j. t( ?) s# U  b4 `                                    &pModuleLoaderInfo->24,. X* a; P$ f$ G$ [5 c: r7 G
                                    pModuleLoaderInfo->EntryPoint );
5 M/ f4 j( g) @. X: l# y                    }
, m/ _7 B- P: F/ k3 a& p7 T1 }5 ~/ F2 m# t
                    nModulesInitedSoFar++;1 k, q/ A8 a( ^
                }
$ a; M. }$ Q3 X# i( Y) k- j            }& D* e3 ]' x- k# ]
" g8 u4 N) @5 P3 b/ |. k  F
            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。
- ^1 Q% H6 B+ G* p$ {: q            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;1 v- O6 W; Z# X, y! q: M3 [# D

0 H5 n, v( W+ j' W* Y0 R. K: l! S            // 处理下一个模块节点
7 A3 B; k, o1 S2 P" k4 j3 l            pCurrNode = pCurrNode->pNext
3 q; U6 N1 j/ O& X! {        }
: k# i( c8 v+ B$ A3 w    }
+ l1 t- `! d% r* h. w# L% L    else/ V1 O# v; l# V) z6 D
    {
% [: ]1 }9 ^- ^6 n* K: X5 `        pModuleLoaderInfo = localVar3C;     // May not be initialized???
; `) }+ x/ I% c2 M7 d+ C    }
4 Y0 u8 o4 f# M$ f% c. ^   
: `! I6 C' f2 H: i/ H    if ( 0 == pInitNodeArray )
4 p; V; I2 W# Z5 O9 t: c        return STATUS_SUCCESS;* _/ r1 |6 L+ I8 P
* I/ R% f  E* c2 e4 r% b" V; Y! L
    // ************************* MSJ Layout! *****************
& _/ ]# D$ R% ?  ^7 f$ S! T    // If you're going to split this code across pages, this is a great
" q% ?) M6 T" E, F    // spot to split the code.  Just be sure to remove this comment  v8 K, H1 h8 O& \6 R
    // ************************* MSJ Layout! *****************: [9 ?$ w/ M  V. M. k. n
    1 P+ u. Y8 n, P0 T' t* a, L
    //
3 b  f0 M6 i. Y- {    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH% s% _* j$ a! x# Q" e$ W: Q
    // 第三部分,调用初始化部分$ f. A/ w; K& q4 _; H7 y# Q+ T
    try     // Wrap all this in a try block, in case the init routine faults
7 d5 `/ m2 H, Y' r  I8 O    {
: V- I" p" m: e4 s5 z        nModulesInitedSoFar = 0;  // Start at array element 0
: P& [' Z4 V. Q0 M7 {: I
3 G: u9 W" F9 Q        //
' l  }! H+ H9 L! e" d8 Z        // 遍历模块队列
& c4 D$ Y% }! ]8 m' ]        //2 Q3 u& w& X9 U8 F( C1 j* b
        while ( nModulesInitedSoFar < nRoutinesToRun )4 `: r8 d9 p3 Z; s
        {$ k; {6 @* d- J2 X
            // 获得模块指针' e+ e/ K* J- R5 x3 Y2 `
            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];* Y1 W, Y- W! q! j$ K8 T# {; D+ s

( Q3 @# Y7 R3 T2 O2 ], r0 B            // This doesn't seem to do anything...
3 y* Z! c3 l6 u; Q7 X            localVar3C = pModuleLoaderInfo;6 `% J( R* I" t: }' M+ u' c! r
            - q+ R7 T6 U) }) S  r$ ]8 O
            nModulesInitedSoFar++;
5 S+ C/ H4 {  h. x' S( Y$ ^4 ^               
) B9 A$ c( d& b            // 保存初始化程序入口指针6 A) |+ H) c, ^; G  [2 b5 c5 R: D
            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;
% \8 g) C  U  x# Q& i            5 r( Y% v; M- z7 T1 A& c! G
            fBreakOnDllLoad = 0;    // Default is to not break on load$ w" I: P/ z6 v

+ w2 g# o4 e, {* t7 G            // 调试用' h  R3 h/ l6 H. m. \' z0 X- u
            // If this process is a debuggee, check to see if the loader
+ b. x8 S& {" |; O; c            // should break into a debugger before calling the initialization." z9 t) M7 ^( I- i* {
            //
* _( M) }* N8 y1 R            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()
6 C7 q  _" M: i+ L) P9 @            // returns. IsDebuggerPresent is an NT only API.
3 Q" E$ O+ a# V0 @  q$ I            //
; H8 a+ w/ V5 e7 w7 z, j            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )
6 G# t1 a0 X2 S  j& O            {
0 [  N  {/ H( |, H; W                LONG retCode;
/ P5 ~9 L+ z# \
/ ~0 ~2 I5 r0 x0 x6 u4 _/ h                //              - b8 t6 i" J0 z. ]4 T" q/ d+ U
                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\4 K# v4 N2 P% c- [  R0 O- Y
                // Windows NT\CurrentVersion\Image File Execution Options"
. G9 l. `6 b( U$ ?                // registry key.  If a a subkey entry with the name of
  |/ B& ?. n* n( U                // the executable exists, check for the BreakOnDllLoad value.! L5 O" [# k& o/ N. S4 n/ ~
                //
1 g4 z/ \" r. m. `- ]                retCode = ( K! I% j. w3 N/ Q4 E+ k+ L# G% b
                    _LdrQueryImageFileExecutionOptions(* i$ t4 x) j1 J" W
                                pModuleLoaderInfo->pwszDllName,
# q( R* i/ i/ q7 \. h' t                                "BreakOnDllLoad",pInitNodeArray
3 \# W9 F- Y! w/ r                                REG_DWORD,
/ P  l3 d5 [& S' `: x2 W& ]                                &fBreakOnDllLoad,
, Y- |  h- U$ k8 O                                sizeof(DWORD),
' T) W0 F; d- n0 U, ]* l- ~' [8 L                                0 );2 n9 y; _5 I( U) p  a/ W& f0 v
) |+ H; D0 e( o- k4 G3 P) F3 a
                // If reg value not found (usually the case), then don't8 a% b" V5 q9 I$ z9 O
                // break on this DLL init
. |5 W8 f2 Z. `2 a                if ( retCode <= STATUS_SUCCESS )
* H: A2 E5 y5 D9 N% H7 }5 [- {                    fBreakOnDllLoad = 0;pInitNodeArray
/ Q( a3 @3 G+ @3 O( q" G( @* p  b            }7 k7 T- G, L& ]6 z
            
' x7 r# v3 ?/ X: ?* V) t+ U            if ( fBreakOnDllLoad )% n8 D4 _0 H( N  v7 c. N- }
            {           
: T. g" P7 G3 M0 y. A2 ]; u# ?: F                if ( _ShowSnaps )
/ b8 {# p, i' v" J7 i* f* y" O7 A                {( p* J! S! a6 ^! F4 L) O* A2 p$ n
                    // Inform the debug output stream of the module name
- X+ m6 A  |5 D' k! V                    // and the init routine address before actually breaking$ ^: v: o$ @" {
                    // into the debugger: u! H5 H8 ]. h2 H) Q. j3 |. N
, \/ n" U# ~. I
                    _DbgPrint(  "LDR: %wZ loaded.",
( k- |# k+ Q1 Q* U                                &pModuleLoaderInfo->pModuleLoaderInfo );$ r5 ?( |$ ]. I% O4 E
                    0 v: z: y) z# t
                    _DbgPrint(  "- About to call init routine at %lx\n",
; L; p% d& X- M( ?. x5 d0 z                                pfnInitRoutine )
5 q2 U8 d+ i/ o1 y                }8 N: m: y  M, ~
               
# o1 H+ t1 u& X( f; s                // Break into the debugger                              
: \5 V% J+ @# v( D( u5 y                _DbgBreakPoint();   // An INT 3, followed by a RET# y5 x9 j/ p8 B1 i2 W
            }
8 {7 A- R9 p# @9 d            else if ( _ShowSnaps && pfnInitRoutine )& x) I/ ^0 V7 W
            {: _& U6 T$ T) N% v* c) c. T% \$ r
                // Inform the debug output stream of the module name1 k7 Q( u# I/ n1 Y: l- Y3 Y
                // and the init routine address before calling it               
. ^4 `! T, \0 Q. c5 o! r* Y3 d                _DbgPrint(  "LDR: %wZ loaded.",5 |" Y! S  X8 |7 O
                            pModuleLoaderInfo->pModuleLoaderInfo );+ S4 [+ H9 K  h

; z; Z: s5 Y7 @1 |& C' U4 B8 x* c8 W                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);4 V0 G- O9 z* K6 t
            }
9 U9 w, u+ m& R$ k                    
2 y7 d/ U. ^2 I            if ( pfnInitRoutine )! E' [9 Z" \% D( O1 e6 |
            {
7 u0 p/ w8 e2 ^# G                // 设置DLL_PROCESS_ATTACH标志+ o+ e) j' j' |% B5 x2 z
                //, M6 p+ N( |: I! a1 ^* Y9 ]+ X
                // (Shouldn't this come *after* the actual call?)
1 [( c; |; f& d                //% C$ m3 Q; r5 V) s' n+ K4 v
                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8             / F! I; S7 T0 }3 s7 w, ~
                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;5 l, O! U& `1 E9 p+ N2 j

4 b3 \8 w3 |4 {* b1 f' k' R                //
' Z5 d0 m- l. q. d% V! A2 w                // If there's Thread Local Storage (TLS) for this module,* l6 G6 A) C$ T: w+ q
                // call the TLS init functions.  *** NOTE *** This only" W4 O4 a7 u9 I
                // occurs during the first time this code is called (when
' P/ \& E# [$ G                // implicitly loaded DLLs are initialized).  Dynamically) R3 U' X- A7 m# F, Q
                // loaded DLLs shouldn't use TLS declared vars, as per the
# e; q3 J$ V) H. D, f$ }( S$ [                // SDK documentation. @% ?0 n7 f0 c  o$ l+ r
                // 如果模块需要分配TLS,调用TLS初始化函数! |: ~/ ?+ M2 }
  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时
* g% ^; {/ @4 U0 O  B  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量
% N# W2 C' T2 c2 u1 X                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )) F6 K& ^8 ^( V; R* v1 V- m
                {
, ]" p3 z- `/ e4 D9 U9 |4 y: o                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,
; l+ h) F+ X+ I  \' ?+ f; m                                                DLL_PROCESS_ATTACH );% W# _* g" T9 r) {% C$ O4 F
                }
7 s# p; x* p$ V7 x: \3 {                4 y2 `& t2 i3 g) z4 ?$ b- |( K
# F- v) e! o% i/ L
                hModDLL = pModuleLoaderInfo->hModDLL
( s  H% I3 ]3 U) r4 h: M7 Y. g" n* K! W7 B4 Q
                MOV     ESI,ESP // Save off the ESP register into ESI
, B# g# Q% x1 z  
6 y, ?# p$ Y8 k5 _9 o* ]7 C  // 设置入口函数指针                5 B& d- r) {- S: E
                MOV     EDI,DWORD PTR [pfnInitRoutine]                     
; g- C5 E+ F% C" a
- X" `$ {2 n2 r* K                // In C++ code, the following ASM would look like:
  p4 V, h) i$ c" k  k) S# p                //
0 T7 e9 q) a( m! A; Q4 x. X0 f                // initRetValue =
& i, G$ I" N+ L, k* O, k                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);
3 B8 f6 o+ X( R: v# k7 F4 K                //
) X9 ]9 L' }# m5 D- _* D, S% r3 B6 o' w- V* D1 g: ]: k
                PUSH    DWORD PTR [bImplicitLoad]
" q% \8 [1 _- R                , R) z$ e6 W/ e' L
                PUSH    DLL_PROCESS_ATTACH* y  B. N8 z; k" z
               
, [6 r1 m, X. _                PUSH    DWORD PTR [hModDLL]
7 t" ]' x, [- [6 b- b: a               
' A4 A6 f2 h; o' j                CALL    EDI     // 调用入口函数
% F/ H& S3 a5 W0 R2 n; S) G. |' j$ ?                - `; B; g- Q6 ~, ~
                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值
+ @" I/ d/ b2 _* {
3 o' r" ?; b5 V* a  H( {9 Z                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the
1 V$ g* j8 I$ V: v                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns
# D. Y; Y' v1 [4 \' ^( c& a& |- g1 n+ u( H7 m; c
                MOV     ESP,ESI     // Restore ESP to value before the call
; [7 q0 r! d% }; D# G* g- J: \9 l% x3 t6 o& A5 b1 y0 T0 d, s
                //
. K5 V' x' J5 J* s2 z                // 检查调用前后的ESP值是否一至
) v, C- S7 H; `6 G9 K  // ' ?8 X/ B7 G' J" ]5 k" R6 M4 J2 g
                if ( _CurSP != _SavSP )% E+ ^" N5 k8 z- |, I* y) H$ {8 o
                {/ l+ r4 y  F2 g5 H
                    hardErrorParam = pModuleLoaderInfo->FullDllPath;( p5 ]/ r: `& s! ?+ C3 K

8 |% g1 c9 G# B% Y* t7 s$ B                    hardErrorRetCode = + g( B# x  y0 h* D( J7 S+ p3 t
                        _NtRaiseHardError(
1 q" ^+ ?9 g  o4 G                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,( @. B7 E* e7 g( B
                            1,  // Number of parameters$ |+ r8 _/ L% o" y$ [7 U
                            1,  // UnicodeStringParametersMask,. A+ _$ C  }, `2 R/ a$ j+ s  ?
                            &hardErrorParam,
# M) D+ W0 m& m3 q8 C* ?5 c  g                            OptionYesNo,    // Let user decide
$ V; p& x$ X$ X, W$ H                            &hardErrorResponse );4 t' c9 W; k% }) {" T
                                            + p" t. O1 `: X  e+ m, G
                    if ( _LdrpInLdrInit ): S! P6 y: K, v5 K' o) H. J& ]
                        _LdrpFatalHardErrorCount++;: ~# t$ R8 i  l* Z

' p- X* ?6 N1 Q1 z# f! M; k, s$ Y                    if (    (hardErrorRetCode >= STATUS_SUCCESS): i1 ~0 p) h' j; P$ o' e% y; m
                        &&  (ResponseYes == hardErrorResponse) )9 G& ]3 }6 A0 w* Z3 g2 g( R: ^. }
                    {$ C3 e1 {* X: k' M! R# h
                        return STATUS_DLL_INIT_FAILED;
/ q1 l7 r" L/ \+ [3 c2 w' ~                    }8 j2 U" s$ j: k- G2 M: L, I
                }5 j4 {' p7 ?& ~; o$ M( K& C% v
9 S, y3 e6 ?9 ?& M5 o' p1 s: V
                //- X& {1 P* [( Y" U5 _9 r
                // 入口函数返回0,错误
8 B# k. }9 S  i! ~                //
1 M' @" J3 `! A9 Y7 q                if ( 0 == initRetValue )+ p% Z5 k( x( |
                {+ O: A3 D" c# ^) D( }4 p0 s$ }! @
                    DWORD hardErrorParam2;
3 E& r) g$ B. C5 v  o/ t. m                    DWORD hardErrorResponse2;- r$ }4 V% ?$ e4 l
                                       
  e* }- g: b! \5 c3 u) J# d2 m                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;, M9 y$ Q* {4 h; s- `; t
                    # P- e, q5 d* t" B0 D
                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,
8 T# }' H2 S" e7 `/ q- q! F                                        1,  // Number of parameters
3 k( P5 e& a8 M" k0 m9 u                                        1,  // UnicodeStringParametersMask2 K7 p# z# O$ _6 @6 y
                                        &hardErrorParam2,
, v* G7 A* a- [" [0 ?/ ]                                        OptionOk,   // OK is only response5 H$ T! z1 |. F' _0 Y+ g6 o' P
                                        &hardErrorResponse2 );0 l. m1 ]* U$ N. x7 z+ |4 L
                                                            5 k( k2 O9 a* Z: j. T. j: F$ F
                    if ( _LdrpInLdrInit )2 v/ `: l/ z, q3 ^7 y
                        _LdrpFatalHardErrorCount++;
+ p- T; p) J7 C% p! x
5 Y3 j4 h1 R9 r) ?1 `6 Y                    return STATUS_DLL_INIT_FAILED;$ v: L1 G1 a: g# }$ g
                }
6 u( r  v) G  M3 y/ q' [6 @0 U3 G            }
6 ?( ^. V  M* [- r& r+ y        }5 P* i& _. p( d/ O/ P: P% O9 p" V

& D: @' l# `/ M/ L) z        //  q7 I8 U9 B- h4 [
        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时6 M. D: G% D" {: I/ ]& w* L0 |
        //      
+ M. `. ?+ r, t7 ]" m: }        if ( _LdrpImageHasTls && bImplicitLoad )/ K4 Z: C6 t0 s! m
        {
/ G. W1 ]* w" n9 o, T; U            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,
/ Q0 d+ ]# R0 y! y  Y# F                                        DLL_PROCESS_ATTACH );
  B, V5 e1 G+ t# ?$ L+ E& c        }
: o5 L: \) t5 r5 Z" z( k    }
: U1 n* z1 O! I( ^3 Y    __finally6 a8 g% M; x* U0 v$ ~  d* T
    {
$ q& `1 \* d! I$ i! E) a0 g        //
4 f6 T0 d' w# I2 X- K        // 第四部分;
6 p( |7 Q; ~$ G4 j) v9 R        // 清除分配的内存, r) x) {8 e+ M* k% r% f/ E. j* T
        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );5 F$ ?; |5 c- ?# W# n* k3 n7 }7 S
    }
0 }8 G4 O% z3 K% G6 q3 D0 J
8 a! J$ y0 G2 M; w: O    return STATUS_SUCCESS;
8 v8 R, T0 F- t& z9 m$ X6 h1 w}   
0 _" M9 D8 B6 [) N2 R' c- F8 i% L) t1 `+ C1 L  e
这个函数分为四个主要部分:
2 Y1 P: p* l6 t2 M$ p一:第一部分调用_LdrpClearLoadInProgress函数,这个NTDLL函数返回已经被映象到内存的dll的个数。例如,你的进程调用exm.dll,而exm.dll又调用exm1.dll和exm2.dll,那么_LdrpClearLoadInProgress会返回3。得到dll个数后,调用_RtlAllocateHeap,它会返回一个内存的队列指针。伪码中的队列指针为pInitNodeArray。队列中的每个节点指针都指向一个新加载的dll的结构信息。0 Q' l2 V) J, q% H
二:第二部分的代码通过进程内部的数据结构获得一个新加载dll的链接列表。并且检查dll是否有入口指针,如果有,就把模块信息指针加入pInitNodeArray中。伪码中的模块信息指针为pModuleLoaderInfo。但是有的dll是资源文件,并不具有入口函数。所以pInitNodeArray中节点比_LdrpClearLoadInProgress返回的数目要少。: @0 o" F! n( M" B) Q) S
三:第三部分的代码枚举了pInitNodeArray中的对象,并且调用了入口函数。因为这部分的初始化代码有可能出现错误,所以使用了_try异常扑获功能。这就是为什么在DllMain中出现错误后不会使整个进程终止。
( H9 |. b. h* R另外,在调用入口函数时还会对TLS进行初始化,当用 __declspec来声明TLS变量时,链接器包含的数据可以进行触发。在调用dll的入口函数时,LdrpRunInitializeRoutines函数会检查是否需要初始化一个TLS,如果需要,就调用_LdrpCallTlsInitializers。+ x8 ^. z, E7 Q/ n' Q/ G+ @- {. ~! D
在最后的伪代码部分使用汇编语言来进行dll的入口函数调用。主要的命令时CALL EDI;EDI中就是入口函数的指针。当此命令返回后,dll的初始化工作就完成了。对于C++写的dll,DllMain已经执行完成了它的DLL_PROCESS_ATTACH代码。注意一下入口函数的第三个参数pvReserved,当exe或dll隐式调用dll时这个参数是非零,当使用LoadLibrary调用时是零。在入口函数调用以后,加载器会检查调用入口函数前和后的ESP的值,如果不同,dll的初始化函数就会报错。检查完ESP后,还会检查入口函数的返回值,如果是零,说明初始化的时候出现了什么问题。并且系统会报错并停止调用dll。在第三部分的最后,在初始化完成后,如果exe进程已经拥有了TLS,并且隐式调用的dll已经被初始化,那么会调用_LdrpCallTlsInitializers。
9 i5 x8 p: Q3 d+ R四:第四部分代码是清理代码,象_RtlAllocateHeap 分配的pInitNodeArray的内存需要被释放。释放代码出现在_finally块中,调用了_RtlFreeHeap 。
您需要登录后才可以回帖 登录 | 加入计匠网

本版积分规则

Archiver|手机版|小黑屋|计匠网

GMT+8, 2025-6-16 05:17 , Processed in 0.049331 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表