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

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

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url]1 w- I$ d4 w, C6 Y5 y+ L4 x% R
# u( E" j$ N  F& p0 z) X
WINDOWS 2K Dll 加载过程2 a9 I$ R1 @, s3 v. [) {: g
jefong by 2005/03/30
7 x8 x5 c9 W9 H8 R4 {$ i. W这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。
' z' m1 i# {; n+ F在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”。
: Y& f. X% {, @你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。9 g$ h: ]; c) B) _. w0 {: W
初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。9 i8 V2 s! ~; P* ~0 x
  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。9 @: v+ y+ ]4 r$ L  \
  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。
/ H" ]1 A; z8 M7 p; e) m  N  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):
9 O  b$ }$ @2 w7 N7 y! [//=============================================================================3 v5 P; n4 T9 s) W0 h
// Matt Pietrek, September 1999 Microsoft Systems Journal
5 Y% e, j/ c1 r* X6 I// 中文注释部分为jefong翻译/ L' p; G( y1 f% n
//
9 k4 v/ c9 |2 G+ g// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3)
8 \- P9 U% r! L" \: t; y, ~! h. n//
1 f: b: H( O8 {/ W; p# C# o// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;1 h0 C! N0 ?3 G$ [
//=============================================================================
' L% h5 Z3 ~( Q0 B. ]7 C9 \& @/ R1 I# \' @; E% A, }: X  }5 i# X
#include <ntexapi.h>    // For HardError defines near the end0 T1 w' p8 Y) k$ B; k: a

0 Z1 C: s" |1 h; a, U' i& `// Global symbols (name is accurate, and comes from NTDLL.DBG)
9 H; a: I, p3 r! ^5 s) i+ j; V3 ?$ Y4 S//  _NtdllBaseTag# H3 C; _. u8 A2 z: D: z$ \
//  _ShowSnaps) J" N9 {! g. H) y) q2 i
//  _SaveSp
( G+ Y5 m; d! [//  _CurSp
) O! z" p+ e- ~! M//  _LdrpInLdrInit
$ `: Z; l5 N9 `//  _LdrpFatalHardErrorCount3 H8 [' f1 F1 {& K
//  _LdrpImageHasTls
+ t" T  q7 X5 _
" g8 Q/ {4 H6 Y2 ONTSTATUS
! W7 B6 A2 t) o1 a, E- j( ~4 zLdrpRunInitializeRoutines( DWORD bImplicitLoad )
$ z* `) ]* Z# `3 W{9 k4 R/ D/ H% A7 D3 m  g
    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了# D0 L" n. t9 @1 y; y& ~
    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();
6 Q" y: r  f& H, D: n7 m
: U) @* g) p4 b# q0 l    if ( nRoutinesToRun )  e; y  I4 u, P: T, O4 ?# n
    {4 c6 c' x* ^: B
        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。2 \( E1 @+ O: x0 D3 w' T( V6 V
        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),
$ k3 Z8 b5 W5 D% ~* k" F                                            _NtdllBaseTag + 0x60000,2 {& f! Z) u9 D3 Q" I3 N+ \
                                            nRoutinesToRun * 4 );
4 V7 C+ e! G" a  C0 t                            + O8 g' O/ l0 a% ]3 e1 A- l4 U. D
        if ( 0 == pInitNodeArray )    // Make sure allocation worked
2 I. T" o  q3 [            return STATUS_NO_MEMORY;8 c- R6 ^! b& C6 H7 R
    }3 D1 k& p* g' O1 f1 D
    else4 R6 K4 i2 A2 F
        pInitNodeArray = 0;
1 U+ o1 _3 |: V5 d# m. p4 I7 X- h+ f, _! d2 R
    //第二部分;! y1 s3 S4 J% L4 K; C  S, r
    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。
( J$ d3 \& z5 N% b* Z! R7 e6 d    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);
7 v; Z) n: q5 w: ]3 {) \: X    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;
/ V3 i6 l9 r. R/ B. q8 o        
. r+ o) |' d( j  j% c$ L5 t; _    if ( _ShowSnaps )
% @( E: p; |" P  C3 O8 z    {
: Z+ S6 J1 d7 Z1 |' D/ x        _DbgPrint( "LDR: Real INIT LIST\n" );; }" u6 O/ M/ k# J8 A& q
    }8 V/ g; M9 s* {( o& {' n
6 y  A0 N* u& |6 H
    nModulesInitedSoFar = 0;$ E. B+ ~4 c, d% P

7 E5 o5 E6 R) f    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块
/ h3 j6 K  R* W6 p2 s+ t    {( L2 o, t1 T9 t3 k$ E: C
        7 n  ~; g" v" l4 B* _
        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块% \: {# j$ C0 n0 }+ W8 H# O
        {: u1 S$ @( P4 B- ^5 T0 `# q) d
            ModuleLoaderInfo  pModuleLoaderInfo;
4 ]  S( q" m2 p. K, @- e$ L9 y            2 A2 Y% e, Q7 r, M* n6 c! x
            //! J' `# b( t9 Z7 V4 B- J/ @
            //一个ModuleLoaderInfo结构节点的大小为0X10字节
% S0 Y, T! ?" {# P' D            pModuleLoaderInfo = &NextNode - 0x10;
2 Q! f$ I! z5 [2 Y# c4 X3 R- @+ I( l            ! [. C  d6 M8 f6 k* l' g
            localVar3C = pModuleLoaderInfo;           B& L9 K2 R) }" ]3 o$ ]5 D2 S

6 ^* M7 Y1 P4 v! V) O; S            //7 h. q  c1 ~4 X4 ]
            // 如果模块已经被初始化,就忽略/ f7 O& S+ H+ `" s/ O; v
            // X_LOADER_SAW_MODULE = 0x40 已被初始化% Q  d) q0 C7 J
            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )2 c8 p' x' U* G) ?1 _+ _: g
            {3 J: b8 m# A* y3 g) ~
                //4 T; D" v4 e3 |0 X' D
                // 模块没有被初始化,判断是否具有入口函数
4 |9 v  z3 A6 ^; N% V0 H                //3 c2 _" J$ r. N. x5 p* a
                if ( pModuleLoaderInfo->EntryPoint )/ ?6 `& \/ s% R0 O
                {1 y- A7 L1 y5 a9 k8 G* a+ h' T! p8 F
                    //; c, E. s2 Z' B* x3 C
                    // 具有初始化函数,添加到模块列表中,等待进行初始化
5 N4 r" l) L$ N" f# c6 b                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;
1 k  ]: l% I) j6 [
0 O2 C9 s9 H/ e$ ]" }- e9 v( I                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址/ N% b7 z- M. \+ t, o( {
      // 例如:
1 D0 B8 N. z& g2 ]4 ]                    // C:\WINNT\system32\KERNEL32.dll init routine 77f010006 i  H. o6 K# f/ |: K/ G$ N' `
                    if ( _ShowSnaps )
2 X: T$ F4 Q6 @; d  H  ?! L- Q                    {# d. S$ f  A& i: z
                        _DbgPrint(  "%wZ init routine %x\n",# k& b' F( d* ~; L9 Y
                                    &pModuleLoaderInfo->24," ~7 ], N; e$ x( [' ~1 H: A  j
                                    pModuleLoaderInfo->EntryPoint );/ j. G6 J) S9 o, P; h' U: f, j, k& y
                    }; U" F0 C5 T, O6 I) g  q
* D& q  R/ N( p0 C; m& a5 H
                    nModulesInitedSoFar++;9 m  h7 w( @( }# c& M! X  I( s
                }  G* @6 |, W9 l& v5 h6 T* M
            }: x+ t" E9 i$ i0 Y  \) v- m
1 \3 N/ W8 c# T( ~9 L8 w
            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。
% }7 g' I& G. h! m, c% z8 r5 m            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;, z, w5 \: W( U1 E' J

2 x' m  J& {9 M. R) q- M7 H            // 处理下一个模块节点
2 i9 L9 X; T7 ~( B# Z2 A- p            pCurrNode = pCurrNode->pNext
; e4 q$ S7 \  e% u6 ~        }; c. l! R, W- W; o
    }0 K: T6 @) E0 h# c- K
    else  a, \$ j8 ~: o& A" G- [+ P
    {
/ }8 E& K. e' ^        pModuleLoaderInfo = localVar3C;     // May not be initialized???" b# w* D' o* @, K. M
    }
  ~' s) C; |# K. V   
) {+ d1 n( M+ h    if ( 0 == pInitNodeArray )
- {* v2 U7 I8 M" ~        return STATUS_SUCCESS;' n$ m2 k0 I6 K9 u% N1 j9 W$ r

& ?  Q. w& b. W7 c' }" `$ B( j! u( }    // ************************* MSJ Layout! *****************9 o) Y% h& `/ s4 r$ f  O/ t% x
    // If you're going to split this code across pages, this is a great
4 I8 r0 {% v' C# s. k3 y2 {1 X    // spot to split the code.  Just be sure to remove this comment
# h8 S0 _8 F& ?4 j- p5 d7 S1 U9 ^    // ************************* MSJ Layout! *****************
& X: c' Y! R9 X2 Q) [, \, D) V    : `. \( t. M! @2 C! K" Z' W
    //7 R7 U! `4 n+ c' H6 Z5 d
    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH
+ t% c; O5 d# u, `/ P* p: [    // 第三部分,调用初始化部分& o  b1 j- _# A  d$ G; o" \8 U
    try     // Wrap all this in a try block, in case the init routine faults, \* d9 r0 B- [, k+ z; p
    {
& y* F; N/ }. ^8 B        nModulesInitedSoFar = 0;  // Start at array element 0. a* O4 W3 d* n- d
& A4 m& b# `  \
        //
6 d3 M3 V) j9 }- j5 q" F        // 遍历模块队列
8 B& P: I: w5 `" G        //
/ D6 L/ F, A! u1 I9 M- X8 K+ P        while ( nModulesInitedSoFar < nRoutinesToRun )
2 {+ A: w  `# `        {) H) ^0 ~# N9 T9 o( J: h; I
            // 获得模块指针1 k  g+ ?' K- w9 j
            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];
; B3 z2 O0 Z+ h
. P. g# F* ~' B) {            // This doesn't seem to do anything..., t; {3 d* x0 h* k$ o9 l
            localVar3C = pModuleLoaderInfo;; D% o- C  O. R# Z, |6 [4 }5 p
            
, n: T+ _/ j$ y# Z: ^! R- R) ^+ z            nModulesInitedSoFar++;7 n. k. I1 o3 A5 w) ^7 Z
                + x8 A: t' E8 _/ W  q/ R
            // 保存初始化程序入口指针
3 v/ Y* m/ y, }8 r: m0 o            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;
4 w7 A* Z3 p$ q# P% z; c            4 P+ P/ _9 F5 I" @$ r+ F
            fBreakOnDllLoad = 0;    // Default is to not break on load
