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

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

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url]& t; [4 i, B7 N- V
; c, x" Q8 C9 u0 I
WINDOWS 2K Dll 加载过程6 _4 x$ v9 O9 r4 i; Q' x
jefong by 2005/03/305 G# k  F6 s5 y! M2 @- U3 {
这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。
* \: ^8 Q! o* V+ m- E$ F8 I' G在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”。# x! g1 ^9 s( P$ [! A
你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。
! c& U* ]; j$ `5 ~" [初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。$ f/ _' l- `6 Z1 ^" k
  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。
+ F( g  O- f4 g! Z0 G, [  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。
9 p% X9 ~; s$ e+ P% u6 ]6 F4 ]7 a6 }  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):2 J0 [' a: x. G
//=============================================================================/ I. s" \/ o! L* [' `# o! M8 s
// Matt Pietrek, September 1999 Microsoft Systems Journal; L; Z3 t  I( J0 h5 @6 f6 c. i7 `
// 中文注释部分为jefong翻译
1 i4 t- K5 P/ h" ^+ r//# J) T3 `: K6 q- x5 a, Y
// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3)+ o' S5 F2 E) x. m- w" c
//+ \* H9 v$ U% n) p3 g8 ]
// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;+ v& L7 ?! x0 u/ T
//=============================================================================
- M( ^: ?+ b' A5 J: N. }- Q$ ?7 U8 P
#include <ntexapi.h>    // For HardError defines near the end
) D. a: Q: F, ~
2 C8 c) H! X$ C// Global symbols (name is accurate, and comes from NTDLL.DBG)
. Z4 i  \  K! q//  _NtdllBaseTag
2 f# ~1 V2 _( O" V; l5 m7 M//  _ShowSnaps  B3 |2 B' ]' ?. G2 F
//  _SaveSp
& B4 Q9 w" k' o, @* A//  _CurSp
* J) I  _( E- Z/ ~" O* w//  _LdrpInLdrInit- d; [. h$ p$ C! T& u# A5 C. T" t: K
//  _LdrpFatalHardErrorCount
2 F0 K# u7 i% Z6 Q$ Z( c//  _LdrpImageHasTls" h; s. O: l2 E& x0 K

8 e; Q% n& f. ~6 PNTSTATUS
3 i9 }0 p( Y# \7 a2 kLdrpRunInitializeRoutines( DWORD bImplicitLoad )
; c. ~/ n7 @9 \% ~  c{
: w3 a' d$ {5 P6 U/ J, H* `    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了
, L+ n- v! w3 b6 e    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();9 |+ I" K* w! f! {( o8 ?
% f+ s* _1 |5 X, q  U4 Y6 q
    if ( nRoutinesToRun )
2 [" a7 m5 J2 s+ ?& }1 s8 V$ `    {
9 z3 {4 a" W2 A( |$ O( F        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。0 v& I0 ]& J/ j1 M8 G) P
        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),
/ m% k/ v. q* M/ D+ D9 j8 K0 S  C                                            _NtdllBaseTag + 0x60000,3 O& v5 m& m$ W: [% c. v  Q
                                            nRoutinesToRun * 4 );
8 v6 `2 S# x+ }7 F9 b1 }                           
: Q+ p9 @+ y0 C2 V3 C        if ( 0 == pInitNodeArray )    // Make sure allocation worked- J' f/ U. s/ F# x' m/ h
            return STATUS_NO_MEMORY;# o& A% o* {4 R+ E9 I" Z3 ~" ^
    }
3 [! R" t; Q/ r/ V0 Y    else* {+ S9 \1 _! Y! e3 U
        pInitNodeArray = 0;2 b( B. c: Z& i, w  ]
0 f4 e( G; s! B% I4 |0 B: \
    //第二部分;
, M6 v: G" }8 P) f    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。6 ~' S5 p3 I* C
    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);0 L1 O3 N4 q6 M3 `# O4 @1 f' U2 q
    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;
/ h0 _7 f5 ~/ `2 r        , |4 S* d# C8 d/ o' l3 h" L
    if ( _ShowSnaps )0 C: _$ A8 l+ P  m
    {
4 ^6 K) Y- l+ w4 [1 e& M' a        _DbgPrint( "LDR: Real INIT LIST\n" );
9 D% Z5 z4 i3 ]. @4 X3 \& h    }: t: k- l7 {( B. z3 ?

) a: G, d7 n2 `$ l    nModulesInitedSoFar = 0;
( z9 `/ s. G( ^3 H* V6 _* n
, {9 c0 [1 H- v6 f$ ], h    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块
* Y; z7 g0 e' J( |0 t    {
! A; q! o4 B/ [        / ^4 {8 m% D( E' I+ r' e
        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块, \% g, S6 C9 r" |
        {
' f2 X1 A2 T$ W* ]3 ?, f% [            ModuleLoaderInfo  pModuleLoaderInfo;7 V7 F& Y3 U0 |" ?8 Q! |! o
            4 F3 f% @# K; p5 W1 O4 i
            //  m* C. _+ v: Z5 }2 j; f
            //一个ModuleLoaderInfo结构节点的大小为0X10字节1 q5 M  X1 X! R( S
            pModuleLoaderInfo = &NextNode - 0x10;
2 x3 i4 v* U2 v8 Q0 E            
0 J; W+ f# x- ~$ n! R* y            localVar3C = pModuleLoaderInfo;         
; |2 w3 A0 R; j4 ?% ?( {% D+ ^- e
            //, O9 p6 z& T* z5 E+ P
            // 如果模块已经被初始化,就忽略
( @" A, c. ^/ c4 T3 V3 y% s; T            // X_LOADER_SAW_MODULE = 0x40 已被初始化
! E1 B0 m  ?/ U$ |            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )
6 E+ u9 Q. w. ?7 w) d% \            {
, A4 |5 H/ k4 P! I$ e* y6 _3 h                //+ {( u/ r* M9 q  G# L
                // 模块没有被初始化,判断是否具有入口函数
0 g- _5 |4 i$ O                //- S2 I/ p# n. G! T" M) \' _
                if ( pModuleLoaderInfo->EntryPoint )
! G6 ?! z" K- T& n; }                {8 x' X+ k, Q5 z
                    //0 N2 }: B" b  O1 O& C2 ~; l8 v
                    // 具有初始化函数,添加到模块列表中,等待进行初始化
  A) j$ D3 f; V( x( l                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;" ~; Y" E% Z! `' f
9 x$ x1 G2 T, S0 S4 B
                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址
; @4 E9 X  f$ ^/ ?      // 例如:
( C2 k# r2 Q  z6 q                    // C:\WINNT\system32\KERNEL32.dll init routine 77f01000
5 M. z: U! i& A2 i& u- U                    if ( _ShowSnaps )
9 k6 r0 j% s0 }9 K- @                    {( P9 j, [# d! E+ A4 p8 d3 Z
                        _DbgPrint(  "%wZ init routine %x\n",
8 M$ C* d7 {" M0 Q) P0 |5 o) Z                                    &pModuleLoaderInfo->24,+ d( a8 C$ E. R5 x  \% J; k
                                    pModuleLoaderInfo->EntryPoint );
1 U1 u% E+ i8 I; q                    }
% _# J( K) [0 |1 f: i* R% d: b7 W4 v) i& |1 `& X
                    nModulesInitedSoFar++;
; ?: A& G3 C/ `+ L8 B                }
8 P& R& S( z2 {( t5 s            }
# c) u8 h- c% b' P: R' B" Q( i$ e- U/ k# e$ w! a9 X  O2 e7 E
            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。- |* L; \; g  o7 `
            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;) `% t. C$ a5 K% A

; c2 `- G8 V- Y) r1 p) ^1 {            // 处理下一个模块节点
" r8 X' {9 L) {7 l            pCurrNode = pCurrNode->pNext
) n  b5 l6 v1 y8 E1 D9 R+ X! }        }
# l6 @! R" y4 |0 e5 \    }
! @2 P. Y+ `2 ]0 M$ P    else
" Y" `3 n; J) h0 X. b    {
8 v( j, n% s# }% O* ]        pModuleLoaderInfo = localVar3C;     // May not be initialized???
1 B+ e  t% u! f! C9 ~9 o  ]* \, Z* u/ t    }
( m3 }6 t- f( M* s    , R! u  b+ ]# a! |/ n
    if ( 0 == pInitNodeArray )( n* q6 ]6 E( E$ ^" c, [# [
        return STATUS_SUCCESS;* _2 N; ^4 Q. W# U+ c

5 k! `" ~% ~3 [# w    // ************************* MSJ Layout! *****************9 O6 @) _" @  R  K$ {
    // If you're going to split this code across pages, this is a great7 B; S4 W! [& s  k$ O0 d( O; B
    // spot to split the code.  Just be sure to remove this comment) y" K6 V4 D! B1 I
    // ************************* MSJ Layout! *****************" y( c  n9 G4 u6 N/ a
   
! w9 j% F- n& H0 W# @7 |* w    //
5 D3 P. V( ?  N7 f0 h( ~    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH$ b% U3 l  v3 _1 G: }4 N8 u0 z0 h
    // 第三部分,调用初始化部分: s. `7 X2 N# W# C" u' R7 }  L$ h- ?4 s6 S
    try     // Wrap all this in a try block, in case the init routine faults
, `- a; }- U/ ?1 S    {0 i6 U, M3 L" D9 |' d
        nModulesInitedSoFar = 0;  // Start at array element 0
  o# g& B& O3 y; `( U4 O/ d& D
1 D! ^9 L: R( \! @        //
5 V) l5 H% X' s        // 遍历模块队列
! ?5 i7 d! P$ x7 i, S4 A) [        //' l3 s+ n4 f: P* f+ o% _$ l
        while ( nModulesInitedSoFar < nRoutinesToRun )
3 D2 N  d3 `2 `( D& L, O        {
3 x! e- Z& `/ [* }* d            // 获得模块指针- U1 j2 |9 v! O6 A# `2 W! W/ M
            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];
) ~8 ^4 s9 Y( K* v% h7 I
4 I8 V& y4 }9 d' Y4 R: O            // This doesn't seem to do anything...  K0 l! c9 G" D+ x8 O" i
            localVar3C = pModuleLoaderInfo;
- I9 N" H' W3 C7 n5 _              Y* `, H$ B, h2 v. Q% p+ c
            nModulesInitedSoFar++;. _# `0 z" M; _- n# A. ^; @
                5 I5 R/ D3 v+ }
            // 保存初始化程序入口指针
: j) h, N5 ^8 S            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;& f2 H3 y* O: L; [' m( j" n, N
            
* z9 q) y/ B  d6 u5 B) Q# Y6 b/ q            fBreakOnDllLoad = 0;    // Default is to not break on load* m0 F) [" z( |  W% ^8 L0 ?
8 h6 ?# m" F+ T& |' L: W2 y% A
            // 调试用- L2 X& v1 u1 ?& R4 c8 ^9 d( y
            // If this process is a debuggee, check to see if the loader
