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

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

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url]6 F7 o1 y- q2 M0 L; o7 q1 T
2 t' t2 ]6 H' G. e+ V, S0 Z! P5 v5 Y( c8 l
WINDOWS 2K Dll 加载过程
4 a$ c: A* g* N9 Fjefong by 2005/03/30/ ^/ C' Y$ W" g9 U7 B! n/ ?/ K
这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。
. U" F& {2 E) H& J- r2 ]在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”。# w+ R# n/ {' v: ^
你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。
: l9 C* ^4 N- v0 t& j初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。: ^& m+ P& o7 b* |# C, m
  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。
$ P( ^) [6 n- l; X  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。
2 \5 X) a2 o& D; y$ c  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):
7 l* S* N' _: i# Z( G//=============================================================================
; z' G4 H/ d( P// Matt Pietrek, September 1999 Microsoft Systems Journal# a) A- R& k$ O* g
// 中文注释部分为jefong翻译
2 Z. n( ]* g& V7 J: U& n//+ q  B7 {' k2 `9 c; E
// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3)  o) a9 y  T+ b! N3 O
//" A" G+ T, ~2 k3 o6 _6 G' S# |
// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;
" i9 i, Z" h: ^. g, w//=============================================================================
9 D7 m4 a3 t" C! z6 q2 k; D! ~2 \  d; l' J  w* M6 A- x
#include <ntexapi.h>    // For HardError defines near the end# U6 x% @9 A5 M' O' Z

! G- q- l" v9 l% v% @// Global symbols (name is accurate, and comes from NTDLL.DBG)
4 C/ W5 `( M- \2 m6 |5 O//  _NtdllBaseTag+ s4 ?& v3 L8 `" q: i
//  _ShowSnaps3 Y, g# b# g+ [5 Q3 V
//  _SaveSp
+ X5 F5 }  I7 o) h5 p: `9 b6 j/ W//  _CurSp5 q6 ^1 E% {2 @8 a& H# I
//  _LdrpInLdrInit
* A" F" Y8 |7 h/ r5 G$ n//  _LdrpFatalHardErrorCount
: J" i6 T$ y8 k//  _LdrpImageHasTls3 Z$ t  V8 ^& ^  k/ a
' V2 P* i" q& y
NTSTATUS8 u/ H6 L; C. G
LdrpRunInitializeRoutines( DWORD bImplicitLoad )
/ a0 A/ R+ y/ |4 s{
; g, W; H7 D; m, N    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了0 _0 Z2 S( U5 N7 _2 t
    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();7 `: M- I2 U% P8 }; }* l! ]

8 E' a; {3 F9 x: ]    if ( nRoutinesToRun )
1 s6 N- ~' t/ i+ s8 d: E$ a$ F% m9 _    {. {$ _! t4 Y; N7 o
        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。
' J7 g! a1 m. Y! ~" [) m: U! f; B- W        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),
) K% ~; R! v  ?# @4 F( d) }                                            _NtdllBaseTag + 0x60000,4 _6 \, b4 |- ]7 ]' t" \& v
                                            nRoutinesToRun * 4 );
+ l& x+ s8 e; q3 f! N3 ^, f. `8 u                            % V+ K, W2 l# U4 e9 O
        if ( 0 == pInitNodeArray )    // Make sure allocation worked' |0 |9 ^6 ~$ F
            return STATUS_NO_MEMORY;
& d3 \& h, `9 V, Q: j( `    }
2 Z* q8 @8 e: e3 W3 I    else$ W% n% w) ?' V2 R! P
        pInitNodeArray = 0;( R6 @+ v8 ?+ F. m/ D

- P7 o9 Y; `6 ?( t( e. ]* o8 q" M    //第二部分;
8 M' g  g0 z6 @' _7 I; r    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。$ f8 ~) f7 H$ u! u8 s% _+ E% C! C% }5 H
    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);$ n1 f. [0 l: B1 F3 Z
    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;
3 z/ ?: N) X; z; g6 r- y  d        
& S% b* t* X7 r; f  O, k* u  Z/ p    if ( _ShowSnaps )
* G* M# n( R% n6 }) S1 C+ z    {6 m/ X5 v) g# h# n* X6 E  P
        _DbgPrint( "LDR: Real INIT LIST\n" );
) r# ?4 N2 F4 O  c/ y    }
  f/ E0 t# }+ u3 A1 N