1 A: }) x7 ~8 `% B! V
# G8 \0 V3 I+ y/ M            // 调试用
( Z0 y9 b" C; ~  Q7 T5 O" [            // If this process is a debuggee, check to see if the loader8 ?2 x  d4 E* k* a( l" A
            // should break into a debugger before calling the initialization., K3 b0 Z, U7 e9 K+ c, i  l
            //$ w0 j. x; ]2 [
            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()
4 Z6 S! a! w. \            // returns. IsDebuggerPresent is an NT only API.
! d: a& T; m5 X0 p3 {2 |- \" m" T            //
' q9 c: w9 O, `; \6 s( F            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )7 k; a) F; p9 J* d7 K, F! N" F* _# \
            {- S4 L& f+ ^7 O/ e
                LONG retCode;7 ^3 ^: \; o7 T* x- _

7 V+ ^5 z( {/ m8 N. x7 x$ k                //              
- ^; {( Z7 c9 \$ x  D                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\
6 {  a. ^7 ]4 P                // Windows NT\CurrentVersion\Image File Execution Options"
9 q, |) M" s0 N0 ~+ W                // registry key.  If a a subkey entry with the name of
+ F) b' Q7 {+ R6 ]- s& |; C                // the executable exists, check for the BreakOnDllLoad value.. B5 d3 s7 k# L6 l/ W0 S8 u+ q
                //
0 O2 g' i( Y& A0 A- {! \6 p8 I; D                retCode =
2 q) [3 x+ V- u7 ]8 l. {  e                    _LdrQueryImageFileExecutionOptions(
7 c2 i) e, Q) w5 M                                pModuleLoaderInfo->pwszDllName,9 O4 v4 x" ~8 `, d0 I/ G
                                "BreakOnDllLoad",pInitNodeArray7 [" s+ m: v" T2 |& X+ a
                                REG_DWORD,+ j" P! I  ?  m) i) v5 d! R
                                &fBreakOnDllLoad,
: v- ]" H& G) j+ p/ `                                sizeof(DWORD),/ x7 }0 l) o, p
                                0 );
  \4 t( o4 d) b3 m$ e( V- H
8 M& q! `/ o/ ~" s( k                // If reg value not found (usually the case), then don't6 a8 v4 ~0 ~  I( U  _3 U# V  B: X
                // break on this DLL init0 h- _! i) c% r8 p1 A
                if ( retCode <= STATUS_SUCCESS )! D2 _" K& \0 R! K1 F) N
                    fBreakOnDllLoad = 0;pInitNodeArray& U, i7 U( ^, F8 t8 d
            }: D2 T. y" R5 J" P
            
5 D' I! H6 r# F            if ( fBreakOnDllLoad )) `& \$ f/ i6 @4 ~) h& z
            {           
9 ~5 ]* i( n; `% Y                if ( _ShowSnaps )2 B2 n1 M4 U6 x8 M
                {
0 i" A. X, J! s                    // Inform the debug output stream of the module name
2 H  b1 }: B/ a1 r                    // and the init routine address before actually breaking
# _) U6 }7 z+ O) [, D$ R8 \. f                    // into the debugger
" q# m7 `& s0 {% g( Z' x& _; d7 s6 f2 R8 E* @
                    _DbgPrint(  "LDR: %wZ loaded.",
# T. D" X: D5 S8 @  A" _2 B                                &pModuleLoaderInfo->pModuleLoaderInfo );
! ?' e# x) A! L& r" A: v& s                    
0 ~$ `5 S% A6 g1 @                    _DbgPrint(  "- About to call init routine at %lx\n",
8 ?( e: O$ H# @* A  t                                pfnInitRoutine )
$ v  w' o% [. ^, C; ?% n' {9 D, B                }- G% e& h& O' y% E& U' l2 x+ h
                " Q* w/ w: E" E7 r2 x, z) C
                // Break into the debugger                              
& t& o; z, d8 S$ E7 J                _DbgBreakPoint();   // An INT 3, followed by a RET- d7 r/ t9 T! s7 |& O1 k1 O
            }2 M! A) R& i1 x/ T! P
            else if ( _ShowSnaps && pfnInitRoutine )) Z3 v; [( V) [
            {+ X0 q' T) B) |: }- g
                // Inform the debug output stream of the module name* }3 y# \8 l' f, }$ c& ?
                // and the init routine address before calling it               
0 k; k* J0 A& L' }. n. w" j* p                _DbgPrint(  "LDR: %wZ loaded.",/ z- `- O6 P+ D: r3 B
                            pModuleLoaderInfo->pModuleLoaderInfo );. V5 O4 x- B/ C& D2 i
6 |& E' l. h2 G7 P2 ?
                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);2 p" N! F% O1 J) l, \9 n; ?, p
            }# a, f. r4 h1 d* u/ I
                    % g; d: X4 V8 S' `' H
            if ( pfnInitRoutine )
$ b' N2 P9 s  E  q            {9 c1 q7 W$ G6 ]* E/ T
                // 设置DLL_PROCESS_ATTACH标志3 Q: x. ?5 n. l" Z
                //
4 @. m0 r7 Z$ d% G3 K                // (Shouldn't this come *after* the actual call?)* ~1 d. W( ?6 Z1 H* l4 k
                //
6 N% N6 o/ X; b# S: v                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8            
) P7 b/ L% E. ]. {                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;
4 D8 R9 y9 N8 g! b
% M) I. U2 v, u2 r# K1 V                //2 p# C% _: o- N0 X& Z5 T/ R, n
                // If there's Thread Local Storage (TLS) for this module,3 U4 a% S7 |+ V( Y1 {
                // call the TLS init functions.  *** NOTE *** This only