1 [. o1 q; L2 ~$ \& M            // should break into a debugger before calling the initialization.
1 O0 c) s9 O+ l            //* ~% `7 c- C# ]
            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()
4 O7 T4 D7 Q, Z* Y6 q            // returns. IsDebuggerPresent is an NT only API.3 ], u/ j; {- Z  c/ e
            //! {4 l; l% V& Z  f" |+ s
            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )
$ |" V8 S, d" p            {! G1 l& V! k2 k# z0 ~* @9 v& a" K: `
                LONG retCode;
: u, ^% ]- x5 ^$ e; Y* G- E
4 \/ S1 n3 y0 C5 A                //              
3 i7 Y) f, r* w" Q3 I9 k/ J                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\# |2 y( G; E0 x3 ?0 L
                // Windows NT\CurrentVersion\Image File Execution Options"& r' C3 Q3 d/ V% D5 S& x) X
                // registry key.  If a a subkey entry with the name of
( `* z. F0 L- H                // the executable exists, check for the BreakOnDllLoad value.+ P5 ?" ^) b0 F$ F. B  m
                //; X) M8 E  E7 K; Z% L) v
                retCode = 8 i4 W- t* t: ]/ v) d
                    _LdrQueryImageFileExecutionOptions(% W. w- d8 X; ^8 e
                                pModuleLoaderInfo->pwszDllName,# h( _+ N6 V$ q2 d/ a. P
                                "BreakOnDllLoad",pInitNodeArray: t- y/ u! f+ F1 Q0 K
                                REG_DWORD,8 w8 `4 l, E, u3 c7 S
                                &fBreakOnDllLoad,
/ g- f9 k9 u' i( x2 `0 [# O9 ^) M/ v                                sizeof(DWORD),
# T# z9 M6 ]  @( m                                0 );: _4 ^) h) C) H+ r  F" H9 B
; v& ?2 f4 b3 k5 P3 V
                // If reg value not found (usually the case), then don't/ d/ Q( E+ B* i$ j% N5 l
                // break on this DLL init6 U# A: o4 y, G; \
                if ( retCode <= STATUS_SUCCESS )0 T$ R0 b) m# P/ s) y4 r5 F
                    fBreakOnDllLoad = 0;pInitNodeArray( m+ S0 G: @% h* n" p4 [' q
            }) v- p7 ^- _" N' b2 r( j5 `6 N
            ( D' ?1 ^# q0 {/ _) T
            if ( fBreakOnDllLoad )
8 ?1 M- J) v$ Z; ]0 G3 L            {           8 D0 h( C% B7 V1 m+ D
                if ( _ShowSnaps )
" s. a" \& t7 ]6 \7 q0 {: Q7 {                {
* Q  Q* _  k) R4 ~; i  |                    // Inform the debug output stream of the module name
* d5 C& T$ ?: u" Y                    // and the init routine address before actually breaking
9 z) e( t+ K+ n, U: V                    // into the debugger5 l% @# ?5 ]* W0 ^! l" H

5 m9 T& z2 k" T) v; h% [# n                    _DbgPrint(  "LDR: %wZ loaded.",7 u6 D. F/ T, e& D& K
                                &pModuleLoaderInfo->pModuleLoaderInfo );
7 `- i5 X+ H6 `- [( a/ H+ X                    
& l  z$ S3 g% {# i) b  N2 u                    _DbgPrint(  "- About to call init routine at %lx\n",9 W8 u+ d! P( {
                                pfnInitRoutine )
0 f/ o) u3 ~2 I- `5 A: v# l                }
+ u% g! Y. Y; H0 J0 r$ n               
* ?  C! }( w, d  g                // Break into the debugger                              * ~+ s  M; a$ h- z9 V& m
                _DbgBreakPoint();   // An INT 3, followed by a RET) {; k, U; w1 d* U  Q% k
            }7 Y; F4 P8 y! ]' X
            else if ( _ShowSnaps && pfnInitRoutine )
: U1 \6 i- u) {5 k; E            {
8 |( P2 r6 g( ]3 s1 B                // Inform the debug output stream of the module name+ @: Z9 \$ `9 X
                // and the init routine address before calling it               , H& A1 s  T& Z
                _DbgPrint(  "LDR: %wZ loaded.",) q5 s3 @+ A6 s6 E
                            pModuleLoaderInfo->pModuleLoaderInfo );! x0 a6 k/ b1 Q* O& ]8 |5 ]

