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

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

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url]
) R0 h# {+ X2 J% I2 K0 w9 k3 u; k4 W9 h0 U
WINDOWS 2K Dll 加载过程
2 }& w8 ?, i! N+ j* Ojefong by 2005/03/30
% |4 T! t; t' O0 y7 C这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。
' p# Y/ Z5 T$ N; C4 y7 L7 s" ]在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”。
* O, G1 p/ X2 E" |4 H1 U" l  ]9 Y你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。( O1 e. g* b1 X* f  U
初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。/ V/ g4 h, u( R( s( ^
  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。" h0 f- |& ?1 S0 G/ p5 r
  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。
: X; Z+ _2 L6 Y0 K+ N+ o, _  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):
1 r: z5 g% i0 J  X) z# [/ Q0 i//=============================================================================% ~& z  H# ]4 l
// Matt Pietrek, September 1999 Microsoft Systems Journal9 s" K# Z) E8 T  l5 e, P  k
// 中文注释部分为jefong翻译
/ S( `( l  ^* H. S. C//" u' V( M' D' ]) z; j
// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3)
5 f; g% ?6 x# s/ {//; U; \4 [) [& x* `8 ]( o
// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;: B) c" h/ D4 X
//=============================================================================/ ^; y' Q4 \7 j1 U# D/ I7 H
  x5 A! {5 B0 p
#include <ntexapi.h>    // For HardError defines near the end
9 S( h- G2 Y9 l3 K6 `2 c( V1 X* P$ H" F
// Global symbols (name is accurate, and comes from NTDLL.DBG)
2 `. b% }9 D" q, a//  _NtdllBaseTag' Y4 T- |& o" l8 n& G5 x# R6 t2 S
//  _ShowSnaps) j0 V3 R8 _! l- k' g1 i! H$ d
//  _SaveSp1 G  G3 S* w% K2 k
//  _CurSp
. B3 l1 F  B- J% w! R( F7 Y7 s//  _LdrpInLdrInit' Z* d$ O' d0 j# @6 {' @4 P: J
//  _LdrpFatalHardErrorCount
" a- n  W2 U* @  K# c//  _LdrpImageHasTls
* }4 ~5 G; }( H) ~# J# M8 E$ U4 d5 N/ s: J& |: q% e8 y
NTSTATUS
; u* D' |! |# o2 DLdrpRunInitializeRoutines( DWORD bImplicitLoad )
6 N/ r( v) l# H$ @! E{
% j6 ^% ?1 t$ d+ n    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了
: i& o7 ^4 G: o* m# A    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();4 D$ ?1 O+ d  [( j, ^

3 D4 z6 d# ~2 U  s- v    if ( nRoutinesToRun )! g& C. H( n9 Y
    {
0 o- N- ?6 X: C4 F$ R2 r. e: e        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。0 [" @9 g  A8 U. ^  a2 @' P
        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),
' S2 b0 }) v# A                                            _NtdllBaseTag + 0x60000,  f! n, q0 P- t- l
                                            nRoutinesToRun * 4 );
, T; g+ S- k- P/ y                           
* q- t: J, e1 y        if ( 0 == pInitNodeArray )    // Make sure allocation worked
( p( x3 w2 E: }$ S* |$ j0 v+ @            return STATUS_NO_MEMORY;
) M) T/ n1 X' C  x: E    }
. n) Q8 H+ ?; P0 k# G    else: ]# |2 r% x" ]
        pInitNodeArray = 0;& f0 M7 ~, X9 H' x2 O$ K

  s& ~7 Y" H' l, F" P0 g; V# E    //第二部分;
9 l& A& G0 F/ }3 ~; ~; q    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。5 l4 a6 A% z. b3 Q1 e
    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);
( R9 J9 k2 ~  \$ V    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;, Y3 G+ R0 N7 i0 R2 p5 c
        
5 Z+ q( ~' ^) L6 B* p& q) n; {    if ( _ShowSnaps ); v4 T3 E$ v. `+ ^8 P) C
    {9 [7 O6 ~* y% _* ?1 B. k
        _DbgPrint( "LDR: Real INIT LIST\n" );4 Q0 O/ m6 W) d- s& L. d; d/ O
    }' G% U/ h3 }! t5 ]
0 R! t5 M) Z# @
    nModulesInitedSoFar = 0;. j" h) p( R# M6 X" }) j1 b, f% w
  o' s8 V" y6 V9 V
    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块
