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

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

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url]
. M& Q7 R- H+ w/ s7 `' |6 I1 H
WINDOWS 2K Dll 加载过程
2 Z/ n/ z8 W6 O9 E6 H, y4 jjefong by 2005/03/30
4 ?2 t& ]' `, q# p4 |; j这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。
( t/ v1 ?" f0 R! |2 u在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”。- V7 J& z6 L8 _+ A
你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。5 w& E, ~* g; M1 x( o
初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。, P, i) a& }+ w- X
  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。8 M8 U  C" l) ~% V
  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。* r; Z; v% B0 ^2 G; y0 T5 ?
  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):- Z$ g; ~3 A4 Q* h/ a7 `6 D
//=============================================================================, m6 \; x9 H" r3 @) R( G
// Matt Pietrek, September 1999 Microsoft Systems Journal
- |8 h) H* v( s6 j! }$ [// 中文注释部分为jefong翻译9 ^9 x  e  ~  p1 r: {3 F( P
//
# [0 f* y7 l5 V$ K1 @& `. D// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3)
* |* v, U) ~4 }! l# X' z//! X* W! r$ C; ~+ K5 l) \
// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;
) _0 M+ N) N  W0 ?2 L//=============================================================================
( K: \, q6 X$ p6 e9 K
1 A1 ^9 ]) e9 f! |2 u0 u+ S0 K; Z#include <ntexapi.h>    // For HardError defines near the end1 |# V" G) j) S. m