% B$ E* F* [- q3 y5 ]    nModulesInitedSoFar = 0;5 \: u2 @- B% H5 E% L2 X3 ^. A

! j6 o  x: ~* g    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块
$ U" T( l  F- R  M    {! {% k" s! {5 U9 f
        
6 S( x! C0 f9 K0 J7 }        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块8 F; P; A( Z* L' m6 j+ ?; e
        {9 {: a& R1 o+ ]3 I
            ModuleLoaderInfo  pModuleLoaderInfo;' A7 `- ^6 K, l
              ?; Y, B& D/ t! _
            //
4 j/ d* X" t, k" \" p. H2 j3 a            //一个ModuleLoaderInfo结构节点的大小为0X10字节
; ]" e+ @; x# I: W3 s9 Z; R; P! ^            pModuleLoaderInfo = &NextNode - 0x10;
& ]( W3 h/ U& ]) \1 f  W! e% Z/ N            
3 D3 w2 \% g& \' G% a7 U            localVar3C = pModuleLoaderInfo;         ( k! R- K2 N; `  @5 {7 z

: O* Q% A% v9 b            //
7 ]. b3 Z2 P7 y0 \8 H# C            // 如果模块已经被初始化,就忽略8 n( O1 h- [( y" o( t* h3 ?
            // X_LOADER_SAW_MODULE = 0x40 已被初始化
6 A" x5 n. \7 z4 @9 e            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )
1 F6 O  H6 D) h+ R8 Q            {1 A! X$ c8 E6 ^2 k# ~# v& l+ S) o
                //
) Q6 D8 r( @& M/ b                // 模块没有被初始化,判断是否具有入口函数
0 |1 O$ O8 P  Q. }                //
5 T+ M1 c: N( b" E# f. f                if ( pModuleLoaderInfo->EntryPoint )
2 `, \7 K% D% f! ?                {
, K5 l9 X7 P) v5 C3 A                    //
- I- ^. b9 Z- r                    // 具有初始化函数,添加到模块列表中,等待进行初始化- c! c% }8 M2 v$ W' T" q
                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;( R# N/ [3 ^; A  ~6 F& N1 [
& Y1 b( l# \- t# g
                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址
3 ^6 L: ^' ^- _6 U      // 例如:
& \2 W$ U/ q7 U7 I) R                    // C:\WINNT\system32\KERNEL32.dll init routine 77f01000
! ?: B9 r4 A  ]0 Z" j( _                    if ( _ShowSnaps )
: T1 g# I2 R/ Y2 V& T  m/ s0 o" v                    {; G: I* g. C* F  q% G+ B: |$ |/ r. L
                        _DbgPrint(  "%wZ init routine %x\n",
8 M0 J. J: Q: Y% n; r                                    &pModuleLoaderInfo->24,
: H" _3 H" U- n, ~1 P# i" M                                    pModuleLoaderInfo->EntryPoint );# m/ i0 M( C2 @: V$ h" `- X: W
                    }
' c" k) H# I$ e+ g0 X" N! Y+ Y
" z. k2 ], s! w( d                    nModulesInitedSoFar++;& S$ e% y9 F, G, V$ O( [( x  h
                }
: F7 P. h6 A; N; `            }# b6 U4 D, ^7 v  c$ G! w) S
2 P/ f# R! }! y$ X& y1 `1 I
            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。3 w9 c- x9 K  o: y
            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;6 `- e4 j) Y9 h$ [
2 b* c$ c' W" [6 T) ?
            // 处理下一个模块节点
8 g1 h. W9 C  v  P& l            pCurrNode = pCurrNode->pNext3 K; d4 g8 r9 T7 L  {& z
        }
7 B- j. C3 A1 j4 Y6 Q) z    }% O  `' @. ~( U
    else