7 K9 K% m+ C7 b$ Q                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);
- g( v( L3 x# K5 y1 J            }
7 G4 X+ R+ t# f4 M( D- e" g. l                    
& k$ G- Y2 V! c3 a( y* W% l$ t( j* c% I) }            if ( pfnInitRoutine )3 j& h7 u5 E: C. t
            {
3 Q2 V+ O* u$ }5 ^! o+ A$ I9 K                // 设置DLL_PROCESS_ATTACH标志- |8 l' ?8 n7 q5 p" E# S5 @% u
                //
4 T6 e+ ?* Z! t5 ?; U                // (Shouldn't this come *after* the actual call?)* i/ f8 G' x5 o. m6 h* x2 T: ]5 w0 b
                //$ b$ \! h4 U* q1 _% a( A) s
                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8            
) s& s" `; t% G* p                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;
$ f) l! R7 Y4 Q! B; q1 x. |5 B/ T" Y+ Q4 s0 j  j6 F  G+ ^
                //9 `8 L" _4 d4 Q, b  R
                // If there's Thread Local Storage (TLS) for this module,
: v) b$ `: n' i                // call the TLS init functions.  *** NOTE *** This only
% D2 h2 R# o  L6 Y3 d# s                // occurs during the first time this code is called (when2 e7 E2 ^  Q& B* L9 X1 o
                // implicitly loaded DLLs are initialized).  Dynamically( F$ H; i% k2 Q7 H3 w' a' ]' t
                // loaded DLLs shouldn't use TLS declared vars, as per the
. P4 W# i+ ?' [, l8 E) w                // SDK documentation/ e* l+ Z& h( g: G4 U* R0 T  @
                // 如果模块需要分配TLS,调用TLS初始化函数9 L+ x! u4 p% E# {1 I) N
  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时
  y$ w" u8 {% z4 I4 n$ |  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量
7 S. m: M$ c3 l% M7 Y2 l                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )
; Y5 v; N6 w. ?; c                {6 m7 i5 N1 e1 K! Z+ C
                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,# x; o" i5 W5 g
                                                DLL_PROCESS_ATTACH );
' [$ ^6 J% b6 F$ l7 ^$ y; [  Y' o                }7 o  R, u% [! h
               
* E/ I8 _  t2 t+ }/ ]3 U
$ `1 u8 y( F) i6 ~. |+ E, C2 z! H                hModDLL = pModuleLoaderInfo->hModDLL
9 P6 B# U% Q+ y* u, E' V' A/ \( S. t9 |6 X: G  p
                MOV     ESI,ESP // Save off the ESP register into ESI5 H2 q2 X2 ?+ Y& [  t: h* \6 k
  