7 q8 C/ D: f4 e. B! \) _                // occurs during the first time this code is called (when
, @/ P' T0 e2 Z6 G                // implicitly loaded DLLs are initialized).  Dynamically
% g# v; K2 G. }5 n; |                // loaded DLLs shouldn't use TLS declared vars, as per the
3 a) b+ N# ?* [( y3 P5 B8 |" }: y                // SDK documentation
: J( e- {! j/ M% V0 t3 S                // 如果模块需要分配TLS,调用TLS初始化函数0 V) H/ @4 e- s2 {+ k+ U
  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时! D+ e4 l3 P. b0 L1 W# N, @
  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量
% e' N3 w8 {$ w# M* k6 v                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )
  o  P1 b3 H7 N( i+ O                {
: G' d  b0 u7 c$ u+ N3 {                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,
# G; }" }; D% D3 v4 K                                                DLL_PROCESS_ATTACH );3 B  F8 Z9 H6 y# o
                }  I$ P4 f5 `% O: Q9 L0 r/ {
                1 {% p6 K  q8 R2 Y

  G& {* J" U: y- d2 ]+ D0 {                hModDLL = pModuleLoaderInfo->hModDLL
9 W; w1 C7 W6 A/ l3 e) ]' H. X/ G: P2 b" i
                MOV     ESI,ESP // Save off the ESP register into ESI
& R; m, F4 Q  q  
! w1 I( y1 `0 P, g' Q) _) ~, a7 ^  // 设置入口函数指针                ' {3 P( p! e( y& Y$ [6 [
                MOV     EDI,DWORD PTR [pfnInitRoutine]                     
/ j' d3 D. h; R- m4 t" y7 z7 f
! V$ V& e, h% c1 E& w( R                // In C++ code, the following ASM would look like:
3 G! Q3 W# K' R. O0 b! p" ]/ }- |* V# Y" T                //2 P9 X& y# ]7 w" ~& g
                // initRetValue =$ t% E' y# @8 e1 n2 A
                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);
9 S' e( }% z; o4 ~0 N! G                //% }" g- M2 B6 j8 e& d
* |7 i# `4 W- A
                PUSH    DWORD PTR [bImplicitLoad]
5 h6 s; m" r: P8 H7 S: A- a                ! _7 X& z! [* D
                PUSH    DLL_PROCESS_ATTACH% J2 F2 l2 L2 J1 j4 w
               
6 d0 }* ]+ Y# h& e1 s$ o* P* }                PUSH    DWORD PTR [hModDLL]2 I; j7 I$ E% q( h5 V. c4 I) f6 E; K
                " U$ _& i: d  f
                CALL    EDI     // 调用入口函数$ y) ]: @5 U2 T: ]+ ?. B
                3 J9 ~2 N. T" o& r: b# r; a3 K/ N
                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值
# }: I* k; t+ C2 e3 }) C* t9 A! {! `! J7 [# {1 Q' `/ Q
                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the* }) y' C7 I# r
                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns
- s, ^3 P, `& Z* V1 z# j% n0 K; U: o1 X. w* H- u0 ^- S
                MOV     ESP,ESI     // Restore ESP to value before the call
. D: J) G+ L( R: L; T4 x# ^! K( _+ V3 @$ k: l
                //$ H+ o. z: y( t4 b, e
                // 检查调用前后的ESP值是否一至! p! ~  Q: G6 x' j6 [3 m0 K9 t7 I: ~* w+ Z
  // 0 A& M; U: C" |: H- f( ]0 h! |
                if ( _CurSP != _SavSP )3 W0 x# u/ i) Y& m( `3 O
                {
) O. [2 `! u. {                    hardErrorParam = pModuleLoaderInfo->FullDllPath;
2 X7 ]8 c' I9 N# }# U( y. ]. x
( K5 |3 m5 C  T$ P% ^" Q! o                    hardErrorRetCode =
2 p1 V! F1 d8 o" k1 W                        _NtRaiseHardError(
  U; U" v; o1 D                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,
1 b# B5 D! z! F& H' s7 z" q' u                            1,  // Number of parameters
% ]$ ?  ]- s: n0 }* Z                            1,  // UnicodeStringParametersMask,
7 ~! p, `; [. G  q+ }                            &hardErrorParam,
" X, V) W  ~- y$ c) d                            OptionYesNo,    // Let user decide8 U0 @, s/ `% ^3 T# @5 q
                            &hardErrorResponse );
: G# S" F- z5 v) }' m& c, t0 T; W                                            
% v/ a+ h0 V' w. l7 Z                    if ( _LdrpInLdrInit )
% p& k) @2 B3 V; B2 R. g                        _LdrpFatalHardErrorCount++;
+ _# I5 v9 Z3 A1 h  J1 }1 d
! q" p- ~/ M% C0 A1 b' d: N  w                    if (    (hardErrorRetCode >= STATUS_SUCCESS)
2 O+ I8 p' L  J2 W. y/ D                        &&  (ResponseYes == hardErrorResponse) )
5 a! B, z5 M0 c/ ^6 \2 `                    {
" v% b# A6 g- a" q, f+ c$ r                        return STATUS_DLL_INIT_FAILED;
9 ~  X$ W& ~; _  E3 z                    }
! \  @- v9 U0 ?/ T                }
: s% L" k4 i  l! [7 S% Q+ ?* V
                //
: V8 S( d) D' e$ ~                // 入口函数返回0,错误
& H! i5 E& `/ s$ L2 z6 ^' P% X8 L                //
/ Y' W) @* G; G0 `* q                if ( 0 == initRetValue )
  ^3 \. u7 }1 e! J8 p                {) M& K# c& h6 I7 _# q* t* x
                    DWORD hardErrorParam2;; l/ s  W# ^7 X7 h% y5 e
                    DWORD hardErrorResponse2;
8 R' P9 T0 ~) J: |3 g                                       
. E1 i$ B: v) B$ k6 Z: i+ V4 [* f                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;
3 \  {0 p- w1 u2 Y                    ; F4 Z# m- {. r0 l7 Q9 s
                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,
( B3 `3 c3 I& {4 s! G                                        1,  // Number of parameters) H, [/ }- H$ p, z- o: u7 }$ g# G
                                        1,  // UnicodeStringParametersMask
- d/ o, \2 N6 C% \2 C                                        &hardErrorParam2,8 U% L7 i/ A* O( ~
                                        OptionOk,   // OK is only response
! g$ o/ e. j5 \+ T! M7 S5 p                                        &hardErrorResponse2 );
; K9 \  F+ L; V( I2 R: J) S                                                            
/ w9 \) u1 v1 W) @                    if ( _LdrpInLdrInit )
+ l8 ^( g, ~+ e: x& T' e4 M                        _LdrpFatalHardErrorCount++;9 k, x. y/ p$ I5 {- }
2 Q% R* J/ A! @9 i/ f
                    return STATUS_DLL_INIT_FAILED;
9 u+ X1 r! A' n. c# y! j! I3 u+ J                }
: Q8 Y; B+ T, n  H8 \$ R7 z4 L7 Q            }% K: Z2 B! y' g% ?* ^* e3 t
        }% B0 a  F) H; V( n

1 a! H' Y0 J  z! h* @        //
* Q+ ~8 A( |: y/ X) j% G        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时+ \# U2 k4 h# n. r  [; v0 l2 C
        //      3 G& W# o3 V+ t& |3 D- ~1 @
        if ( _LdrpImageHasTls && bImplicitLoad ), K" M+ n$ g, ]9 z& J5 a
        {/ w1 `' i- V- B. l
            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,5 ]4 V( I7 J( _
                                        DLL_PROCESS_ATTACH );/ J0 b- f2 @( J: e7 H5 j
        }
! _( R4 B/ y- K; ]. c    }9 N0 f4 F1 N( }* N, x$ s: p
    __finally
- x8 a% _$ ~9 ]: O" a& W# ^+ z    {
8 u" ~5 ?" v/ v. c: X8 T        //
3 g- H: t) C9 v. b5 F7 {! Q        // 第四部分;; \, |6 P2 V7 q9 e& N
        // 清除分配的内存
1 b/ W) l: K6 c  U" C! l* c4 K        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );0 p1 S. M& b! S  }& Y: V( X+ t
    }. A) r' |# y6 {2 J