1 z- R3 i( S0 m" y  E  @    {1 @2 R5 C* S/ n; g' F; F) {
        pModuleLoaderInfo = localVar3C;     // May not be initialized???
1 @6 M7 P6 @* [- n$ r% b$ Y7 f5 m    }" E# _+ b1 g) T
    8 c. w- x( m! n2 t
    if ( 0 == pInitNodeArray )  S, b* T* g$ P4 N: Z
        return STATUS_SUCCESS;. e' e+ m8 b. W1 U
& G1 B1 y& U! i4 u& _( m) M
    // ************************* MSJ Layout! *****************
# M" G8 _0 K* J    // If you're going to split this code across pages, this is a great' p% ~2 Y3 @! B
    // spot to split the code.  Just be sure to remove this comment
  {" R. }  f7 |" X    // ************************* MSJ Layout! *****************
/ L6 E- d# E& F* |" i    - p3 }; j1 j+ M" W; k% }0 [8 A
    //
5 T9 R& r- c% J' q2 m    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH- Q! `& U- ?6 c& V; a* V3 `1 a
    // 第三部分,调用初始化部分
# c) A1 p0 E3 Q- Z    try     // Wrap all this in a try block, in case the init routine faults
% G( X3 }3 P: P% V    {1 F% U. U7 O( D' r2 U
        nModulesInitedSoFar = 0;  // Start at array element 0
! a# |6 m3 P' M$ D
1 p1 L% Z) n! P+ z        //
' c. F& u* f0 m! y9 Z, X        // 遍历模块队列8 r9 `  y* m* [( Y! g& D8 N
        //6 X3 X+ m6 Y5 p- H- d
        while ( nModulesInitedSoFar < nRoutinesToRun )# w& @& q  w3 G& H% I  O7 }( P9 z9 @
        {
3 h9 [9 I2 p; F9 r/ G9 U            // 获得模块指针6 |& j( v* v- t7 K* r
            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];
1 e- R$ g; O$ Z" I; J1 u$ Q2 v( n8 b& F; q) U' i- k
            // This doesn't seem to do anything...
5 @( [9 L2 k- E            localVar3C = pModuleLoaderInfo;
! q$ u8 q5 D/ H2 T$ _$ ^& ?. h            
% U1 J4 m8 g& z7 O9 Q& X3 n            nModulesInitedSoFar++;
! G1 n% N4 O- n% X: z                , ^2 f/ K# i# U; h' B
            // 保存初始化程序入口指针
( b# I- G- w3 W2 \- P2 }            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;6 j4 q2 x# W# f3 J1 b0 s1 V
            4 s8 C( ]% a) F/ V1 z6 l8 i0 k
            fBreakOnDllLoad = 0;    // Default is to not break on load9 H# n; q( m3 w5 S$ D

' Z" j$ |" R0 q9 n. t            // 调试用/ m+ X! b) h7 T, Z2 I/ L
            // If this process is a debuggee, check to see if the loader
' H4 M9 c* l6 d5 M9 i, `6 r& E! X            // should break into a debugger before calling the initialization.
3 L4 r" p; {! A            //
5 X, A( v9 {; g) m            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()
) z1 V" O3 K% j. j/ h            // returns. IsDebuggerPresent is an NT only API.
. u, R1 f$ _4 Y4 \) b5 w            //+ N( ]1 Q. r# w5 z0 e
            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )
; \  o  t& ~. \$ t1 B$ w2 M9 P            {
) g; B9 b2 g: V' D1 c( K                LONG retCode;
8 K; A4 `/ L: g9 k
2 j- {. u# Q6 k( s                //              " C5 _3 y0 d  ]( N- p
                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\3 e( {+ }0 j6 O6 [4 G( |: v; N$ Z
                // Windows NT\CurrentVersion\Image File Execution Options"' l( C, d, ~/ A) i/ Y$ c
                // registry key.  If a a subkey entry with the name of
0 L2 V2 k+ a' `+ E                // the executable exists, check for the BreakOnDllLoad value.
0 q  g4 m' w4 K2 h' C                //1 e9 r: b$ s# U0 v
                retCode =
; A% c$ F) `" D1 p                    _LdrQueryImageFileExecutionOptions(1 c; f: D- z# V9 U
                                pModuleLoaderInfo->pwszDllName,; f9 B2 _  D* r2 ^( Q& l
                                "BreakOnDllLoad",pInitNodeArray
# d+ N' ^) k3 i8 t                                REG_DWORD,8 }& _& B, |1 B; |
                                &fBreakOnDllLoad,
9 B6 _" k6 }5 b$ d+ I1 N                                sizeof(DWORD)," s; U2 s. u, y% p
                                0 );
: q5 c+ Z  ]+ e. r0 Y3 h6 _0 h
                // If reg value not found (usually the case), then don't
9 f) }2 F) {' l! @+ r$ f! ]7 i9 b                // break on this DLL init
  ^/ l( w2 Y  L% o                if ( retCode <= STATUS_SUCCESS )3 H* h4 }, L' n( i5 O$ `1 ~  V7 _" ?! M
                    fBreakOnDllLoad = 0;pInitNodeArray
% Q+ J5 k+ L+ Q, y3 s& X" i            }
' X& X3 p- Q0 P) ^! d            * ?- l) _1 H" S! V
            if ( fBreakOnDllLoad )5 W6 m# |1 P3 R* W1 f5 t4 |
            {           
& e" V& Y. ~! n  x: w2 T; s                if ( _ShowSnaps )
# c5 O4 y. p) |  h. S7 |7 `# y                {
7 ]) B7 g8 j- {9 \- N/ [                    // Inform the debug output stream of the module name9 a9 i- `3 {8 V" t  m
                    // and the init routine address before actually breaking
8 {( F1 d* s& D8 f8 O7 y( A5 |! ~                    // into the debugger
8 \; L: R8 s- Z! [0 \/ X$ P6 m0 Z. g6 k4 s) \
                    _DbgPrint(  "LDR: %wZ loaded.",
7 F3 B9 ?" A3 P4 _. o+ W                                &pModuleLoaderInfo->pModuleLoaderInfo );
$ S9 y4 w) R1 Q8 @. o% q, m( E                    * [8 n% o- D$ x& H" y+ W2 e2 j, K
                    _DbgPrint(  "- About to call init routine at %lx\n",' h+ x2 Q% G3 G! ~3 {
                                pfnInitRoutine )
- m7 C# A0 e2 g" h# l8 ^$ x( \                }6 P/ D. M1 f/ Z* Y
               
* l' Y/ |# A1 e* A! m: F2 S. n                // Break into the debugger                              
7 f2 X+ [4 ~9 c, g5 i% q7 t                _DbgBreakPoint();   // An INT 3, followed by a RET
/ R' I; B7 P8 X) v. k$ ]: x4 R' _            }
; n" F0 @+ q( [            else if ( _ShowSnaps && pfnInitRoutine )7 a, ]2 u" `$ h# R2 _4 ^
            {- Q9 k3 P3 v' }8 C6 f  X( y# W: U
                // Inform the debug output stream of the module name% F$ |( J. N4 l' ?. Z$ C3 K
                // and the init routine address before calling it               
3 _3 ]* d' t4 ^. R                _DbgPrint(  "LDR: %wZ loaded.",
; T9 Q9 G* W& {  |2 b" j                            pModuleLoaderInfo->pModuleLoaderInfo );+ Z" S( i" p8 a3 d' y
0 u) a" J* L* _' [% @
                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);9 H9 D4 y* j' ^2 j
            }! G* M' ]' r) i) D9 _1 {+ C  `
                    
, V( M% ]# W* e# o/ \+ j  L            if ( pfnInitRoutine )
' Z- }  F& V( k$ l! o3 Y7 f3 l+ e! u            {
. }% j7 B! C# ~( r" g  m9 u* ^7 F                // 设置DLL_PROCESS_ATTACH标志
! R( z- J" G' t                //3 C) b% Z7 u% {! o
                // (Shouldn't this come *after* the actual call?)
5 y+ j4 t( `# ^$ a1 H! V- q                //
8 f$ A4 W. F( I( y7 w                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8             " A7 [/ e- }) {: |
                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;5 e, n3 x% @% R3 @" P/ r
8 r5 X! H$ I' W% Y9 U* W! r
                //% ~; ~9 h$ f% E; @1 ]) Y- F
                // If there's Thread Local Storage (TLS) for this module,& l' p- k- p5 X% a+ l
                // call the TLS init functions.  *** NOTE *** This only
! A* v9 s' z5 M* P% n: \7 u9 i' f                // occurs during the first time this code is called (when) H: S7 T( F3 P% q/ S4 M, k
                // implicitly loaded DLLs are initialized).  Dynamically& {/ j" e& G" g3 R. \1 p
                // loaded DLLs shouldn't use TLS declared vars, as per the) k* e% K+ P& T9 d7 _! l
                // SDK documentation) {, s3 ]2 ]% C7 w* _1 m8 L
                // 如果模块需要分配TLS,调用TLS初始化函数
; ?/ x7 l7 w# V& o  n  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时' E3 a$ q9 o: D6 q
  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量
0 A, G  t- N% |0 D                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )
+ L* r9 _/ f: x; ?1 }  {                {
, f# m9 y4 ?' y& c2 s+ x                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,
7 v: h+ A" x8 u; X2 t                                                DLL_PROCESS_ATTACH );3 J+ L6 m' M4 r/ f+ }+ z( I
                }
' @$ S# `+ X. Q  d+ {                ) G9 n8 G6 _% b: r# g

6 T0 F4 Y3 Z& L, Y& c                hModDLL = pModuleLoaderInfo->hModDLL
; J' K% Z& I  L) i; g2 ]2 \
3 W8 w3 L4 o7 O) t. _0 E. U% Y1 `                MOV     ESI,ESP // Save off the ESP register into ESI
% V) \7 c8 i* Q  L* K0 m  
- P6 l/ Q" }5 S' [  // 设置入口函数指针                ; J9 C; {# u0 w- C- u( X
                MOV     EDI,DWORD PTR [pfnInitRoutine]                     : P6 ~% v( L: E- v; L6 m; j( o

5 H- e! s! j  W                // In C++ code, the following ASM would look like:
2 r! l6 Q; n2 i  X                //
& ^( e9 Z9 X8 {( [- q                // initRetValue =/ B' Y& h8 C& h  v+ H9 o* U2 g
                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);
5 F6 a/ [2 R2 e                //
. }! K+ M* M. ?0 Z- {( i% S  g$ ?* O, J. r) ]/ J- Q' {1 V7 c
                PUSH    DWORD PTR [bImplicitLoad]- A5 k/ L. ?+ F4 O# z9 P/ P$ |
               
! h' O2 Z( f& Q3 ]1 Q+ j4 n, H                PUSH    DLL_PROCESS_ATTACH
1 }" u& v7 M8 f                ( u! T( b( Q; a4 A; |( T
                PUSH    DWORD PTR [hModDLL]0 ^+ _; e* m; R; b! [1 E
               
% ~7 O) e  j3 R9 b5 Q                CALL    EDI     // 调用入口函数
) ]( T& g! _# K% g' Z' `               
8 J- G3 R7 R! p, f                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值
4 |. d$ {# m0 [8 z) e& i1 C. s! w+ I+ }9 E1 K$ H) E! g2 O& \: Y
                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the7 }0 ~, E( c5 j! j
                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns2 N1 q4 l% h1 Q
- W- g0 V' f# _& h  Y+ |
                MOV     ESP,ESI     // Restore ESP to value before the call
" @7 }- s! E" i; p0 ^4 I9 c, D' f; V! k& I) [1 F$ d
                //; h* S3 c, {- A- T2 G& i, P
                // 检查调用前后的ESP值是否一至
2 U0 i. V' [6 K  // # W) j2 x% T% `( r
                if ( _CurSP != _SavSP )1 ?% X- m2 a: o2 H& K* E
                {* O: ]: L1 X. \9 v3 A+ K3 P
                    hardErrorParam = pModuleLoaderInfo->FullDllPath;
( t9 t: _% O- s
: r/ ?8 ?2 _7 O6 D9 f                    hardErrorRetCode = , e; e) w; c  T3 k' W. O  u
                        _NtRaiseHardError(% B) e+ t) c1 q" U- e) c1 a
                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,
: t/ T8 V7 v- R0 n- z                            1,  // Number of parameters
; P8 ]% u: O+ x6 p. Z3 U                            1,  // UnicodeStringParametersMask,
, `7 ~0 k# A" |$ j0 e8 V2 q4 w                            &hardErrorParam,
; F' b  P4 p1 K, k2 ^                            OptionYesNo,    // Let user decide
" B5 ~: V8 H' d2 o, z4 k. U                            &hardErrorResponse );( L9 s4 ], N( C) W
                                            
3 Y. B' ?4 F7 p$ I4 l$ x                    if ( _LdrpInLdrInit )+ g# Y3 d2 ~. z, b) j
                        _LdrpFatalHardErrorCount++;8 }% @5 B/ a' c& e5 z

, X9 d4 V! i; Y3 s8 g: o                    if (    (hardErrorRetCode >= STATUS_SUCCESS)
! g6 j. s. ]& q+ X8 D. r                        &&  (ResponseYes == hardErrorResponse) )
4 V/ U1 X) Y) B. k                    {% y7 S, f, l4 j9 T; N" w
                        return STATUS_DLL_INIT_FAILED;- H1 }4 }8 Q2 b# m4 h
                    }
3 ]# W, W4 Z5 E% {+ p                }- t0 ?& G8 a) X1 Z# ~: [0 W
8 y2 E/ V, {3 ~  p1 ]3 y% t0 W
                //6 O  q* {+ a/ B& V' ]2 k3 L
                // 入口函数返回0,错误
+ ]/ ~2 }& n6 ^4 {1 I" a                //, B2 C1 s3 d4 ~% }! V. i
                if ( 0 == initRetValue )2 a5 r& v; u( u7 G5 ~
                {2 X2 L0 O1 J! O9 q% k
                    DWORD hardErrorParam2;
$ T' e& V% v+ y9 q% v2 v5 e                    DWORD hardErrorResponse2;/ F* P( P4 j) n# {2 N
                                        0 j$ Y. g$ y7 i5 p
                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;% G2 v) j. z4 N' B& {
                    
# e: A2 G" d7 l                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,
8 v: u) r  i/ R2 Y+ M% s                                        1,  // Number of parameters
/ m8 n# W& z4 J  W- e6 c                                        1,  // UnicodeStringParametersMask
9 l1 E( I! @+ s                                        &hardErrorParam2,
* r* k) d: w0 M: s- U. O                                        OptionOk,   // OK is only response
; H/ P9 _/ T. B5 u0 p8 n7 L: J                                        &hardErrorResponse2 );+ {8 g  u$ a" [2 v/ R; t4 e
                                                            ' Y+ j, v# K" S
                    if ( _LdrpInLdrInit )2 \6 e8 z/ l/ r
                        _LdrpFatalHardErrorCount++;
& T$ n, I' V6 _" P
) ^6 A! f9 S" H  W) ?                    return STATUS_DLL_INIT_FAILED;8 g5 ^( V8 F0 z+ R% G
                }
; q, c: n' |' p5 `& J            }& Y% M& H0 i9 ^4 f
        }
( {( p% E& {1 ?+ k) N
: D1 M0 {. Y$ y7 A5 Z        //( F# Y  ?# ?9 R4 d* r
        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时" _5 V# X0 H5 S$ T9 |# ~' r
        //      
, w  h* K3 [) `        if ( _LdrpImageHasTls && bImplicitLoad )3 _* m% n5 r8 @% `4 T
        {$ `; B5 Q2 @  _) j% [
            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,. J' x3 k- g6 |5 {$ C: X
                                        DLL_PROCESS_ATTACH );3 m0 n! }0 H" ^$ @! D6 c3 q
        }" J) U4 `1 s  j' I# T
    }' A) Z8 ]' B' w: }1 [: g
    __finally
& {$ Z6 V0 f2 [/ ?8 R/ A    {
1 ]: X/ m% T- M        //3 n9 M/ \' g! `
        // 第四部分;$ C! c* M$ {. I
        // 清除分配的内存* k% `# U  G8 ]( }/ U7 _  k) U
        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );% u0 G- w* k) |# E# Y) x# b
    }5 p5 X' U: l# `$ K& M

5 L4 z* `$ t: U  u" P: G    return STATUS_SUCCESS;
2 \6 K3 v5 l1 [5 }( d}   6 v0 S4 w3 X0 W/ D

" ?9 J- K. y' K9 A# m3 }! h9 @这个函数分为四个主要部分:0 y8 X+ [+ ^" l0 N1 C8 p
一:第一部分调用_LdrpClearLoadInProgress函数,这个NTDLL函数返回已经被映象到内存的dll的个数。例如,你的进程调用exm.dll,而exm.dll又调用exm1.dll和exm2.dll,那么_LdrpClearLoadInProgress会返回3。得到dll个数后,调用_RtlAllocateHeap,它会返回一个内存的队列指针。伪码中的队列指针为pInitNodeArray。队列中的每个节点指针都指向一个新加载的dll的结构信息。
% i; ^# t2 V3 `) T" C; @0 [8 i二:第二部分的代码通过进程内部的数据结构获得一个新加载dll的链接列表。并且检查dll是否有入口指针,如果有,就把模块信息指针加入pInitNodeArray中。伪码中的模块信息指针为pModuleLoaderInfo。但是有的dll是资源文件,并不具有入口函数。所以pInitNodeArray中节点比_LdrpClearLoadInProgress返回的数目要少。# }% R! z) L4 a) `# r- m% ^& y
三:第三部分的代码枚举了pInitNodeArray中的对象,并且调用了入口函数。因为这部分的初始化代码有可能出现错误,所以使用了_try异常扑获功能。这就是为什么在DllMain中出现错误后不会使整个进程终止。
1 h! g' r* T9 F9 R7 `; e$ F2 N另外,在调用入口函数时还会对TLS进行初始化,当用 __declspec来声明TLS变量时,链接器包含的数据可以进行触发。在调用dll的入口函数时,LdrpRunInitializeRoutines函数会检查是否需要初始化一个TLS,如果需要,就调用_LdrpCallTlsInitializers。! d% ~2 h% S2 ]
在最后的伪代码部分使用汇编语言来进行dll的入口函数调用。主要的命令时CALL EDI;EDI中就是入口函数的指针。当此命令返回后,dll的初始化工作就完成了。对于C++写的dll,DllMain已经执行完成了它的DLL_PROCESS_ATTACH代码。注意一下入口函数的第三个参数pvReserved,当exe或dll隐式调用dll时这个参数是非零,当使用LoadLibrary调用时是零。在入口函数调用以后,加载器会检查调用入口函数前和后的ESP的值,如果不同,dll的初始化函数就会报错。检查完ESP后,还会检查入口函数的返回值,如果是零,说明初始化的时候出现了什么问题。并且系统会报错并停止调用dll。在第三部分的最后,在初始化完成后,如果exe进程已经拥有了TLS,并且隐式调用的dll已经被初始化,那么会调用_LdrpCallTlsInitializers。$ c* F1 T1 P" w$ J; y
四:第四部分代码是清理代码,象_RtlAllocateHeap 分配的pInitNodeArray的内存需要被释放。释放代码出现在_finally块中,调用了_RtlFreeHeap 。
您需要登录后才可以回帖 登录 | 加入计匠网

本版积分规则

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

GMT+8, 2026-10-20 10:20 , Processed in 0.077241 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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