* E" ]' O! n8 }% A* F: P// Global symbols (name is accurate, and comes from NTDLL.DBG)
# ^/ U, N0 m2 i! s; g  I//  _NtdllBaseTag: Y8 m2 p. `/ i/ t; D
//  _ShowSnaps0 ^6 \, J6 ?$ t$ d
//  _SaveSp
  k: N& \3 {* N8 S//  _CurSp
9 ~) Z1 Y% n7 [' C1 Q% z7 \//  _LdrpInLdrInit
, ]. P" w& y7 w" s+ r5 L; J& h//  _LdrpFatalHardErrorCount( k# i6 k; Z9 m: ?' X. l
//  _LdrpImageHasTls
0 R6 B, r( k  `& F* ]
% d/ G3 F5 y# `/ rNTSTATUS
+ g) e+ K" E/ q! x6 ~6 ELdrpRunInitializeRoutines( DWORD bImplicitLoad )! ~* c1 q9 j4 X) |# D$ T+ ^' c' ^
{0 [" I0 }; D) J" I+ w* ]( e4 c
    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了- P3 a) ^4 W" x" Q
    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();7 f, A$ [, g2 I2 X, E  b8 h7 i, k! `

' z4 c1 S) |. w3 G: s2 o% m    if ( nRoutinesToRun )/ a" W2 P7 c* [, Y% ?$ z7 ]; R
    {
6 W0 s5 z! T6 Q" [        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。
& C" K; }6 U& H        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),
1 X& V5 A, E; Y                                            _NtdllBaseTag + 0x60000,
6 A- f8 i. u0 a; n1 F4 \9 p8 J                                            nRoutinesToRun * 4 );
1 C& V$ J& N' f& I3 T$ `4 W- `                           
8 V# P# U2 s) P8 R        if ( 0 == pInitNodeArray )    // Make sure allocation worked: r1 ]( f# A" G) w
            return STATUS_NO_MEMORY;; V( S( ]" ?6 p$ X' R) V" _# b
    }
3 z. a% f4 S7 E0 N% |    else0 a9 n9 ?" M& ~$ r* Y! F! Z8 @3 B/ P( `
        pInitNodeArray = 0;
, M' e7 @" b/ N* R) l) s6 O
+ V( C+ t4 d( d3 F    //第二部分;% d+ P" \9 l! t# p: s7 u4 a+ O
    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。  Z/ W* c! M' ]+ W6 I
    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);3 f7 p% F6 O; d. N
    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;+ w; A6 e7 T6 D+ J1 d
        6 E0 B1 k1 }" z# C3 |" o
    if ( _ShowSnaps )
& E+ @/ n2 o5 W% J% r' M: i, P- I3 B, [    {5 c6 ?' c+ h5 y
        _DbgPrint( "LDR: Real INIT LIST\n" );
, ?9 \& ~+ H: u8 Q    }* h' D3 r8 _' h$ |2 b* I
/ U- H' Y, ]$ h. J
    nModulesInitedSoFar = 0;
2 K* v* B- y- B2 s
- Z: \1 B& M' @4 M; P$ ^: W    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块
" b. ], u1 q+ [- ]; t1 }    {
& \. B, ~# k' K6 _5 J  B        ' H5 x. v. a4 J$ a- S5 C8 N
        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块& `0 C+ C' h5 z1 w: l# a0 M
        {  x* Y$ C: j0 }" \, _9 R
            ModuleLoaderInfo  pModuleLoaderInfo;
( ^4 }4 n3 f% ^- c$ h, l            
5 _4 ]! b, B) q0 e8 O            //
, B9 w, c3 h7 ~' H  A            //一个ModuleLoaderInfo结构节点的大小为0X10字节
+ n( N6 u3 y% A8 u. R            pModuleLoaderInfo = &NextNode - 0x10;. @; \0 a: [* x2 M5 E2 }. u
              g  T! x5 R" a/ b0 q; }
            localVar3C = pModuleLoaderInfo;         6 h& X' E# V# h; `  ^2 @' `7 w

6 k2 e8 N4 ]! Y8 x            //
- E+ |& P9 a+ a9 W' y3 e            // 如果模块已经被初始化,就忽略% f0 ^, [- s  }1 b
            // X_LOADER_SAW_MODULE = 0x40 已被初始化! x4 q! W1 s* e# A# U
            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )5 o9 {. {# e0 y6 ]+ O+ K
            {0 r& M  c3 h; Z7 h, w
                //7 \4 o. Q# L: }: O7 m& h
                // 模块没有被初始化,判断是否具有入口函数2 d& U: M) W2 F# Q# z8 m  O
                //
+ R; Q$ Z+ |, {+ f+ i                if ( pModuleLoaderInfo->EntryPoint )
1 V( g$ u8 A6 m4 W2 S                {, H9 X% [6 M" Y2 S0 B' S
                    //
# ?* a- O7 ?$ k                    // 具有初始化函数,添加到模块列表中,等待进行初始化) U- ^, \- z+ V; P4 w
                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;6 o  D2 G9 L+ b: ]
' @/ W4 [$ y0 n* U% C
                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址
/ T& s( n4 Q- o+ p) n2 p      // 例如:. B2 S1 S2 ~. C- b3 L
                    // C:\WINNT\system32\KERNEL32.dll init routine 77f01000
0 l# b$ O3 m9 Y                    if ( _ShowSnaps )9 d5 E0 h+ u( P8 @4 ~
                    {2 S0 w8 z. x4 X# b7 L; \! r, D
                        _DbgPrint(  "%wZ init routine %x\n",
7 N. w: v* l9 w9 N, e. s0 G4 K& O) u                                    &pModuleLoaderInfo->24,
/ i6 K$ V* T# {' T; @/ N3 u* I                                    pModuleLoaderInfo->EntryPoint );9 W0 d% V5 q0 a9 R! X; R" Q; r
                    }
, H4 V3 ^# ?9 v' h2 k. \3 I1 H2 {- |
                    nModulesInitedSoFar++;5 F% o1 R$ u: v
                }! S; Y* e% @9 m" y7 ^
            }4 ~0 A& i7 p* v) u; Q7 i

; V1 F) C7 L  D8 V% e* M* A) E            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。
5 V4 W6 e0 q2 Y$ q            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;+ E9 F8 z3 l2 {, Q) O8 @9 {3 u. k
' l0 K! l8 r* X: I1 T& w8 c
            // 处理下一个模块节点
/ `3 Q8 F: M" i5 p% G            pCurrNode = pCurrNode->pNext( s/ U' G1 n( s2 q
        }- u. _* e  g( C& `
    }
1 k* R" U6 z/ `& j  U& d& A" G    else
( b; H( ?% R- _' J8 T    {
# m* x% T4 Y9 @) y, S        pModuleLoaderInfo = localVar3C;     // May not be initialized???( T  c  B, U" @% ^  D! ~2 x
    }  I7 n( F+ p) e2 M/ B  @
    % }3 y) k+ R6 p( |) [! D
    if ( 0 == pInitNodeArray )
& M0 @0 _0 G' L4 L# w3 s9 O        return STATUS_SUCCESS;$ k$ B# T: B6 {
* D& r; t  Q, Q! D/ W/ I
    // ************************* MSJ Layout! *****************
- b% }' p: q) m! v1 q' C    // If you're going to split this code across pages, this is a great
4 u/ S: z+ d8 ]9 y  r    // spot to split the code.  Just be sure to remove this comment
9 W& Q2 v- v1 C7 M% C5 B  b    // ************************* MSJ Layout! *****************9 I7 V0 e( R# w/ e
   