7 t% p4 A: X, m+ X  // 设置入口函数指针                " O+ q$ @4 D/ E& v
                MOV     EDI,DWORD PTR [pfnInitRoutine]                     ; Q) a! b+ Q! @" M

; s" c4 d3 L! f                // In C++ code, the following ASM would look like:
5 }" K7 V1 r0 H! P1 y+ f9 J1 X5 @                //0 k! |5 Y0 f3 F4 B
                // initRetValue =
5 H: C9 W( S% t4 W4 H$ D2 u1 K                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);9 n* W/ ~2 q) ~+ Z3 ^
                //3 t, {* v( @) Z2 d0 w# h3 ]& l( {; y( ]# j
/ y# B! {( R4 r- o8 H" S$ x
                PUSH    DWORD PTR [bImplicitLoad]' f" {- H; W8 O+ u, M
                8 A9 `+ N1 H* o0 w0 W/ N% w& X  v
                PUSH    DLL_PROCESS_ATTACH5 o) V. X& u7 V2 F
               
' ~1 m$ F- |. g5 i$ e                PUSH    DWORD PTR [hModDLL]
# g; n& m; D4 j% T; y; c                ) r. t) d2 S$ K6 M6 O5 E) Q5 u
                CALL    EDI     // 调用入口函数
( _2 i0 V: V6 N8 v* H: v% J( v& e$ m               
7 e3 N9 f- v, P7 q5 o* W1 B                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值+ c! n' O" h2 u1 T' u4 V! u2 L; j. T
5 W. Q& E* n2 |& i/ Y
                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the
/ h& D( {0 ~# I9 d  ]5 n                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns
0 [* w4 N* O- s' `0 ]1 K" |# ~# [3 z, x  B! ?
                MOV     ESP,ESI     // Restore ESP to value before the call" g* b7 F; w# R7 i2 d