3 Q1 \, z4 k: v& E    {
. L/ m$ P, ~' N% s2 S        # X4 b% Y8 E6 S  b
        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块+ c8 J# n8 P; ]8 x' I
        {, U5 }/ _& r+ H7 p
            ModuleLoaderInfo  pModuleLoaderInfo;9 d- D4 E$ Y3 g, [/ Y6 U
            
5 G+ K- Q" ^. P1 D; Z            //# `/ c) e0 E  ?$ k9 `. w- T$ w
            //一个ModuleLoaderInfo结构节点的大小为0X10字节* [8 Y! s& S% w/ Y7 n% H
            pModuleLoaderInfo = &NextNode - 0x10;
5 E9 Z' _0 X+ G1 X- M3 Z            - G  A; v8 t' a- F
            localVar3C = pModuleLoaderInfo;           ~" E. V/ ?$ ^" z2 E

$ V3 W8 J1 m: V6 u            //
( V# `1 E  c8 t+ i  C$ [- K            // 如果模块已经被初始化,就忽略
) |* P% c' G2 \4 L. M+ {            // X_LOADER_SAW_MODULE = 0x40 已被初始化, t: Y2 g" T! A8 u: o# O! c
            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )4 z# m; m3 _% r3 S  i
            {& m! I2 b( _: j# E
                //
/ x. ~! @1 O  b. t( B                // 模块没有被初始化,判断是否具有入口函数
0 O& h2 n5 W1 W' T3 d9 o                //
% z# C7 k: `4 w# W, L                if ( pModuleLoaderInfo->EntryPoint )* a0 Q% P) L! B. [0 F+ M
                {
8 a0 m: K7 y- b& m' D+ P; l7 d9 Y                    //
% j9 Z) j& z; ^# K1 C6 K! y# X% m                    // 具有初始化函数,添加到模块列表中,等待进行初始化
7 n  M) |8 M! B1 a2 h: b3 u                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;+ y' ?$ M! d2 v) U' X
0 p- `% A# S/ E" Q1 y/ G. F/ k
                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址
, q# q) W5 B4 w      // 例如:8 R4 K1 X: b/ _2 ]6 X
                    // C:\WINNT\system32\KERNEL32.dll init routine 77f01000
6 L9 }7 O  ~8 {4 M                    if ( _ShowSnaps )# v$ b; m8 a# x8 P; K
                    {) Y* j- y1 W4 ]; f0 G+ X& X
                        _DbgPrint(  "%wZ init routine %x\n",( H4 {) Q4 }( J9 W1 w/ d7 {
                                    &pModuleLoaderInfo->24,
/ b8 Z' W* E" J3 Q                                    pModuleLoaderInfo->EntryPoint );
5 z1 t4 g+ o, k; J* _                    }2 E- @6 Q* r6 Z7 b, d

2 `- a; I% _3 Q  Y; J3 y4 h                    nModulesInitedSoFar++;
6 W( @& E3 }, i) n4 z, Y                }9 T# i+ M/ M4 @1 G3 Y$ h
            }