: U5 o. u0 g3 m, W6 G; u8 d
    return STATUS_SUCCESS;3 o3 |3 c" W* f0 g/ l7 `
}   1 ?4 ?# l" Y& z. M8 T( z) }) z4 K
, v0 v/ Q9 y; U: b, C" [* s
这个函数分为四个主要部分:
/ q: U: q) u% u8 I0 u7 Z9 ]一:第一部分调用_LdrpClearLoadInProgress函数,这个NTDLL函数返回已经被映象到内存的dll的个数。例如,你的进程调用exm.dll,而exm.dll又调用exm1.dll和exm2.dll,那么_LdrpClearLoadInProgress会返回3。得到dll个数后,调用_RtlAllocateHeap,它会返回一个内存的队列指针。伪码中的队列指针为pInitNodeArray。队列中的每个节点指针都指向一个新加载的dll的结构信息。
1 g- }1 t  ^" }: D" e% [! Q5 ^; Z二:第二部分的代码通过进程内部的数据结构获得一个新加载dll的链接列表。并且检查dll是否有入口指针,如果有,就把模块信息指针加入pInitNodeArray中。伪码中的模块信息指针为pModuleLoaderInfo。但是有的dll是资源文件,并不具有入口函数。所以pInitNodeArray中节点比_LdrpClearLoadInProgress返回的数目要少。" H& p6 ^3 y2 W" r
三:第三部分的代码枚举了pInitNodeArray中的对象,并且调用了入口函数。因为这部分的初始化代码有可能出现错误,所以使用了_try异常扑获功能。这就是为什么在DllMain中出现错误后不会使整个进程终止。
' n' g$ [9 b7 W) p3 s/ N8 I* G另外,在调用入口函数时还会对TLS进行初始化,当用 __declspec来声明TLS变量时,链接器包含的数据可以进行触发。在调用dll的入口函数时,LdrpRunInitializeRoutines函数会检查是否需要初始化一个TLS,如果需要,就调用_LdrpCallTlsInitializers。1 i# U9 |% y- r; Z( ^/ b1 a
在最后的伪代码部分使用汇编语言来进行dll的入口函数调用。主要的命令时CALL EDI;EDI中就是入口函数的指针。当此命令返回后,dll的初始化工作就完成了。对于C++写的dll,DllMain已经执行完成了它的DLL_PROCESS_ATTACH代码。注意一下入口函数的第三个参数pvReserved,当exe或dll隐式调用dll时这个参数是非零,当使用LoadLibrary调用时是零。在入口函数调用以后,加载器会检查调用入口函数前和后的ESP的值,如果不同,dll的初始化函数就会报错。检查完ESP后,还会检查入口函数的返回值,如果是零,说明初始化的时候出现了什么问题。并且系统会报错并停止调用dll。在第三部分的最后,在初始化完成后,如果exe进程已经拥有了TLS,并且隐式调用的dll已经被初始化,那么会调用_LdrpCallTlsInitializers。( F) o8 D' ]; ?( F) Z  J) |
四:第四部分代码是清理代码,象_RtlAllocateHeap 分配的pInitNodeArray的内存需要被释放。释放代码出现在_finally块中,调用了_RtlFreeHeap 。
您需要登录后才可以回帖 登录 | 加入计匠网

本版积分规则

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

GMT+8, 2026-6-4 13:30 , Processed in 0.277704 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

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