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

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

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url]
( }4 c# x+ V1 r' i1 t7 W) C; v6 o' O+ g; U9 P7 T
WINDOWS 2K Dll 加载过程
. I+ v. v$ x4 Sjefong by 2005/03/305 F8 V" @: \7 I# T% J6 Z: }( X" Z
这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。# X, w  w) Z% H0 k
在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”。
8 Y9 k$ C; v/ i2 w! l/ }- ~5 G你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。: P* L% p" Q( E1 R
初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。6 g2 |% _, ^) T, c
  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。
- }( `1 q* w( N6 u" [4 w) |  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。, W9 n7 C( }, n9 C
  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):) w! [7 _: X% g5 s8 z
//=============================================================================
, M7 |7 U! J; S! Y7 p9 [! o// Matt Pietrek, September 1999 Microsoft Systems Journal" n3 B1 X5 h& v) i
// 中文注释部分为jefong翻译
: @3 \0 z8 ^7 j( A$ M, W; i9 t//- C3 o' z. e" S& y/ W# d
// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3)
/ t, U( ]  W& _//
/ K& A1 I8 p- ?& F5 t9 X// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;
0 h- z* L: z' O, E8 p% i7 j//=============================================================================  J. Q6 n9 Y$ h5 I, d

: X8 t; @3 @2 Z: O+ |" |#include <ntexapi.h>    // For HardError defines near the end
" E* w( t* Z9 ^# a# C3 l1 d9 ~
/ u. P+ D9 o# o3 s3 M% n) m$ `// Global symbols (name is accurate, and comes from NTDLL.DBG)
1 t$ d2 ?3 {: [//  _NtdllBaseTag
8 O4 O" a& t; o, I//  _ShowSnaps
6 t+ M6 X" n& ]0 p9 }//  _SaveSp; m4 Q9 r  R1 B+ S' H7 }
//  _CurSp, j/ h+ l. w! M, h4 e( W9 N4 V
//  _LdrpInLdrInit& f+ y) d8 C' q  n/ \1 _# I
//  _LdrpFatalHardErrorCount
# n: H4 T4 n0 C/ x) {% @& j9 |//  _LdrpImageHasTls
/ y' g. S7 ]1 l5 v. O9 N7 z5 A# c( M2 n' L6 Q
NTSTATUS
$ D) h' e5 [2 s& fLdrpRunInitializeRoutines( DWORD bImplicitLoad ), h' @/ y1 V9 n3 h5 p% b( P
{: A$ {' t1 ~  F, Y$ c
    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了) |8 E4 e' z" u2 q; ]
    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();
+ ^. K; g: ~/ y: p" ~! |2 ]0 X' b. ]( e& f
    if ( nRoutinesToRun )
2 ~% ?1 S3 b& c/ w+ T    {+ P+ I! e2 |3 `" x  I- q
        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。
* I3 n$ `0 j1 \/ g! @3 a        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),: ^& c# z% i8 P% {8 v3 @. q+ }6 Z
                                            _NtdllBaseTag + 0x60000,1 `. o9 ^) h4 x, C& C
                                            nRoutinesToRun * 4 );
' e0 e* w8 C3 t! J" e3 u                            % S! _% v6 V$ m- j# |1 b1 X+ F  P
        if ( 0 == pInitNodeArray )    // Make sure allocation worked
7 T; s# n6 ]( n8 |2 V+ T            return STATUS_NO_MEMORY;
9 Z8 i6 x( R" K    }
$ H7 H) M' R% H+ k6 _    else% s, M4 _! q, \, \3 w4 f- j
        pInitNodeArray = 0;/ f- v1 `- W5 h/ v
" E+ l) _" @. X$ j* _
    //第二部分;7 n# q3 H5 h, S$ g
    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。
3 p8 S6 [" F% G' ~7 ^9 {1 l    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);
' ^% @6 N! A" J% ?4 D: L4 g    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;& r2 c3 ^2 L. h/ o- v
        2 L7 m7 I" ~! r4 {
    if ( _ShowSnaps )
) Z: W/ T$ p, _. L$ j+ Q    {
; z8 b/ J1 s: t+ s+ n        _DbgPrint( "LDR: Real INIT LIST\n" );0 }3 W) W' d; b4 `
    }
) m& I4 S8 k0 {; j7 ~3 ]: H. c7 x" w9 ?# l4 ?6 Z7 p/ Q! k# x
    nModulesInitedSoFar = 0;
) J3 f$ T7 [: K# y; O) {3 W3 G0 b9 e+ N- y
    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块; z; X2 W4 q9 j( x$ ~
    {& ?) m% N- S* P2 t, A7 x
        - D  k+ k8 d- P' d# x* I; l8 P! ~
        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块7 `: M% [4 A) r5 y5 y9 o& s
        {
, L7 _7 ?$ L, S+ z% B$ g! q            ModuleLoaderInfo  pModuleLoaderInfo;) E7 I2 t: T3 v! K, C: B
            9 V9 \* x; |; s6 K' Z7 }+ u; h
            //# @+ s. Y' o0 j4 w: S, i' }
            //一个ModuleLoaderInfo结构节点的大小为0X10字节
8 _) K& E  Z  {) J. G8 z            pModuleLoaderInfo = &NextNode - 0x10;! ^. l6 y$ H4 e; }: \
            
$ B. J' H& d. w            localVar3C = pModuleLoaderInfo;         
& @! M. b: ?+ L$ @
1 F% F% r% q  ?1 \9 B# A; Q2 ?            //
6 d& Y. q9 J1 W- F9 Z2 m! E            // 如果模块已经被初始化,就忽略, j9 |; a5 P4 a1 O/ f0 r
            // X_LOADER_SAW_MODULE = 0x40 已被初始化7 q" f' L  \8 \5 }$ z
            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )
( T( R- {( H2 _7 D            {
" w3 R6 f4 x% Y9 l  ?! _) c                //
+ M1 l8 K6 {/ H; ?                // 模块没有被初始化,判断是否具有入口函数
; b/ |( R8 \5 [, g8 P- v% G7 a8 v                //1 X# S7 @- h, E) G! {- U
                if ( pModuleLoaderInfo->EntryPoint )( [9 X1 [- V) c) U
                {
1 z- ^9 F+ Q# s1 a) n' |& |- D3 V( J                    //. i6 H, j* C8 X+ S
                    // 具有初始化函数,添加到模块列表中,等待进行初始化. {3 }) a- T4 b/ d
                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;
/ z* B5 y1 L: b) J. t* |+ P: J, q4 |3 S
                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址
5 S2 f( b( f3 e( w      // 例如:( z* V$ M) d2 o  ]' R9 h: H
                    // C:\WINNT\system32\KERNEL32.dll init routine 77f01000
3 n; d$ I& y  X: v                    if ( _ShowSnaps ): O8 D5 y  s+ h
                    {' r5 D' ?5 \  W* D9 c* }7 a, q
                        _DbgPrint(  "%wZ init routine %x\n",* e; d1 c8 [/ G  `6 F
                                    &pModuleLoaderInfo->24,
  U7 c. r9 w2 J                                    pModuleLoaderInfo->EntryPoint );
% Z& ~8 v: y, W) }7 F1 h) y                    }( x. i! I8 [1 Z
/ G$ N; [& D$ v
                    nModulesInitedSoFar++;
' I% c. E. q9 e9 L( d, }# j                }4 u% ?: k1 y" i% r: c
            }
' m- x% e2 G  U6 Z. A* }5 ~, |' O+ [( V- j) D7 U6 l
            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。
" \7 K) {: |: t& t1 U$ C) S            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;
1 `0 P$ a- K7 l0 J. n# C  ?7 x  u5 z
            // 处理下一个模块节点- g- D4 i) l! [7 A
            pCurrNode = pCurrNode->pNext
8 s3 \* \) _$ @- ?        }
# D$ S( [8 ^$ W( E2 g4 V8 @    }
0 P5 y) H1 W% p. F0 U" {    else% T+ `* ^+ I9 v/ v0 ?  R
    {" l. k+ N  |( ~2 w9 m. g
        pModuleLoaderInfo = localVar3C;     // May not be initialized???& H" K, j8 v, z( I  j# K+ d
    }" t; o/ n; {) r: A
   
% R9 N2 D9 r8 r" d2 q$ Y1 I7 M' ?    if ( 0 == pInitNodeArray )
3 f% x# E2 s) ~+ {( n$ Q        return STATUS_SUCCESS;. @6 X% g  G, @. h: ^

6 K6 O* ]* d" D2 H    // ************************* MSJ Layout! *****************
% G+ p. q- e/ Y( s$ J7 R8 l    // If you're going to split this code across pages, this is a great) ?: J% {- h+ V( s! G* v- n5 u6 W8 \
    // spot to split the code.  Just be sure to remove this comment
3 {6 Y+ |( c$ S- @  [+ l+ f, q    // ************************* MSJ Layout! *****************( A8 @2 `, k8 K& ?$ }- n; Y
   
" y$ \: }7 {8 ?, v1 p) W" V    //
' X# K! V8 a4 m3 o/ m, v$ o  D    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH
7 v- N5 V. u: F    // 第三部分,调用初始化部分; G; p; m8 }: t2 Z3 y
    try     // Wrap all this in a try block, in case the init routine faults% @* ]. ?5 }' T3 i$ [
    {6 m' m9 t4 z, N" w  @+ K
        nModulesInitedSoFar = 0;  // Start at array element 0; v5 ]$ ~( W# T5 c! j% \$ l
3 ^7 b7 X+ m2 J+ d+ a( i1 H
        //
/ K& w/ p' B$ r8 \        // 遍历模块队列9 S% _, |7 l) }: K2 v
        //% H5 {- }- M  q# L9 x) z
        while ( nModulesInitedSoFar < nRoutinesToRun )- k. I! D/ S5 P6 g; r; k& S
        {
9 {+ Z6 U$ S$ c, {, D" i; T            // 获得模块指针
$ L7 b$ k2 w* U' l% e, G4 ?: N            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];
  ^( \* R$ ~8 U# Z7 F1 w% h  Y, }% Q3 _/ z
            // This doesn't seem to do anything...( b. F  D: o1 Q) O% D5 a
            localVar3C = pModuleLoaderInfo;0 n4 \- f) M$ N$ H1 d+ P
            
, F$ j, f7 l' E8 N            nModulesInitedSoFar++;
/ G" W% C% Q9 _8 h               
" [) z/ {7 O2 b8 @            // 保存初始化程序入口指针
; [" c6 J, Y# _* F; _            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;
3 I2 X$ W$ ^3 D            
! C9 X& l# ?. L: n: }; e            fBreakOnDllLoad = 0;    // Default is to not break on load
6 w8 M# R3 A+ d# f  v
  ^$ R% o0 x$ E( p9 X7 r8 T' D            // 调试用+ x! P' }" W. d/ g/ @- E8 a8 i
            // If this process is a debuggee, check to see if the loader+ |# w9 L1 A! X9 \3 n3 f
            // should break into a debugger before calling the initialization.1 y1 h. X) x% f
            //
6 N! I7 \* t6 ]* O0 X5 d            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()  o  t$ e) x) I
            // returns. IsDebuggerPresent is an NT only API.
/ E4 O( F# ?, ^            //% F) M0 Q1 @" n2 z1 S' j  T/ r
            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )
. Q5 s! E4 H& S6 |            {
5 S$ x. u8 m7 g6 o( _                LONG retCode;
, p. [/ N( e& _* A* A/ B
- U9 @+ x# V% m( S: J# i                //              
9 o  d7 A2 e/ g3 I( q                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\. e0 l/ M/ u7 P$ k+ [! E/ o* y% I3 ~, O4 J
                // Windows NT\CurrentVersion\Image File Execution Options"
1 T& F: u% b# Q9 v" I                // registry key.  If a a subkey entry with the name of
0 w$ q7 O3 O2 F( m                // the executable exists, check for the BreakOnDllLoad value.
, [) f8 V" k  U% t/ [( A1 M. ?! `                //% z7 J+ U: f7 h. D0 E
                retCode = * m  ?' }2 ^# J# G( v. I1 L$ L4 S
                    _LdrQueryImageFileExecutionOptions(( x- \- g# e0 b: N1 T: b
                                pModuleLoaderInfo->pwszDllName,5 Z5 r. \' T) O( t2 x# R
                                "BreakOnDllLoad",pInitNodeArray
, z# J( S) g; H                                REG_DWORD,6 Q4 r% O. E, A* p9 j8 W) H' i  q
                                &fBreakOnDllLoad,
: z3 B/ A4 S% o" H                                sizeof(DWORD),# C) }: n5 j6 q$ e
                                0 );$ p- \7 w9 h( T4 m8 C$ p3 H

$ o5 e( A2 I, F" N                // If reg value not found (usually the case), then don't
0 S6 I; E4 ?! W9 ^" A  p. |/ _                // break on this DLL init
$ |) _$ y" Q2 R1 R; ^                if ( retCode <= STATUS_SUCCESS )
2 I4 O0 D7 _4 T7 q/ [6 S; y+ z2 c& R                    fBreakOnDllLoad = 0;pInitNodeArray. }: d9 H$ z) s, Y
            }
# p3 U: l; G& K            
6 U- y1 Q9 m4 X$ x6 _& G, h            if ( fBreakOnDllLoad )
4 _' i" I+ W5 k' N( B            {           - |6 N6 l* r1 b* V+ m; z
                if ( _ShowSnaps )& R& e* K) K: @% w2 v5 i/ W8 O4 }: P
                {; P2 r: E- r/ U( p2 d) ~; n6 q+ F5 n" G
                    // Inform the debug output stream of the module name
) E2 e; D% M+ |+ ?- a& |                    // and the init routine address before actually breaking" ]" m" i4 Z+ I( p! X$ Q
                    // into the debugger& ?1 w7 [* |' m. f4 K( Q8 e. y; }

$ k* ^: e: Q* b                    _DbgPrint(  "LDR: %wZ loaded.",
/ i5 i  C6 o+ f# G                                &pModuleLoaderInfo->pModuleLoaderInfo );$ j4 f* u: N1 q# K  u* w( s
                    : j' U; h- M  \  v9 l
                    _DbgPrint(  "- About to call init routine at %lx\n",
1 f- [$ u4 ?. O, ]# G% u                                pfnInitRoutine )
/ P8 I# K! m6 c9 i% T( g+ r                }
$ t: p2 @; a/ j" a. y# X4 O1 R                  B% r( f& U! ?4 I5 b
                // Break into the debugger                              / O( e+ E5 A& H$ G0 [
                _DbgBreakPoint();   // An INT 3, followed by a RET
6 C2 w% H* A- ~5 k6 A            }
) ^7 ]3 e+ U! Z  J: p- q( h            else if ( _ShowSnaps && pfnInitRoutine )
7 k$ c5 Y$ g* B7 d! N            {
2 u" u  T; o6 P( q                // Inform the debug output stream of the module name" d2 x( r( F) H" `/ I
                // and the init routine address before calling it               " B  {# f1 Z: I7 P6 w/ z" O" E
                _DbgPrint(  "LDR: %wZ loaded.",
" ]: T$ r5 a5 f- M# w                            pModuleLoaderInfo->pModuleLoaderInfo );" G8 _% F" A. Z+ F! G0 u
- ]; k- p0 B. W/ ^
                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);- T% `$ Z3 {% Q5 K; @- s
            }, t% C1 J- x* u: p- P( P# `  g. b
                    
+ i5 b* ^. B" Y            if ( pfnInitRoutine )
9 L! i" y1 ?/ i# e) s+ X3 G, r2 t; `; y            {$ Z2 r3 }4 C$ w' f
                // 设置DLL_PROCESS_ATTACH标志0 Y! B2 i0 G5 {: T
                //; S" m! M& _9 ~9 T1 V& Z$ Q
                // (Shouldn't this come *after* the actual call?)
# E. v  ^! w" I/ I/ ?/ @) X9 |                //2 Z/ |" }& {) p. V$ u2 A# o
                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8            
4 N! _* U+ T+ U$ s( W/ U0 Y6 q                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;
8 i+ |2 P+ [& j
, `( q/ G, S+ M+ E# m6 ]3 }! y( p                //
7 q8 Y7 ~' m0 s0 Z! |9 S6 b! ]                // If there's Thread Local Storage (TLS) for this module,
" W  `- I! t) [& ^. o( t1 Q, g. d! |. J                // call the TLS init functions.  *** NOTE *** This only* k( O* N# s. |
                // occurs during the first time this code is called (when
/ s! z& _0 y9 ]                // implicitly loaded DLLs are initialized).  Dynamically
) P4 M; W4 j* l) G                // loaded DLLs shouldn't use TLS declared vars, as per the
" t* J6 ^: C  a* Z4 Y                // SDK documentation
( {  S  @. s2 }! p8 D  w                // 如果模块需要分配TLS,调用TLS初始化函数) m: w' k- ]+ v+ o
  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时
5 X$ |  m# |- x6 E* h) Q- O  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量
2 `# a" a. y2 w  H, z# m# O                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )1 V% O8 M4 C& \5 D# `% E5 L
                {
! D; g, [# Z. g. R+ G8 o                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,! ^) S* g3 u3 K1 L1 z, }
                                                DLL_PROCESS_ATTACH );, }+ `& m1 J* r5 `0 m
                }6 K# n, _# O. P" N- l5 w; G! r
                " p4 E0 U& s2 w. [: J' b
- _) W5 m. M* ^- h8 [0 c$ \8 U9 M0 I
                hModDLL = pModuleLoaderInfo->hModDLL
6 |& i# o& s; D; C8 f, m! U2 u7 q3 x$ A1 S7 Q
                MOV     ESI,ESP // Save off the ESP register into ESI+ Y+ E1 h% ?. M7 A: V3 }
  
7 E! ~% `! a% y& |3 H1 s2 I  // 设置入口函数指针               
3 v. g1 ]. {7 g: G                MOV     EDI,DWORD PTR [pfnInitRoutine]                     + w# [3 [$ i2 k8 d

- s/ v3 U- Q, @% s5 c  [                // In C++ code, the following ASM would look like:7 V/ \; t0 G8 t4 C9 F
                //
9 O( |) a! r% D# U8 e                // initRetValue =
7 `8 n6 J& c8 q' C. O                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);4 \2 y6 ]  l5 e8 o
                //
- X( J2 q/ N$ ^% C7 F* R" Q$ C$ A% q8 F) k7 R" V
                PUSH    DWORD PTR [bImplicitLoad]
% U2 B% C  r/ F" `/ z8 B' Z9 x               
) b, u! u8 t! \: J! _& _, e! F6 ]                PUSH    DLL_PROCESS_ATTACH
$ E, ~1 A8 l; i' y2 d4 e                ) M) [7 i9 H; c- s9 C5 P
                PUSH    DWORD PTR [hModDLL]- N- f3 ?: e3 }; V# ^: Q* F( [
                8 ?) t' D5 w' W
                CALL    EDI     // 调用入口函数3 ?0 j& E" K( |0 O" a, V8 T9 Q
               
7 n) F# x; |+ G0 W( b5 Y( I                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值: Y2 D9 P, I) w! Z! g6 e/ `
4 i( l' F- r6 e& c5 R0 T- y, _
                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the/ r# b0 Y& o( ^2 [$ J* N
                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns# J) z) ^8 E7 y3 U5 B% l' \
4 v/ v8 w; v1 J3 o4 ~, H, ?! Q
                MOV     ESP,ESI     // Restore ESP to value before the call
$ ?2 L+ a* c- E1 F" k2 L% |. `/ C) m1 A  Q
                //
) r5 v2 E+ |/ f9 i3 G6 J                // 检查调用前后的ESP值是否一至
$ K0 O2 B" _; C% S  // . o; h$ ?) s4 i  L3 x) a6 Y
                if ( _CurSP != _SavSP )
" f$ K/ ]) }8 b( @. |                {
: u: d$ N+ K1 G( J2 j4 I                    hardErrorParam = pModuleLoaderInfo->FullDllPath;5 s5 A% n: r( a
3 O7 r& P) E% ?4 I" Z
                    hardErrorRetCode = / V( a% ~3 Y/ _+ J6 p0 j
                        _NtRaiseHardError(; q2 u  M$ a9 r7 A
                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,
1 y" r; `  \" J5 e/ F                            1,  // Number of parameters, n5 R# U' ~+ p8 P/ X
                            1,  // UnicodeStringParametersMask,) A/ o/ ]+ D4 O1 P% _' b
                            &hardErrorParam,0 D/ m, k$ j; k% ?
                            OptionYesNo,    // Let user decide
/ @+ c  {1 O) p$ y- c                            &hardErrorResponse );
$ B0 v$ F4 E, F) T                                            ; g/ j+ a2 d' ]" T$ v( I0 K7 s( Y0 R
                    if ( _LdrpInLdrInit )
. \+ c9 k; F# y1 H% U5 J                        _LdrpFatalHardErrorCount++;
0 [& F( U4 k2 k! X8 k* |7 k/ {1 G4 z; b( e8 m# h* {  s4 U1 H9 C% R3 X% n9 @
                    if (    (hardErrorRetCode >= STATUS_SUCCESS)
8 B, R' o$ G5 ]" c. L                        &&  (ResponseYes == hardErrorResponse) )
0 ?( H" S1 j3 w  M  z- p- V% Y2 b                    {
# y% r2 N4 p/ h$ p+ u# r                        return STATUS_DLL_INIT_FAILED;
4 Y! r" U" _# ^( F& Q! b1 Y5 n                    }# @% a' b$ @" ?
                }8 I/ S+ j5 H9 c& V  L9 n* o" G

; c& D, b( G: R* o$ Z                //. m5 [' {. q, z: [1 }
                // 入口函数返回0,错误
. i) m7 O* }( f" l- k2 H7 p                //: v1 e# c2 t6 x! O( T2 i6 V$ h
                if ( 0 == initRetValue )
2 _2 H9 ~/ L/ A/ n5 L* ~, r5 p                {
  p. g& W! P" y% ~' C6 N                    DWORD hardErrorParam2;, @  F% y" ~/ H( R, K7 r0 z* A: T
                    DWORD hardErrorResponse2;
( c; M% i/ ^/ ?+ p" a( [                                        9 G7 X  a' X2 R& U
                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;5 ?8 k) ]2 ~+ P! _4 D: q" \
                    / q( j* |8 L1 I8 p
                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,4 N  U' ?- C/ m+ R
                                        1,  // Number of parameters
, a4 p: C2 R: U+ r                                        1,  // UnicodeStringParametersMask1 S4 \1 m, w( }9 N# |5 Y4 v" E
                                        &hardErrorParam2,9 x$ O  J/ @2 W: j- j! V  _/ o
                                        OptionOk,   // OK is only response
+ L; t# E- G: d                                        &hardErrorResponse2 );% I3 w# z' r% \. q- Q
                                                            
5 z* B5 a( n# T; ^$ B7 X                    if ( _LdrpInLdrInit )
( C% F; u3 `1 J                        _LdrpFatalHardErrorCount++;7 c4 r. ?' V3 o* E
' O: l0 e, z/ [/ @" F
                    return STATUS_DLL_INIT_FAILED;
9 q+ s9 v# g' o3 c( O4 K1 _1 s" F                }8 S. M; S- z- _3 Q) u% P3 v! L
            }0 ?! _/ [( Y" m
        }
$ g2 F. m5 F7 I. o+ i3 I
) d; a, o/ X. H$ X/ Z$ c) a/ R8 L" `        //5 p$ d  ^. }( l, X
        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时) T% n3 S3 I" `* F8 W. k
        //      
8 @/ F5 `( ~; w1 c        if ( _LdrpImageHasTls && bImplicitLoad )
9 O# T+ [+ O4 ~        {
7 V, T# Q/ z; n8 p  ]            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,; O. G, J( D7 c1 b+ w! ]% T
                                        DLL_PROCESS_ATTACH );
6 l1 W: F4 c7 v) u2 W        }" j" H3 z9 x& d/ C4 ?
    }: L/ R. ?6 m: H& q! E
    __finally1 d3 }. X/ b6 x  k! i* t) c: i" W
    {3 b! ]- \* V% O; J( B
        //
) h3 h7 I8 Q" [& I        // 第四部分;; M9 T+ V2 e# ~* Q$ w. |( d+ J* E6 D
        // 清除分配的内存3 s) s4 u3 f2 q+ c
        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );
# {; W2 h) k8 N$ O1 g# ^3 a4 l' P  q    }" o* f. K8 R3 i8 `9 [

& S5 G+ T2 ^9 V" {2 A    return STATUS_SUCCESS;. J/ I4 S6 ^: F3 i! F4 k
}   & f. q: ~; L8 z# R

% p- n' b% D6 s& I2 y9 `* {这个函数分为四个主要部分:
6 E" H/ W) c% R+ O" ?8 b( D一:第一部分调用_LdrpClearLoadInProgress函数,这个NTDLL函数返回已经被映象到内存的dll的个数。例如,你的进程调用exm.dll,而exm.dll又调用exm1.dll和exm2.dll,那么_LdrpClearLoadInProgress会返回3。得到dll个数后,调用_RtlAllocateHeap,它会返回一个内存的队列指针。伪码中的队列指针为pInitNodeArray。队列中的每个节点指针都指向一个新加载的dll的结构信息。
8 ]1 d3 t- {. B) t8 w2 g二:第二部分的代码通过进程内部的数据结构获得一个新加载dll的链接列表。并且检查dll是否有入口指针,如果有,就把模块信息指针加入pInitNodeArray中。伪码中的模块信息指针为pModuleLoaderInfo。但是有的dll是资源文件,并不具有入口函数。所以pInitNodeArray中节点比_LdrpClearLoadInProgress返回的数目要少。
# N8 J' o3 K+ a; c4 Q三:第三部分的代码枚举了pInitNodeArray中的对象,并且调用了入口函数。因为这部分的初始化代码有可能出现错误,所以使用了_try异常扑获功能。这就是为什么在DllMain中出现错误后不会使整个进程终止。( b( D+ D: h: w/ C
另外,在调用入口函数时还会对TLS进行初始化,当用 __declspec来声明TLS变量时,链接器包含的数据可以进行触发。在调用dll的入口函数时,LdrpRunInitializeRoutines函数会检查是否需要初始化一个TLS,如果需要,就调用_LdrpCallTlsInitializers。) B0 G3 x+ {% o9 e- V* Z' o
在最后的伪代码部分使用汇编语言来进行dll的入口函数调用。主要的命令时CALL EDI;EDI中就是入口函数的指针。当此命令返回后,dll的初始化工作就完成了。对于C++写的dll,DllMain已经执行完成了它的DLL_PROCESS_ATTACH代码。注意一下入口函数的第三个参数pvReserved,当exe或dll隐式调用dll时这个参数是非零,当使用LoadLibrary调用时是零。在入口函数调用以后,加载器会检查调用入口函数前和后的ESP的值,如果不同,dll的初始化函数就会报错。检查完ESP后,还会检查入口函数的返回值,如果是零,说明初始化的时候出现了什么问题。并且系统会报错并停止调用dll。在第三部分的最后,在初始化完成后,如果exe进程已经拥有了TLS,并且隐式调用的dll已经被初始化,那么会调用_LdrpCallTlsInitializers。
4 {, p( x8 L8 B4 B  v/ j& N$ V$ {四:第四部分代码是清理代码,象_RtlAllocateHeap 分配的pInitNodeArray的内存需要被释放。释放代码出现在_finally块中,调用了_RtlFreeHeap 。
您需要登录后才可以回帖 登录 | 加入计匠网

本版积分规则

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

GMT+8, 2026-6-8 07:05 , Processed in 0.089010 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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