$ i4 w2 |/ w9 ~# j/ I& M    //
; `* [0 U; j& d    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH
4 J+ E$ k. A3 C% M9 Y3 Y# J    // 第三部分,调用初始化部分  t& Y7 Y7 S4 t* J
    try     // Wrap all this in a try block, in case the init routine faults
% c- u/ b8 i) o2 u5 e& |, m$ W    {. q+ g6 B+ o: h3 a0 I8 F8 u* u
        nModulesInitedSoFar = 0;  // Start at array element 0) v3 n2 r/ w. I4 l# F( I

0 h' G/ l: B  F7 ~2 ~        //1 y8 x. a) a  a( _# k  w4 o" S$ a
        // 遍历模块队列
' X! F, \  a5 l* Y1 w0 }        //% N/ O& r( n# K: Y( R
        while ( nModulesInitedSoFar < nRoutinesToRun )
" \0 Y& P# ^& L4 u# g( ^        {0 g8 a& t8 U* x" }/ a# d
            // 获得模块指针3 ?* V  k/ f2 [: j& b3 S1 L6 C
            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];
% T2 {* e3 U9 Y3 Y6 S8 c+ l; @5 E" n& t4 J( i" j
            // This doesn't seem to do anything...* s1 M, @  |3 L" v) S& C
            localVar3C = pModuleLoaderInfo;
5 y1 `" W5 @+ y& p            
0 y4 P  [) l* b3 P: }            nModulesInitedSoFar++;: ]9 [9 u, e% b+ ~" f* c
                ! R( x1 y( d, ?+ Y, Y0 `4 R: x
            // 保存初始化程序入口指针8 Q/ M7 _: `4 G8 m' @1 T  \0 K
            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;9 q& j  k" U9 f) Q' d6 I) V
            & T' M* j% F1 t0 j( q
            fBreakOnDllLoad = 0;    // Default is to not break on load
8 P, ~! T) R4 a. n4 {
% h& Q! y8 O* u; X8 R            // 调试用
/ {/ `: {. L- W0 [* u  m" k# k, n            // If this process is a debuggee, check to see if the loader! M1 q9 K* {$ a9 V- t
            // should break into a debugger before calling the initialization.
" Q0 G0 ]& A4 H" {! @            //5 r/ l( u4 I! C6 V, P
            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()
; w1 t  K$ @/ Y- `% \" {. V            // returns. IsDebuggerPresent is an NT only API.) M2 Z$ o, d3 F, `  }4 B
            //
" h1 S. N4 }1 k$ h5 v- A9 o4 R            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )
% s/ a% A" v3 Y/ u            {
/ W& c1 O3 b- `( _                LONG retCode;
! U. `! B  d* n" H( s: K( p8 z" Y! A# @& i8 `2 g
                //              
! O% t& D* a3 `( T( z                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\
$ H& e' u9 r( k  a6 L                // Windows NT\CurrentVersion\Image File Execution Options"
% t0 ?% j3 V5 \9 Z% B! D  M9 j                // registry key.  If a a subkey entry with the name of
: P. v9 i; ]& J3 O. Z4 V                // the executable exists, check for the BreakOnDllLoad value.* I' J: D7 H3 ]+ l6 c; J; W
                //
, n& U7 {( I6 E! s7 }                retCode = / j) q5 ]/ t* y- e; x( ^
                    _LdrQueryImageFileExecutionOptions(
; P: {$ i! Y# i6 Y+ r& x                                pModuleLoaderInfo->pwszDllName,* {. L0 H& L( e
                                "BreakOnDllLoad",pInitNodeArray
  k9 X# u/ q2 U: ]- S7 c6 P+ o* X                                REG_DWORD,( o$ M4 N- R6 B: j6 a
                                &fBreakOnDllLoad,
; G7 U; a, h$ c/ ?) d; o8 R2 C! K                                sizeof(DWORD),
  ?( }7 _" I, }& {6 j                                0 );
" S9 I% Y* }+ A5 f2 K3 Q+ W7 v
% w3 ~# T5 ^% z$ G( X5 d4 P3 D                // If reg value not found (usually the case), then don't  ~9 N! Y) g9 g% {; B. T4 z* s& Y
                // break on this DLL init
# P! b# d2 W* }$ A                if ( retCode <= STATUS_SUCCESS )
' K: a( Z( ^! p                    fBreakOnDllLoad = 0;pInitNodeArray
) K3 f1 G) u5 ^" C            }
9 W, O& j5 q) x9 @- w* Z* y5 H            
& u: a( X$ `" ]6 S2 v" U) F2 e            if ( fBreakOnDllLoad )
  W) t' P% f, A7 y  _- f' `) u8 R            {           
" O: _0 o& v: U9 R- o                if ( _ShowSnaps )
0 }( u8 ^) b* ^' `- E( l                {: w0 `4 F4 K8 ]
                    // Inform the debug output stream of the module name
  K7 b# ]3 D( Z) E5 [5 H4 v( `; e                    // and the init routine address before actually breaking
" o3 D4 j/ w, y( M* z2 o9 d" [- J                    // into the debugger9 m" K5 m3 r$ A$ E
( o5 @* x( H- K7 m
                    _DbgPrint(  "LDR: %wZ loaded.",
% k/ K8 Z, g; k/ k7 _                                &pModuleLoaderInfo->pModuleLoaderInfo );
' D; X6 \  b& T0 |6 z: R  ^3 r# c+ \                    
% |" R8 `& T1 G                    _DbgPrint(  "- About to call init routine at %lx\n",
0 |: |, Y3 W. |7 B. B" {                                pfnInitRoutine )
, U# z! x! ?% d7 F8 s3 j                }
: E* ], H" v& ^$ L                # Z6 a6 m8 A2 O9 u5 f" b& T$ _
                // Break into the debugger                                b9 ^- Y7 y, A
                _DbgBreakPoint();   // An INT 3, followed by a RET
* M' d5 u! S3 y4 L            }- X+ A$ N; G7 h$ u9 h
            else if ( _ShowSnaps && pfnInitRoutine )- d4 D0 X3 u& N- a$ z
            {
, o* F3 u  I7 M5 }                // Inform the debug output stream of the module name7 l8 y& I9 N9 s5 C) z
                // and the init routine address before calling it               * _) \" T+ E" }; a1 I0 p
                _DbgPrint(  "LDR: %wZ loaded.",! a/ I' r- V+ \
                            pModuleLoaderInfo->pModuleLoaderInfo );
, J: O7 F. `& D' R& J1 B" p! _* x. b$ Y8 r6 L
                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);) \5 b# q! o+ A
            }
. J$ v! q" T2 u8 k# U                    ! V6 P2 {2 {# ~+ o- q) D: ]$ Y2 h
            if ( pfnInitRoutine ). c6 Y; v* ~) m9 x
            {
, E+ Z$ K- r6 Q! a& c                // 设置DLL_PROCESS_ATTACH标志8 d. U  J5 V% s' m+ V
                //) e# l' O! o# A0 P  T
                // (Shouldn't this come *after* the actual call?)0 I. ?4 L2 k& q- B0 n8 z' q
                //8 P0 T  q6 T) I( i4 ?
                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8            
  ]# e; M3 A: ^8 u1 o$ J                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;# J% R. ]/ v# j
2 `* E: ^3 ^$ B- ]' M/ i* l1 V2 d$ b
                //
/ ?% j# D: I2 ]( H& y" c1 V                // If there's Thread Local Storage (TLS) for this module,
2 V6 {# u. c" @2 x                // call the TLS init functions.  *** NOTE *** This only
5 S+ M. W! ^% u8 {8 [/ T                // occurs during the first time this code is called (when: c. ]4 y1 c" m, K
                // implicitly loaded DLLs are initialized).  Dynamically& L( \, h% M) B" \, a( R/ s# b. O
                // loaded DLLs shouldn't use TLS declared vars, as per the
# a7 s0 P$ H) u                // SDK documentation. r+ I  ?! I9 M+ l" q5 ^
                // 如果模块需要分配TLS,调用TLS初始化函数
! ~0 n2 m7 h- T9 q% o  g- n& D+ h  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时
9 e! ?( R9 F' H7 J6 R) t  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量2 T9 c  C5 A- H* [
                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )+ a, R- K( `+ w
                {( u* r& m/ B  E2 e9 F* @
                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,2 p# J: |$ \: m1 a5 \' ^+ |* f
                                                DLL_PROCESS_ATTACH );+ [' F: I9 h. P+ w( H
                }
( S. O/ N! A4 U6 S: |" N, B3 c2 V3 m                6 t5 L; L& E, Z. w1 ^

, s7 [3 u, u0 T% e8 o  D$ @                hModDLL = pModuleLoaderInfo->hModDLL! A& B# [  i1 L/ o
5 ]& \: e8 y# N; f
                MOV     ESI,ESP // Save off the ESP register into ESI
& w9 x  K+ @* q" U2 F  8 k2 O/ B% a7 Z! d  [& Z
  // 设置入口函数指针                0 h+ C8 \" h' i/ i4 k% d
                MOV     EDI,DWORD PTR [pfnInitRoutine]                     
! m# e7 m6 d) U, F
! {/ K9 Y; L7 O) _6 @                // In C++ code, the following ASM would look like:
2 P% D! E4 l3 m' t) I8 Y3 `                //
: ^# `5 c; `5 ?, w1 m                // initRetValue =; _, O: U" T2 R
                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);( q$ G; o9 `7 v9 z. X
                //
; V; ?; o9 L& x8 Z
6 }( c5 j- P/ `4 U                PUSH    DWORD PTR [bImplicitLoad]
; {$ U* H+ G# s" Q' r! x8 ~                ( M3 Y$ y7 o9 n4 m$ j
                PUSH    DLL_PROCESS_ATTACH5 f% o: l' C( ?) i
                9 {: X" T. x2 {8 G- F& y( u) a, `
                PUSH    DWORD PTR [hModDLL]( F7 {( K8 C3 u1 C% @7 Z; k! j% q7 d
               
, G) ]; s( |' F+ H: V                CALL    EDI     // 调用入口函数
3 p$ P0 u3 e+ r/ s& Q+ _4 K* t               
6 p& [! @! I7 {                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值
- [9 u5 d5 O+ E' h) I- U. c4 U' i' [) Q9 y
                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the/ J: A+ P) S0 y8 n$ u9 z
                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns4 V! {9 B9 {3 d, N+ L

5 ?+ i- v4 |4 _8 T- v, r                MOV     ESP,ESI     // Restore ESP to value before the call
+ H; @& n: q$ W5 b9 Y- j& P! N6 g' k
                //2 F+ J% E9 u; b: F& t& f
                // 检查调用前后的ESP值是否一至
5 _; P3 ~4 q5 o  //
8 h& ]5 B8 @! K5 r. V. r7 j4 O# l7 U                if ( _CurSP != _SavSP )8 y8 A# {0 v8 x$ C1 ~8 H' {
                {
+ {" y0 r9 b) |( u                    hardErrorParam = pModuleLoaderInfo->FullDllPath;$ c- T, D# v5 K9 c, x8 t* s* q  n
- v: K3 R* e$ A
                    hardErrorRetCode =   h* B2 C4 {6 q2 n7 t
                        _NtRaiseHardError(
( \# N- r0 v# j. Q4 T' }& f3 ~                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,. N* _0 g; J1 w
                            1,  // Number of parameters: f5 e( e4 K6 y3 `' I
                            1,  // UnicodeStringParametersMask,' }* o4 s8 X* B. K- l+ {! [
                            &hardErrorParam,% A3 c9 L; q0 R, d4 F
                            OptionYesNo,    // Let user decide+ ~5 k  j: j9 }: F; Q* T0 \
                            &hardErrorResponse );
& w6 N/ T- t: M/ o# `7 [                                            ( w0 D1 b/ y. p( _
                    if ( _LdrpInLdrInit )% s( }: h8 \% `" M  u. E
                        _LdrpFatalHardErrorCount++;
# ~, s8 I7 p0 t1 m& m; g; |' B6 E- @: s. Q/ m) c% E3 F2 L( F
                    if (    (hardErrorRetCode >= STATUS_SUCCESS)
& l% L* y+ ~+ L$ H; Y$ q8 t                        &&  (ResponseYes == hardErrorResponse) )
% T0 {- R7 Z' e% F: B0 E0 x                    {9 F4 c8 F4 R5 _) X' ~* Q
                        return STATUS_DLL_INIT_FAILED;
/ P8 q9 k7 E: D; _7 a9 ~( `                    }
) M# g: p9 E* E6 @/ Q+ x- A; N                }; `- J7 {  D/ s: I

+ ^4 q! x# E2 }4 |- v                //
' |0 v& u# |: w                // 入口函数返回0,错误
% b! ]. x0 M  G7 d8 a4 ]                //
, @. @- H& x: f5 ?, g/ H                if ( 0 == initRetValue )
' d% ]5 S# i( y8 M+ x1 ?+ Z# ]  i1 o                {
# f7 B- a/ r+ i6 K                    DWORD hardErrorParam2;
( E& n$ h* S8 I; _4 c                    DWORD hardErrorResponse2;8 _% F6 y  j' V# B4 }# O
                                        + ~) h- x1 ]" r) q; D4 h6 n6 `
                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;. s0 |* B" z, [. @& q3 Z
                    9 S, b7 N7 j2 z+ r. v/ l
                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,% B9 u1 K( T& f% n' D
                                        1,  // Number of parameters- m! x3 v2 _$ D
                                        1,  // UnicodeStringParametersMask
* E- z3 x; Z" Q4 S* I                                        &hardErrorParam2,& `, ^6 C- J  G5 \1 u
                                        OptionOk,   // OK is only response
* c. _5 {( a; X1 C                                        &hardErrorResponse2 );, \; Q1 _7 l# q3 Q
                                                            
# q6 @# Y: c. \5 u! P                    if ( _LdrpInLdrInit )
1 v9 O. g+ i; B, @& m0 W& O7 d& k                        _LdrpFatalHardErrorCount++;0 |, m8 ?  M" d2 Q# y( z) h
# ^5 _$ X2 b# X9 z4 V( |
                    return STATUS_DLL_INIT_FAILED;( e, r6 g9 b# J! C5 t0 o6 w8 i
                }
% W5 b0 I+ P  r+ t8 r            }% p: ~# F# V) K1 @1 \, v8 u" r1 m
        }3 E! R0 N3 s7 y0 V/ h& w2 q- n

: w) M& `& E; _: ]' H        //
6 @4 V( c/ l0 l        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时
3 u7 h5 m( D$ e$ n" J        //      ' I$ V! r' s: V: ^/ _3 C
        if ( _LdrpImageHasTls && bImplicitLoad )
  ?9 Y( B3 F- I1 I6 H/ p4 u        {2 \. C" S  e$ h7 e/ ?* p: R2 Z' |
            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,
+ B3 T% ~8 T3 y( l) D$ z- Q% I0 f                                        DLL_PROCESS_ATTACH );
: Z$ l# z7 M+ {' c( J        }& G, S! R( k- a: n7 F, ]1 d1 }9 U% ~
    }
' t% K8 o% C; G    __finally7 \9 q4 J6 m5 s" V( P0 A
    {5 P7 S3 n1 k+ ]3 s; g
        //
! a' C0 u/ c' F; @" r; N$ o% e  W        // 第四部分;
% |2 I: W$ l/ M( }        // 清除分配的内存5 u6 m% w6 D3 f  u( x( O
        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );$ _6 K# H/ M+ [8 b( D1 c
    }
0 ~0 T5 |+ W. L- T% {- r; F) E8 F/ n
% C: s% C( p, B1 [6 D    return STATUS_SUCCESS;
# }( O$ v  @( a% ~, s}   
3 J! y/ Z) Y7 V; T# ^2 D- @+ d+ T' K9 ?; N6 f  \' J. h3 j
这个函数分为四个主要部分:
+ E( a3 K3 e7 D; |" m. ]) s一:第一部分调用_LdrpClearLoadInProgress函数,这个NTDLL函数返回已经被映象到内存的dll的个数。例如,你的进程调用exm.dll,而exm.dll又调用exm1.dll和exm2.dll,那么_LdrpClearLoadInProgress会返回3。得到dll个数后,调用_RtlAllocateHeap,它会返回一个内存的队列指针。伪码中的队列指针为pInitNodeArray。队列中的每个节点指针都指向一个新加载的dll的结构信息。
- j( P0 J. A! s二:第二部分的代码通过进程内部的数据结构获得一个新加载dll的链接列表。并且检查dll是否有入口指针,如果有,就把模块信息指针加入pInitNodeArray中。伪码中的模块信息指针为pModuleLoaderInfo。但是有的dll是资源文件,并不具有入口函数。所以pInitNodeArray中节点比_LdrpClearLoadInProgress返回的数目要少。
" ?6 Q+ r  ?) @' G% U# e- a3 O三:第三部分的代码枚举了pInitNodeArray中的对象,并且调用了入口函数。因为这部分的初始化代码有可能出现错误,所以使用了_try异常扑获功能。这就是为什么在DllMain中出现错误后不会使整个进程终止。
* T" E5 L- C  S; T# D  O6 x另外,在调用入口函数时还会对TLS进行初始化,当用 __declspec来声明TLS变量时,链接器包含的数据可以进行触发。在调用dll的入口函数时,LdrpRunInitializeRoutines函数会检查是否需要初始化一个TLS,如果需要,就调用_LdrpCallTlsInitializers。# W) c+ ?. ~' F  M) l
在最后的伪代码部分使用汇编语言来进行dll的入口函数调用。主要的命令时CALL EDI;EDI中就是入口函数的指针。当此命令返回后,dll的初始化工作就完成了。对于C++写的dll,DllMain已经执行完成了它的DLL_PROCESS_ATTACH代码。注意一下入口函数的第三个参数pvReserved,当exe或dll隐式调用dll时这个参数是非零,当使用LoadLibrary调用时是零。在入口函数调用以后,加载器会检查调用入口函数前和后的ESP的值,如果不同,dll的初始化函数就会报错。检查完ESP后,还会检查入口函数的返回值,如果是零,说明初始化的时候出现了什么问题。并且系统会报错并停止调用dll。在第三部分的最后,在初始化完成后,如果exe进程已经拥有了TLS,并且隐式调用的dll已经被初始化,那么会调用_LdrpCallTlsInitializers。
+ Y6 B1 t/ r: y4 P0 z9 r# w; k四:第四部分代码是清理代码,象_RtlAllocateHeap 分配的pInitNodeArray的内存需要被释放。释放代码出现在_finally块中,调用了_RtlFreeHeap 。
您需要登录后才可以回帖 登录 | 加入计匠网

本版积分规则

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

GMT+8, 2025-6-17 10:54 , Processed in 0.086420 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

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