' ^( c- W$ S; m! U/ r
                //
5 L. ?, B* z7 L                // 检查调用前后的ESP值是否一至
$ t/ ~8 M! G. R0 T. g- ?! R  // 6 t9 Q  A; ~5 n( a4 I% z
                if ( _CurSP != _SavSP )5 ^1 _2 k* a' p4 K
                {3 G' X, X( D' C5 S
                    hardErrorParam = pModuleLoaderInfo->FullDllPath;7 y0 a* T  \! t

( u: H  `" D- q8 [1 ^2 I                    hardErrorRetCode = # @; D. s% ?! E2 q" q& j. Z
                        _NtRaiseHardError(4 l+ f' G- n2 [; p& \- e
                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,
0 K8 S+ J0 w# L7 \; V2 K% V  l                            1,  // Number of parameters
3 z9 E* w9 n6 S' s0 H& j, s/ T                            1,  // UnicodeStringParametersMask,
) ~. ~6 b/ [5 k$ L                            &hardErrorParam,
1 p: Y3 h5 M, H# Z5 N& c                            OptionYesNo,    // Let user decide9 z+ R8 U  E& a  S' o
                            &hardErrorResponse );
  R+ w; c) ^6 {! o                                            ( h5 f  c( _; ~$ j4 y) \; f) L
                    if ( _LdrpInLdrInit )
  X1 M1 M; D7 Q; F                        _LdrpFatalHardErrorCount++;6 u) F9 z3 ^, B0 U! X/ r5 l# ^
: l5 g: ~* t; n7 h1 s4 O* k
                    if (    (hardErrorRetCode >= STATUS_SUCCESS)- B) T2 ~3 E5 C# r& {
                        &&  (ResponseYes == hardErrorResponse) ), R7 Y0 z, }  f) b/ y
                    {
' c3 z8 }( Y! m" n; g  m                        return STATUS_DLL_INIT_FAILED;/ F0 E* }1 }2 ]- t5 a3 \
                    }. L. o5 K" j8 I* ]3 O* ^2 |. t
                }9 _4 I. O! o  N+ R, m
8 d1 a0 o2 g6 y* q
                //7 i5 O& `. [' m. R* x
                // 入口函数返回0,错误& b( {, M5 [; V$ b) k
                //. [. q0 S+ G2 j4 j$ E$ t4 c
                if ( 0 == initRetValue )
" w3 j) ]. o& I' M! Q                {
$ _8 N) a, f2 `3 K                    DWORD hardErrorParam2;7 p. m* j4 Y9 i+ Y4 e+ n7 _  r3 D
                    DWORD hardErrorResponse2;  G1 R# G" X8 s3 t, \
                                          Y7 [: ?6 ~4 a1 B/ r$ k2 V3 A
                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;
0 D4 l9 l7 R# x" o/ x) D9 b# I                    
4 m; L( L% m7 y5 Y/ V% ~                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,
) Y4 j4 r& e2 T8 ?* V+ Z' f                                        1,  // Number of parameters6 a; R, Y: }7 r1 C7 c$ W* g
                                        1,  // UnicodeStringParametersMask2 C- V* |& Z  v. P
                                        &hardErrorParam2,
$ r* v! }5 P/ k# q/ b                                        OptionOk,   // OK is only response% @' Z/ D9 L6 _2 Y+ m1 E# f( h1 U
                                        &hardErrorResponse2 );2 ~2 y3 @; }- H
                                                            ) a3 k# b$ `9 L  o: I7 H& F$ X5 w0 w
                    if ( _LdrpInLdrInit )2 L4 h2 I- q4 P* \1 q
                        _LdrpFatalHardErrorCount++;
- X: a% j0 s7 W: S) |
6 {1 [+ R- B7 z. ~7 u8 O) u% Q                    return STATUS_DLL_INIT_FAILED;# d% U- O0 k* ]2 k9 F, p
                }# c$ i. L, w) g9 D) }4 T
            }