: |1 W* d" Y0 x+ w0 {/ O4 E6 t1 K  @+ f0 ]1 m- F" q% _) H
            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。0 r8 \, \* n* l) ^6 t! T- Q5 e8 U
            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;+ [# q6 n5 Y' w
6 t* k9 u1 o( B- G4 L4 L( {; m; W9 r6 H
            // 处理下一个模块节点2 j. v! T5 t" k6 g8 v
            pCurrNode = pCurrNode->pNext
# D* m. ?5 g$ Z& M. ^( l        }
# a1 C& g. }! l9 R    }$ R* `7 i! ~7 z$ ?  V+ G
    else
1 M* I7 {) u) J# z* J6 @1 D, z! Q    {
0 T7 z! N  a  \/ z" l- q8 @' @( {        pModuleLoaderInfo = localVar3C;     // May not be initialized???
! W# p2 @" Z5 A8 O    }
# d: I, U- v% ~' ^5 n1 h  W   
" i8 f: \0 A7 S    if ( 0 == pInitNodeArray )
# ~' C; v; ^* ]1 a/ R% p4 Y        return STATUS_SUCCESS;
: p- B0 t, Y$ n' |, k
& Y' r6 t! f3 Y; A+ c) K/ R. t+ l    // ************************* MSJ Layout! *****************7 u8 ~7 t7 A/ H0 S7 p# q5 u
    // If you're going to split this code across pages, this is a great
3 ?% d) y1 t) F: q9 i    // spot to split the code.  Just be sure to remove this comment
' l0 x0 _: w% }* z4 b9 v$ I    // ************************* MSJ Layout! *****************; l. B" ^. m9 i. Z
   
+ |" z: P) N9 Z. {! W4 ~    //& m9 W  f, [# [" P
    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH
  D$ N7 P! E3 ~- f! W5 d    // 第三部分,调用初始化部分
) u9 A. g, }; n% V5 v    try     // Wrap all this in a try block, in case the init routine faults
0 s7 j8 _. N. y0 o0 m  F  t. U- u% q    {
2 ^7 I; l1 ?7 [2 ?( l( t        nModulesInitedSoFar = 0;  // Start at array element 0) p5 D5 g5 B; {9 s$ j. [

2 x* |5 q) ?( G) y- c; R        //1 t2 d. P6 m. [1 r2 x, P/ O
        // 遍历模块队列
* n5 L7 z1 K! w* w6 r" W* ~* \        //
. a3 I% _- N4 ?+ W9 N        while ( nModulesInitedSoFar < nRoutinesToRun )
6 w: ^6 U9 m% s/ ?9 B1 i$ N" C        {
( r8 e2 R4 y# f4 O  F* T& _            // 获得模块指针& n* R3 p% {, f) T/ [
            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];9 ~* w; }! a2 E7 v

8 U2 p  i) f( S2 C" u2 }            // This doesn't seem to do anything...) w* ]2 f( y( z' I1 z6 E+ D
            localVar3C = pModuleLoaderInfo;( S* M+ F4 B& I* Z3 s6 N
            
- u9 ]# N5 n" O$ [% c( V  E5 V) Y            nModulesInitedSoFar++;8 c/ F) {$ j9 c; r. \
                7 i* l0 ]" n: ^, [
            // 保存初始化程序入口指针) w, W$ o! @. C% k
            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;/ z8 h$ g8 ^! c  B  S
            
0 s. k2 j. f2 w* w- H$ I; E            fBreakOnDllLoad = 0;    // Default is to not break on load
  ~/ z9 Y0 H+ v+ R, {9 N6 W) m% f
            // 调试用
7 l# k3 g" Z5 z6 ~8 {            // If this process is a debuggee, check to see if the loader/ @; C: |9 `  s, Z; E
            // should break into a debugger before calling the initialization.. I9 Q7 e+ C# |  P: b& c7 Y
            //9 B  [& Y9 P2 m- {/ u% E3 `3 ?
            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()- P1 l, b. Q: b7 l+ B
            // returns. IsDebuggerPresent is an NT only API.
- W  R9 x1 i8 y            //* [4 l# l6 h% J3 {& [: t0 K; k
            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )
$ w: i" x- _( j- f6 |" D) Z6 \            {
3 D, O( c4 q5 C7 q, m; \) V- q8 g                LONG retCode;
( [$ I+ E* I4 ?8 x. y: ?. v1 e% {8 U# p: ?0 n* V
                //              
$ X. B* U: D. E; F                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\: x: u' z- ^( d( o* f
                // Windows NT\CurrentVersion\Image File Execution Options"
' F$ {2 J7 _, Y: n& M% e+ @6 G                // registry key.  If a a subkey entry with the name of
2 b, Y8 H8 W- V                // the executable exists, check for the BreakOnDllLoad value.9 k+ r  r* P, `+ t6 T& E
                //
+ O( Z  @, r8 u& T+ o                retCode = # E2 o6 y/ \. E* l9 z5 H- T. x
                    _LdrQueryImageFileExecutionOptions(
$ ^9 o# L: o# \/ A1 P0 b                                pModuleLoaderInfo->pwszDllName,6 N. C- R2 R/ y$ m6 L
                                "BreakOnDllLoad",pInitNodeArray* `5 Z& ?7 Z# s4 {
                                REG_DWORD,
( l! I$ J2 w8 S; n( m* \/ ^                                &fBreakOnDllLoad,2 T& `5 o$ N! W! a9 s# z5 y
                                sizeof(DWORD),
& h" D8 d' B" n; o: n& ~                                0 );
( `/ T& r$ Z, _9 S1 W4 U) u
3 H7 t! ]" }3 I/ X, I, X                // If reg value not found (usually the case), then don't
9 l* m* e  r8 l                // break on this DLL init
, H6 p; w6 {* ~                if ( retCode <= STATUS_SUCCESS )* k2 V) O$ H1 i; ^) R3 A: V
                    fBreakOnDllLoad = 0;pInitNodeArray8 `+ i' h. T$ F8 s6 A6 d3 w0 B, d4 M
            }
. P: J# w% c7 p; M  k            
& {- B- X# O/ ~3 J            if ( fBreakOnDllLoad )
! |# Y5 f7 A2 l5 h8 b            {           
* {8 ?7 K& t; J4 a5 q                if ( _ShowSnaps )
! R- f5 E8 K8 R                {  I) ?' c$ W) v* D: z) z' S4 {7 c) F
                    // Inform the debug output stream of the module name
, _6 f+ i( K' ~4 V  ^4 l" Z                    // and the init routine address before actually breaking
8 X6 N( @$ ?1 l2 ?( a; v                    // into the debugger
5 B! ]/ g% |* V. |
- h& f% S) }7 v+ K5 J; @1 r                    _DbgPrint(  "LDR: %wZ loaded.",
1 b( M+ S( R0 h/ P, a                                &pModuleLoaderInfo->pModuleLoaderInfo );. Z, t8 ?+ Z$ T6 D- e4 C
                    
) _3 \$ W! t2 O8 h# ?1 q0 |                    _DbgPrint(  "- About to call init routine at %lx\n",7 [5 V1 q3 s) \, ~/ K6 O8 s
                                pfnInitRoutine )" [. V0 H! r" S& l) g: ^  K% _, T
                }
, `3 U: v& h* j+ t                / F0 k" N+ A8 y( B
                // Break into the debugger                              
0 ~. S! {. T; x                _DbgBreakPoint();   // An INT 3, followed by a RET
* ^- q% V& o" U* A% ~+ R            }
& f7 i9 u& ]) A* o2 T1 P; Y            else if ( _ShowSnaps && pfnInitRoutine )) Q, E) r6 \# B4 a; @$ B" _4 q
            {0 Z% f1 D! Y1 c# Y3 }: B
                // Inform the debug output stream of the module name$ r- k& A9 f& i4 H8 {
                // and the init routine address before calling it               
- l4 a8 h6 h7 @8 \6 c3 W! b                _DbgPrint(  "LDR: %wZ loaded.",' R2 b) x0 u9 _* \3 C( r# t
                            pModuleLoaderInfo->pModuleLoaderInfo );0 s$ S. L8 `6 @
' F) f. F0 K0 r: R2 t) H7 T! O3 ~
                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);2 I8 N' x" ~% U  S8 {% _
            }
& B- y9 g2 q: v% F% C- V- h1 {                    & b' E% L" s0 Z' @9 t# _8 C) v
            if ( pfnInitRoutine )
# X) w( X/ _- c( g" K7 ~            {. f9 Z3 E9 a' S6 h2 M" l( }1 u8 s
                // 设置DLL_PROCESS_ATTACH标志1 J, R" x1 g9 K* t7 ^- Q! `
                //
; I. G" o% f" J0 i4 x8 |' o7 E1 i: F- g                // (Shouldn't this come *after* the actual call?)- k& ?8 t" b. H0 p
                //3 _3 r: ^7 I4 d5 N5 U
                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8             + b( J* E1 F( |) ^( ]' Q
                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;
  v7 i, t' a% J1 p/ f" h  [- @" X: ^9 I3 }/ a
                //
5 {% z9 F  a  [, z1 P, r& g" F& \                // If there's Thread Local Storage (TLS) for this module,( T6 U. P$ z2 ~
                // call the TLS init functions.  *** NOTE *** This only
( r: s% p8 _- [& W1 ~- k! o                // occurs during the first time this code is called (when! j; @* e6 r4 b0 u; ^
                // implicitly loaded DLLs are initialized).  Dynamically, S+ U- Z1 R% X/ V1 _  W
                // loaded DLLs shouldn't use TLS declared vars, as per the
- i: ]) v1 v4 p# h6 o( S  j. ?2 o; q                // SDK documentation
5 e0 c6 I4 g6 o: \6 A# _% O( [                // 如果模块需要分配TLS,调用TLS初始化函数( P7 |! J0 C" N
  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时
) u: _' X* q; S" g8 H1 x+ D, g  A, u  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量
5 t! k9 V& R; \! C4 ]0 a9 G4 d                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad ): W! b$ `( H# Y6 ~) t8 q
                {
3 ?+ ^# K) g# ~! m                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,
3 \( C/ O$ k! [1 r0 X                                                DLL_PROCESS_ATTACH );: U  v8 Z9 [5 {4 @
                }  K" k, r8 z& S/ J9 W3 o
                , {0 H- h% }: I& @- m# M6 }

5 t% k1 ?# R, u( w                hModDLL = pModuleLoaderInfo->hModDLL
6 r, u' ]: z5 P# u
8 v9 |) y! V2 o- P                MOV     ESI,ESP // Save off the ESP register into ESI: ^2 Y0 t  R' O' q5 S0 M2 k1 T! x
  ' l( N# \6 Z. v; W3 X) i0 D# u
  // 设置入口函数指针               
  w* c7 Q% s# _9 @, u* G                MOV     EDI,DWORD PTR [pfnInitRoutine]                     
/ M( z! Z4 x' q5 ^2 V/ b+ d( M) n8 i% h! p: N
                // In C++ code, the following ASM would look like:
/ V& r. k" ^. x1 C1 E+ G. g                //
; c0 {: b1 I. \1 j4 o                // initRetValue =
3 g/ D7 m- b# p4 K- o% l. p                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);3 L7 |7 D5 n' w( @9 N! p5 R
                //" Q8 q4 Q5 y1 E& A
' C$ M6 [7 U& _  i- B
                PUSH    DWORD PTR [bImplicitLoad]9 \  ]5 P5 `5 I& \) @0 G9 T8 T
                  X7 _2 ^7 K# ?& [# F
                PUSH    DLL_PROCESS_ATTACH
8 S2 k6 _  ?9 ]# R2 a. D                . T, h6 A( A0 }# I( w  ~+ J
                PUSH    DWORD PTR [hModDLL]
" |/ \1 i1 V) X& b( I- E                1 M) N8 K5 ]0 L9 N! X4 p& q
                CALL    EDI     // 调用入口函数& d, N" Y7 S4 s# Q5 |  J
               
( _& M# ?6 [, m: T' ^+ Q6 t* E                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值$ g! B6 n) J# r: W! y

4 D- [2 R% n4 n6 [( I                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the0 H# ^  A, b( r7 E+ W3 g
                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns
. g6 d* J$ R" ~7 Q) a- A4 D+ l, y8 Q1 `/ ]/ `8 D, N9 [
                MOV     ESP,ESI     // Restore ESP to value before the call
% h* j( @  }! ~# E9 o  Q7 ]) Y3 D+ M" J8 I1 r+ Z
                //
6 j. {3 x- L8 y1 U. G  \, S                // 检查调用前后的ESP值是否一至
: _# a% {( x& q- K  //
" N, g2 V- i" o2 ?3 H                if ( _CurSP != _SavSP )
5 O* N% |$ Q1 w$ Q, a6 _5 v                {) \2 s6 J( \# V4 k. J" p, V" B3 f
                    hardErrorParam = pModuleLoaderInfo->FullDllPath;! d( |" t" Z* \5 p6 g( k

9 j7 I9 }* `1 Y! o  X                    hardErrorRetCode =
2 |3 ~# Y3 Y) o8 \) r5 V                        _NtRaiseHardError($ \) P. z( y3 S# i5 R8 G$ x
                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,
$ s& ?: U7 r' N2 A& w                            1,  // Number of parameters2 h! {* q7 ~  k
                            1,  // UnicodeStringParametersMask,
. S* U- F4 G5 Z/ V. w                            &hardErrorParam,# G' a1 v( W  \3 B  [0 R% S, ]
                            OptionYesNo,    // Let user decide
; `5 L* v2 w7 l, B9 o                            &hardErrorResponse );
3 `+ F9 {! j  g1 ~% x- E, V' A                                            
. f( y( O9 t- q: u5 H                    if ( _LdrpInLdrInit )  ~4 c& G# w! ]9 K$ ?3 X
                        _LdrpFatalHardErrorCount++;, I! j5 K+ C1 p' Q; H* U7 [5 d
2 S1 ?9 c  Z; e( ~) u- N; C
                    if (    (hardErrorRetCode >= STATUS_SUCCESS)" o! l5 i/ U- c& E' C4 e2 H1 H) i6 J% |
                        &&  (ResponseYes == hardErrorResponse) )
! U( v5 Q: f' h+ _* o4 L                    {4 k# b& z- F8 }6 @( T$ r! t. }+ j+ s
                        return STATUS_DLL_INIT_FAILED;9 C8 i. ^: ?1 j) M: P) i! ^
                    }8 q( Y% Z0 u3 j
                }! e' e% w2 |+ q$ N) O" D

  j: R) f  B/ u: |9 z( t5 G                //
9 g8 V" Y0 a3 r. o. f                // 入口函数返回0,错误  q: g! \0 p0 s
                //5 q, U8 x% F; u* ]/ x4 g) {
                if ( 0 == initRetValue )
' G( V/ R' S& s3 t( `! X                {
+ Z6 d1 p  A0 M                    DWORD hardErrorParam2;, {1 B4 W' z0 Z. J/ ?
                    DWORD hardErrorResponse2;
1 [6 h6 Y. B' s6 q                                       
! Y/ g& a& [& W6 D                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;
  K) Z- l5 O  Z3 |+ a8 Y                    
/ S6 z+ N: O* B' R  p                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,2 ^% N4 t& _1 _% D
                                        1,  // Number of parameters- S8 x2 X. i$ c& O8 ?* z
                                        1,  // UnicodeStringParametersMask
5 v* a/ E! z$ a! Q" _% Z$ {( F- K                                        &hardErrorParam2," m( P* O$ ~, M- ~
                                        OptionOk,   // OK is only response8 L  `, O8 s! W; ~9 Y  Q# D5 s: x
                                        &hardErrorResponse2 );
. l8 F' G+ T$ a5 b! v' @                                                            : u2 S; d/ a: |* q. v
                    if ( _LdrpInLdrInit )
2 y3 t4 _4 W# N! f                        _LdrpFatalHardErrorCount++;
* R8 W) k0 n: H4 d
) @; x" r- c2 Q: J+ f                    return STATUS_DLL_INIT_FAILED;
/ E5 C9 u. |6 j                }
/ Z9 a* P9 k5 F$ ~2 ^% }            }
% h( e0 B! e# x        }# N$ v2 u( D2 k. V% P

. J- C" f7 W( z6 ~        //- p& R3 j6 h) b9 B( G) @& f& v" r
        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时
5 ?1 w) S) l5 e( j) m  s        //      ! i) q0 @3 Q. Y1 l4 m6 e
        if ( _LdrpImageHasTls && bImplicitLoad )  A. V7 T6 l5 ~# H" ~, P: r9 I/ l0 [
        {
- }! ]2 Z" w, O5 S5 L" l            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,3 h: R2 A% X- r. b8 N7 N0 h$ Y
                                        DLL_PROCESS_ATTACH );
% z. S( W3 D% p$ V4 A        }" q$ n8 m$ g6 K6 m6 F! L
    }  M9 J1 n2 A% p
    __finally/ x+ D$ T, s6 Q, w- }
    {# O; s7 P' I9 _! A- u! C$ Q1 `3 Y
        //- H! h1 `8 ~8 W0 U2 v7 U
        // 第四部分;! x- r+ m$ q9 H8 R2 J  F+ l# _- P
        // 清除分配的内存
1 J! M) b5 S) ^. t" n7 M4 B        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );4 H3 J  V% e! G5 ]( _/ R  M
    }" w. {' e; k) ~. J/ [: F
; d6 p4 l9 i  s5 s2 _+ u
    return STATUS_SUCCESS;
2 I7 o1 c; s$ |- Z" ^" i$ d}   4 c8 v* }; m& i1 j$ f. H

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

本版积分规则

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

GMT+8, 2026-3-15 05:22 , Processed in 0.168070 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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