0 n% z+ i% H$ {' {8 `        }
3 |; c0 c6 }) J( ?/ N! u$ M
, x  Y1 A3 @% ^7 |  \- F  f        //
; W3 I4 d# d% \  ~) ]: P- y4 P        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时
& W. W; ]3 ?3 R4 _8 `# ^6 f. n        //      
% q% x6 T$ L; Y8 V        if ( _LdrpImageHasTls && bImplicitLoad )! _& p# C, k0 V+ V0 J
        {
  h' r- d7 y, h4 F7 D, r( g5 [9 u            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,
) i" }& c1 B! k: T8 T; D                                        DLL_PROCESS_ATTACH );, ]7 X% I  A: Y  U+ e
        }
1 p+ G* O  K) e0 \8 U3 G, X& d    }, J8 Q5 V/ L7 c2 L. v; s) B
    __finally
/ v5 K, A* g$ ~+ q  l    {7 {3 z2 _% f, p9 U- U( O
        //
- ], m/ _' n6 h6 |# s/ B$ r6 q5 ^        // 第四部分;# ^& ~0 `* L/ |9 V! _8 t7 F  C
        // 清除分配的内存' ~- W3 X, f- R7 ?. ]; q4 i6 w
        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );
# b* N; \3 G* h- [$ }9 k/ }2 b    }
( P8 i" I) P8 X1 [; U. i
, z# R* q. S1 X    return STATUS_SUCCESS;1 O( X* `4 n! a" f4 r7 X/ |$ s
}   + z2 n5 s5 x+ U: q1 C7 @
: N3 w+ U5 g6 B7 C
这个函数分为四个主要部分:
, z2 f6 X, e3 j一:第一部分调用_LdrpClearLoadInProgress函数,这个NTDLL函数返回已经被映象到内存的dll的个数。例如,你的进程调用exm.dll,而exm.dll又调用exm1.dll和exm2.dll,那么_LdrpClearLoadInProgress会返回3。得到dll个数后,调用_RtlAllocateHeap,它会返回一个内存的队列指针。伪码中的队列指针为pInitNodeArray。队列中的每个节点指针都指向一个新加载的dll的结构信息。
, M, o9 m6 h+ B( M4 [二:第二部分的代码通过进程内部的数据结构获得一个新加载dll的链接列表。并且检查dll是否有入口指针,如果有,就把模块信息指针加入pInitNodeArray中。伪码中的模块信息指针为pModuleLoaderInfo。但是有的dll是资源文件,并不具有入口函数。所以pInitNodeArray中节点比_LdrpClearLoadInProgress返回的数目要少。& g! Z9 \* c* E" C' y  [
三:第三部分的代码枚举了pInitNodeArray中的对象,并且调用了入口函数。因为这部分的初始化代码有可能出现错误,所以使用了_try异常扑获功能。这就是为什么在DllMain中出现错误后不会使整个进程终止。1 v+ \% r! O) g3 \; x4 g& ]
另外,在调用入口函数时还会对TLS进行初始化,当用 __declspec来声明TLS变量时,链接器包含的数据可以进行触发。在调用dll的入口函数时,LdrpRunInitializeRoutines函数会检查是否需要初始化一个TLS,如果需要,就调用_LdrpCallTlsInitializers。! t% O2 d: z# q, l. _, M
在最后的伪代码部分使用汇编语言来进行dll的入口函数调用。主要的命令时CALL EDI;EDI中就是入口函数的指针。当此命令返回后,dll的初始化工作就完成了。对于C++写的dll,DllMain已经执行完成了它的DLL_PROCESS_ATTACH代码。注意一下入口函数的第三个参数pvReserved,当exe或dll隐式调用dll时这个参数是非零,当使用LoadLibrary调用时是零。在入口函数调用以后,加载器会检查调用入口函数前和后的ESP的值,如果不同,dll的初始化函数就会报错。检查完ESP后,还会检查入口函数的返回值,如果是零,说明初始化的时候出现了什么问题。并且系统会报错并停止调用dll。在第三部分的最后,在初始化完成后,如果exe进程已经拥有了TLS,并且隐式调用的dll已经被初始化,那么会调用_LdrpCallTlsInitializers。
+ [. L: B* a8 x9 V" I四:第四部分代码是清理代码,象_RtlAllocateHeap 分配的pInitNodeArray的内存需要被释放。释放代码出现在_finally块中,调用了_RtlFreeHeap 。
您需要登录后才可以回帖 登录 | 加入计匠网

本版积分规则

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

GMT+8, 2025-5-2 02:36 , Processed in 0.029231 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

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