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

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

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url], t+ e6 h! u4 D0 x* r. m2 e8 V

3 v% ?: A" F% xWINDOWS 2K Dll 加载过程
( g" T" V* B& N! J! hjefong by 2005/03/30
4 j/ R+ `4 Q; e7 P, b5 ]7 S这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。  Y( Z% g" @1 e$ M1 ^* Z7 N
在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”。
$ i5 l7 d9 U7 H6 H! e( q2 |你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。7 }, u; [2 I8 R/ `
初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。- k4 \* C1 b# h6 \$ J
  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。
- |3 a5 X* g- j* O2 \  N% G  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。
, n% I7 U9 R1 p; q+ I) R0 m  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):+ t$ c( C. N* H+ O2 l
//=============================================================================
. \5 D/ V9 i2 \0 V0 p// Matt Pietrek, September 1999 Microsoft Systems Journal
- a0 q5 U* o+ z( e/ X$ h& o// 中文注释部分为jefong翻译5 W8 d* i( W+ ?3 Q
//
0 M3 V/ l: p/ P! O" I# j$ |& C// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3)
0 u$ Z0 P: b: Q+ A4 u//
6 ~/ _. E; c' }# z# Y" r! b// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;5 m9 v' L4 C# h* c
//=============================================================================4 ]! q/ M2 G% d$ X
( w4 M0 l+ J4 }% l
#include <ntexapi.h>    // For HardError defines near the end
8 `2 `- W* {- ~" U4 h5 s
3 ~4 t* y; K' F$ L/ Z// Global symbols (name is accurate, and comes from NTDLL.DBG)
" @0 _/ e" J" S//  _NtdllBaseTag+ Y7 i" r# k: l2 Z5 T% w
//  _ShowSnaps
& I  ~5 _6 M, J  {//  _SaveSp
+ V' F" i- W9 Q+ j, h( u//  _CurSp
  m9 Y% z+ Q3 `' N; U( y//  _LdrpInLdrInit1 h* \% q; S- r& t1 [/ ]. n
//  _LdrpFatalHardErrorCount
: w/ J- K- c1 R: F% p: p  k7 ~  t//  _LdrpImageHasTls
; z1 @# ~& |2 R2 v! I, Z% g3 r$ y' Z# b+ U# t$ H' H2 A
NTSTATUS
$ |4 S  l. L' W& K  W% GLdrpRunInitializeRoutines( DWORD bImplicitLoad )8 }" a! w3 z2 j% v/ I; Z( y/ K
{- R; i) w. q/ j) l/ v
    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了
4 o; |" ^' F1 ^; P2 m    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();
1 N  X8 r& e9 ~" G8 `$ z3 m) A1 L
3 }2 v7 O- W2 O    if ( nRoutinesToRun )4 f7 n$ v6 x! O+ U$ g8 s
    {
0 B: g9 w' i& ?$ E        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。! f; n# ^" E, @) n$ D2 S
        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),
+ \" F" ~! N" i8 g                                            _NtdllBaseTag + 0x60000,3 F: H: D$ Z! L9 h* y0 O4 m
                                            nRoutinesToRun * 4 );% R! q* D* M' \. H# y- d$ p
                            # Z* j' I; P# F  Z+ H8 j1 k
        if ( 0 == pInitNodeArray )    // Make sure allocation worked
/ f: \, u- {$ q) w0 Y  V            return STATUS_NO_MEMORY;4 n+ e+ O- `2 {" m
    }  S5 t# H0 [, s
    else
1 z  `( A  K& `' \9 r3 D        pInitNodeArray = 0;
4 u- d, y. @6 f6 n$ W
1 d) Z  N8 ^" n* C5 h, T3 o    //第二部分;" Y* C7 S0 ?; x6 S
    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。
3 G5 s+ H$ y" A+ R8 r    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);
# @; ^) J* }  F8 j    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;
% M0 S8 F) T# m; w5 f3 g. f! z        
. i; p' v" H" F    if ( _ShowSnaps )
- n' s6 P  v2 T    {5 F7 G6 I4 C: x3 p9 H* X
        _DbgPrint( "LDR: Real INIT LIST\n" );
: a% o9 z0 T5 \, s& j0 N" A    }7 p- P5 o* D2 O% C6 T, d, U- V
) W8 o# s; f6 t, g& Z! X3 }
    nModulesInitedSoFar = 0;
' d) ]; t1 Q$ w
- B3 W* Q* X: W: e1 a7 h* v    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块
% T* `: ?/ \7 m% |    {
' q/ ]6 t5 l  n3 t1 O2 R1 T* H        
- O& B; v4 n( o+ y* [        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块+ r  ]. h$ d- C7 |4 P, ^  G
        {# B! M; i' z8 }9 p" @
            ModuleLoaderInfo  pModuleLoaderInfo;8 s; o8 u/ h7 k
            
- ~! X4 _2 j" A2 ?            //7 M" X8 ]4 q' n: t
            //一个ModuleLoaderInfo结构节点的大小为0X10字节( @; D% c, q, ]1 P
            pModuleLoaderInfo = &NextNode - 0x10;
- x1 Y, O. |9 c! g            
0 n( v# m" H# W. A$ F            localVar3C = pModuleLoaderInfo;         
: Y' t8 @- ]+ A, S5 F7 T: A8 w
6 A8 J& V1 M0 r& q+ _            //
# ^1 t4 u/ ?3 w, m/ b' m/ M) X            // 如果模块已经被初始化,就忽略( Y# N3 U( O% x, i% d# \
            // X_LOADER_SAW_MODULE = 0x40 已被初始化7 y, }8 ^# ?% q7 {
            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )
: ?& x$ b% [0 Z% ]5 h4 y            {: U# N0 z( Z  i( t% v
                //4 |! l9 N  O+ \/ t, E) s/ D+ I: P
                // 模块没有被初始化,判断是否具有入口函数
5 k, ^! k0 R6 K2 g                //+ x' L7 J! A# m( x/ C
                if ( pModuleLoaderInfo->EntryPoint )+ C5 \0 Z; y$ t2 L# ^! V6 a
                {) L# o( t! U2 ]
                    //1 }& b4 [" V3 x! H! _  r. j) P7 V
                    // 具有初始化函数,添加到模块列表中,等待进行初始化" T9 z, S3 A9 R4 Q3 u; ~
                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;0 I+ T$ l( P4 u/ C6 a, o4 C1 h

9 @: X+ G8 k  X$ {$ v0 q                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址
) N3 G/ ~: Y) k& h% N, V& V      // 例如:
% v; i+ f3 b; Q                    // C:\WINNT\system32\KERNEL32.dll init routine 77f01000
3 `' f( H1 N# B3 i5 @                    if ( _ShowSnaps )8 }' l6 ~% T* p9 i) p
                    {- S& K' V% n3 |
                        _DbgPrint(  "%wZ init routine %x\n",
. u$ B# }4 p% ?3 a4 [7 |$ e/ \9 q                                    &pModuleLoaderInfo->24,
! {& L; n% N& l8 C! G                                    pModuleLoaderInfo->EntryPoint );
9 b6 n3 U# _1 y. |* q+ `: T) p                    }
, W' B2 D4 |9 U5 I& `- ^* p. |5 u1 b- p6 x
                    nModulesInitedSoFar++;( e# z% w, G) i
                }( K0 x; L; J+ e5 F7 U+ f
            }- f8 x) h8 z/ w" x$ u

, O/ Y4 b& R7 `. |9 D            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。
  g" Q  r+ t2 A            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;
( B/ R. J6 z" h1 v. a" k, n6 H% D
; o/ C' t/ p5 r% N7 g8 u            // 处理下一个模块节点2 b1 X- u% W' Z. E5 c5 M7 e
            pCurrNode = pCurrNode->pNext
" @, c! \9 y6 L6 C0 }        }6 b; n2 y/ J' S- M3 D
    }2 o4 a# f- z; s# t. O5 @
    else
0 K% M+ B$ C" Z9 |/ D: [    {  c0 P" r1 U3 {* ]( X8 B% U# e4 h: |
        pModuleLoaderInfo = localVar3C;     // May not be initialized???5 K; W  M+ |! J! H# ?; b
    }* x4 J5 s/ A7 F4 m& @' K- x
    & a' h9 ~) P4 r1 h$ f. o
    if ( 0 == pInitNodeArray )
+ a7 N) z$ v2 h& |  h        return STATUS_SUCCESS;
/ J8 o. v0 A* `0 a% ?& ?% G0 i/ w' N+ k- J: r5 G
    // ************************* MSJ Layout! *****************; G5 f4 e$ K+ a8 H# Q
    // If you're going to split this code across pages, this is a great" ~, m( c0 G  h4 k# ~
    // spot to split the code.  Just be sure to remove this comment
; x$ c5 E6 \- n8 }( Y) b0 `    // ************************* MSJ Layout! *****************" T0 {  c' t( J  m5 U  K! u7 d
   
- [2 l4 m6 G: [% R% n% r/ r    //
  Q( g4 t1 n! Q  ?' ?    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH
% e1 z; T0 g2 |    // 第三部分,调用初始化部分2 \% V! ~! \. Z6 W" X, A
    try     // Wrap all this in a try block, in case the init routine faults. ~/ n# X8 f/ u( N
    {
' L8 {+ L- U* B% G( N" J        nModulesInitedSoFar = 0;  // Start at array element 06 c8 s: J5 i0 K- b+ a
- s+ a2 M- b6 B4 Y7 r1 E; ?3 |
        //
# v( f6 x* D  w/ v) W! r        // 遍历模块队列
& }  y4 L1 [+ g3 Z' H$ }) R        //9 c* G/ f/ k7 @3 [+ V# _
        while ( nModulesInitedSoFar < nRoutinesToRun )  Q  y7 C& B7 g+ `- [7 @2 r! A
        {& D9 u+ I- S& V( u
            // 获得模块指针9 R0 f/ {8 S. d1 S# o
            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];
' k) F- r) \3 N6 N$ Q
; w* m  M9 O3 Z6 c  d# {            // This doesn't seem to do anything..." t1 y1 s4 \9 @! ?
            localVar3C = pModuleLoaderInfo;
0 {6 n4 m! \# {2 @            % ^! [$ |2 L8 S2 B0 N
            nModulesInitedSoFar++;
: P4 e7 ^  w1 V1 n! H7 N" K7 p               
4 Q; Y1 K+ {* H6 M& r) @9 w            // 保存初始化程序入口指针4 c; Z+ x1 m/ d' W
            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;
+ U! L+ j) l5 H' }! N; A            
) \! {; {+ {! E) u7 l4 R# [5 W            fBreakOnDllLoad = 0;    // Default is to not break on load
. `2 S0 e2 Q4 w% Y" y0 Z( @, }- D, Z9 r/ n: `# N- i: \0 \5 P/ h5 [
            // 调试用
  E/ {: s! ]; L, H& _0 A6 G! P            // If this process is a debuggee, check to see if the loader/ p! C. L5 b( D/ z% K" e' ]
            // should break into a debugger before calling the initialization.
5 o0 j2 b" w  n" z            //2 R2 ~5 k) t( l4 D
            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()& N2 n3 |) T+ @4 O3 Q+ r# ~
            // returns. IsDebuggerPresent is an NT only API.
. K8 O3 z) f. X# q            //4 C( I# T6 t# A& k/ |
            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 ): z( ^' R7 K: ]2 S/ m0 p
            {! M& h% q& N7 p, @. k8 l
                LONG retCode;
; d. Y- F8 _. C3 }1 k# `% K
; u8 }' z* t/ o0 J( ~  j                //              
! [3 o0 X0 Q; K( T4 p6 Z$ X                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\9 ~; _. g9 u% w
                // Windows NT\CurrentVersion\Image File Execution Options"$ R9 S4 |3 W, F0 K( I4 y# s" f
                // registry key.  If a a subkey entry with the name of
" K  l; `7 d0 S3 b& A% }  C" O                // the executable exists, check for the BreakOnDllLoad value.
" ~( U0 v. B  J, j' G; C: D                //
, P- b: |4 X0 m) Q  g0 b                retCode =
' W6 O( Q1 |5 b" Z                    _LdrQueryImageFileExecutionOptions(
. f3 F% e" Z! R5 M7 N' L                                pModuleLoaderInfo->pwszDllName,
) u- L& n% [9 K& Y0 t, u" B                                "BreakOnDllLoad",pInitNodeArray6 O7 Z; W8 E7 G
                                REG_DWORD,9 i6 M6 z  a  H1 J; ]  h  h
                                &fBreakOnDllLoad,
. j8 q0 J" S4 P3 J: |- n. p) B+ E                                sizeof(DWORD),0 U& _% L! T6 E1 y
                                0 );6 C3 n' {- V; B$ @# }
) d4 U+ E' {$ _) K% B
                // If reg value not found (usually the case), then don't
4 Y) S- K1 X; U1 y' ~% S2 h9 s                // break on this DLL init! V/ }3 k) ]( M: x- d  [/ h
                if ( retCode <= STATUS_SUCCESS )* j: k! e1 _) s. m6 D
                    fBreakOnDllLoad = 0;pInitNodeArray
$ a$ F) j# O4 w8 @+ H- o            }" \' j* l/ V1 x! {0 e, z8 {9 V2 }
            - \0 r! D' r6 I. x# _( q
            if ( fBreakOnDllLoad )
; }7 U, [& l, l! r0 y8 t            {           
( V6 z  K: V1 Y. N+ X                if ( _ShowSnaps )8 p3 U+ J8 {: ^" f" A
                {
' T- O6 ]  W5 p$ e& q5 {8 m                    // Inform the debug output stream of the module name
' ^/ F( e3 [! ?) h  {                    // and the init routine address before actually breaking
4 N' e) m/ k( t1 W7 f# p                    // into the debugger
5 W; K! S0 \/ @. X" P! b: o6 I5 a3 _
                    _DbgPrint(  "LDR: %wZ loaded.",
5 ?$ o: I9 h- s" V1 v+ ?$ ?2 h                                &pModuleLoaderInfo->pModuleLoaderInfo );$ r5 K  z0 k% c' }5 o4 R1 k4 W" n
                    
; G& {" z, I$ s                    _DbgPrint(  "- About to call init routine at %lx\n",
9 ^* I) B$ o' T3 b0 P                                pfnInitRoutine ): x" ?; O$ t3 r3 i( q( Q, z3 F& N
                }3 d! X; G" P! a# D  p; H! x
                9 U% T8 Y! \, @  W
                // Break into the debugger                              
* B) W# @5 k* g% w! Z7 J5 s                _DbgBreakPoint();   // An INT 3, followed by a RET5 Y; h0 {$ k0 _# }' [
            }
8 ~& q) X) ~0 \1 I2 Y5 X6 C, L            else if ( _ShowSnaps && pfnInitRoutine )9 @- B% _+ o& A( x" M
            {
& a; I4 l/ S( s( p* F. t                // Inform the debug output stream of the module name
& S' i; X8 @0 J4 z% K2 }                // and the init routine address before calling it               8 K" ~7 [; g( M+ `
                _DbgPrint(  "LDR: %wZ loaded.",; u9 ~5 u4 u1 ]/ L! W+ y- x
                            pModuleLoaderInfo->pModuleLoaderInfo );8 z2 D' Z! b9 U, ?" t5 a# {1 L

( v( N6 P9 G! g/ n; A' e3 G4 Z* x                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);
- c5 I& U/ M+ x. _8 u            }
6 H$ T' S! ~7 G                    
7 o/ z$ }# R# x" j9 d7 L            if ( pfnInitRoutine )
. m1 y1 Q) h" ]6 w/ t            {6 A. b; x1 P7 ~
                // 设置DLL_PROCESS_ATTACH标志
; u* U4 E# c/ }* I- v$ E* w                //! U; W. s" f  K! B
                // (Shouldn't this come *after* the actual call?)
$ [; a! x" K; ~3 x( h0 `2 m4 ?                //) `! B; ~8 e" I" N
                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8             2 T2 ~7 R) K2 x
                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;
; i* H0 }" F) F8 D# o* z4 W: D; X+ @) d2 P
                //
2 z: D1 z1 |0 k& J/ y                // If there's Thread Local Storage (TLS) for this module,
1 `) {9 p" ?  |' W8 R                // call the TLS init functions.  *** NOTE *** This only4 r0 Z/ j% a" H) k
                // occurs during the first time this code is called (when
3 e* a/ a4 o0 r' A: q                // implicitly loaded DLLs are initialized).  Dynamically" F0 ~0 t7 c1 P
                // loaded DLLs shouldn't use TLS declared vars, as per the) S/ ?7 R+ I: d1 f4 D
                // SDK documentation
& Q6 _% l% ~" ?- _  M- h7 Z4 I  t                // 如果模块需要分配TLS,调用TLS初始化函数( @; h5 ~* R2 X. e/ P8 q
  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时
' T$ Z) d$ m0 C! ~  n/ f  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量
& u, K- p# N7 P                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad ), E% ~  V& f; G) P
                {
. i8 F# k; `* P3 H                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,* W5 q0 ~8 T: ?4 T; |* Q
                                                DLL_PROCESS_ATTACH );( ^3 {$ Q9 Y$ V# O! ?3 I9 K
                }
: @7 o5 M' d0 |2 E& _                6 ^5 `1 t8 x$ `' Q+ ~
( A1 x* k) ?8 F, u8 \8 e8 L
                hModDLL = pModuleLoaderInfo->hModDLL
* k. K; D) J7 y7 k; E1 U6 ?4 H7 Y; D6 h6 F
                MOV     ESI,ESP // Save off the ESP register into ESI0 V' q! u1 a# l7 U# K0 Q1 S
  ) O5 ]* G; g+ y2 r' c" t3 |
  // 设置入口函数指针               
2 Y; d& N7 [% F0 i5 k0 }5 U# s                MOV     EDI,DWORD PTR [pfnInitRoutine]                     
5 I6 I0 H: F  i" q. H( Q& |! L6 O4 w# ?, s5 H
                // In C++ code, the following ASM would look like:" ^' U* t3 j8 y1 v1 [, m. [0 Z
                //
$ i* {( y1 l- e- \+ Q9 X2 w# r                // initRetValue =/ g8 Q+ w: g0 s9 h+ W; I) Q
                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);. V2 E' b7 C+ i" Q
                //% W; O9 [  ]  W; o0 x4 ~9 G
" H2 M3 H( ]0 ~6 R4 f" F
                PUSH    DWORD PTR [bImplicitLoad]. {) T0 p1 B5 x0 ~. ?
                * M/ q' Q" ]: J* m
                PUSH    DLL_PROCESS_ATTACH' j2 O0 x# O) E# W1 @, ?  ^
               
0 A' i% w+ F4 T, q2 x3 A                PUSH    DWORD PTR [hModDLL]
5 c' P3 T, _. J                & {' x) S) v" M2 ?/ L, Z! D1 ?
                CALL    EDI     // 调用入口函数& s) X; I) X3 j
               
2 j( l9 Y% X! r% o8 h( M                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值
8 f0 Z5 P( _$ b" K# q
) ^" ]0 }$ i' B                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the$ E, d9 P* b; X: w9 |) R& q  X0 f
                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns$ u5 x9 z, p' E% _7 c
( |( I9 m9 o- t' e
                MOV     ESP,ESI     // Restore ESP to value before the call
$ Y  Z3 E2 }+ |1 X
7 w( |! k( m1 R' `/ T; x- t                //
" b6 U7 X9 S( q* m  `$ t                // 检查调用前后的ESP值是否一至$ y6 f/ d# J; q" v$ b
  // 1 B4 E' M; ~1 ^% O# w
                if ( _CurSP != _SavSP ): B: R: Z1 |3 R$ `- A
                {9 U4 h- S. n3 f. u- j
                    hardErrorParam = pModuleLoaderInfo->FullDllPath;
6 O, P: P2 m& d) t, T2 F, q
- \' o7 L: |2 e                    hardErrorRetCode =
6 ~  [4 U2 c4 D7 N) u+ P; }7 K                        _NtRaiseHardError($ w" ]/ [, C" O& b  |
                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,
3 i; Q0 Z. S" b+ [% E% b                            1,  // Number of parameters9 R7 o7 |" n" d3 }: n3 o9 ?& P7 I
                            1,  // UnicodeStringParametersMask,
8 |& e, ]! u8 d7 w; w; k                            &hardErrorParam,3 u% d3 y9 A( g3 x2 s7 P9 {
                            OptionYesNo,    // Let user decide; A0 Q/ n  I1 P  @7 @9 d0 U
                            &hardErrorResponse );
/ K/ s9 Y9 A4 ?" X* [                                            7 Z! ~: y# U, O3 D* s
                    if ( _LdrpInLdrInit )
( B, Q! n% a+ g/ I) x                        _LdrpFatalHardErrorCount++;
- g' k) ~/ c( d1 d0 `5 g# z% ]2 A4 P% s+ O
                    if (    (hardErrorRetCode >= STATUS_SUCCESS)
, _5 p+ D8 S5 H. X                        &&  (ResponseYes == hardErrorResponse) )) S7 Z/ ?% \1 p+ P: K* {3 P. h
                    {
1 x9 l. J; p$ I                        return STATUS_DLL_INIT_FAILED;
" V* H. R* r! C* r$ \                    }- o1 M9 T" h* }0 x4 f- X4 g
                }
7 R7 L8 ~3 o/ A9 w/ f4 u' m! @9 n5 W) A$ z) u" W
                //
. C5 f8 H7 f. I3 h5 }8 L8 f3 E                // 入口函数返回0,错误
. G4 }0 w3 T* A                //; L; W( ~* y: h) y  Y. c3 `
                if ( 0 == initRetValue )! d" w  |0 g7 I
                {
: [( \5 a/ z7 N                    DWORD hardErrorParam2;; V) V: n" }0 p8 t/ w
                    DWORD hardErrorResponse2;
) [' ]: T) s. M& r                                       
* _# P+ u, U8 S! B+ G! D& ]7 M                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;+ M4 k. ?, p0 z
                    
" L# V1 W' j; R2 f. t                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED," l% I; s1 G. o6 Q, N: ~2 I/ L$ ]
                                        1,  // Number of parameters
7 \! g2 Z; a) E* Z6 Y3 f, c1 Q                                        1,  // UnicodeStringParametersMask
; _) i8 N8 `! w, v! c' P                                        &hardErrorParam2,3 d  u% k- N4 W! D9 Z# c
                                        OptionOk,   // OK is only response
3 L0 b# f7 f- U9 @; y" e( @                                        &hardErrorResponse2 );
! ~: z+ M/ `! J9 L# |0 S                                                            
' Q) Q4 }$ a- h. H4 d                    if ( _LdrpInLdrInit )
+ h6 i: Y: M, l6 u% q3 \                        _LdrpFatalHardErrorCount++;" V0 Z0 `" Q5 @0 `% b& R& Z
( y/ X! ~3 \: y4 B& ]
                    return STATUS_DLL_INIT_FAILED;
* `* `" K/ H4 n1 c% ?/ C7 _  B                }
% O( }3 g+ D2 f* n+ J            }
# h& l; h7 W! P        }0 o# M% k4 u  L0 W. @% J) c
$ H) h& f; S0 a$ T
        //9 W# B. K* ?# [& h; j* \1 k
        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时$ e3 q, M% I$ B, Q& R7 g
        //      2 j. J5 o, m5 c6 f* T. @  e- b4 Q
        if ( _LdrpImageHasTls && bImplicitLoad )" p4 U+ v; h* E% h) d1 ?
        {
: S0 D. o! X4 S6 r+ C/ T            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,$ l2 a$ v4 o7 W  A" k
                                        DLL_PROCESS_ATTACH );& D/ b- o0 _8 E! R
        }
; K, s( `& \+ ^    }5 @2 u  {& }9 ~4 s) P, u, b  L/ J1 ~
    __finally
. A3 z. L, ]# K6 W, w    {7 K  Z( w' T! u: ~) I
        //
3 x* \/ M3 w, r4 N1 z- f( Y        // 第四部分;
  j0 u# c# R2 _( j        // 清除分配的内存
5 f7 p+ Q" w; n; W2 v' _7 E6 L# C# |        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );
. ~( t% [2 `* E# @  A. g/ Y9 M    }. \% T" x* P" s: c. n5 Q$ _* m8 F
4 t2 c$ ^+ j2 P2 |- I
    return STATUS_SUCCESS;6 o2 i2 U7 S7 c, }4 h; L) T3 B! K
}   
" R5 r, R) K1 q& S4 K) `, j
# F: v. {2 j) }5 ~& x! d这个函数分为四个主要部分:) o, x) O7 ^, O3 R
一:第一部分调用_LdrpClearLoadInProgress函数,这个NTDLL函数返回已经被映象到内存的dll的个数。例如,你的进程调用exm.dll,而exm.dll又调用exm1.dll和exm2.dll,那么_LdrpClearLoadInProgress会返回3。得到dll个数后,调用_RtlAllocateHeap,它会返回一个内存的队列指针。伪码中的队列指针为pInitNodeArray。队列中的每个节点指针都指向一个新加载的dll的结构信息。) }  _2 R+ b( n: C7 o0 o- H
二:第二部分的代码通过进程内部的数据结构获得一个新加载dll的链接列表。并且检查dll是否有入口指针,如果有,就把模块信息指针加入pInitNodeArray中。伪码中的模块信息指针为pModuleLoaderInfo。但是有的dll是资源文件,并不具有入口函数。所以pInitNodeArray中节点比_LdrpClearLoadInProgress返回的数目要少。7 l, P& F; |- g& t, c
三:第三部分的代码枚举了pInitNodeArray中的对象,并且调用了入口函数。因为这部分的初始化代码有可能出现错误,所以使用了_try异常扑获功能。这就是为什么在DllMain中出现错误后不会使整个进程终止。
, R5 c9 y8 _4 p+ I另外,在调用入口函数时还会对TLS进行初始化,当用 __declspec来声明TLS变量时,链接器包含的数据可以进行触发。在调用dll的入口函数时,LdrpRunInitializeRoutines函数会检查是否需要初始化一个TLS,如果需要,就调用_LdrpCallTlsInitializers。# V/ r" e. ?/ `  {$ u& x9 R: w
在最后的伪代码部分使用汇编语言来进行dll的入口函数调用。主要的命令时CALL EDI;EDI中就是入口函数的指针。当此命令返回后,dll的初始化工作就完成了。对于C++写的dll,DllMain已经执行完成了它的DLL_PROCESS_ATTACH代码。注意一下入口函数的第三个参数pvReserved,当exe或dll隐式调用dll时这个参数是非零,当使用LoadLibrary调用时是零。在入口函数调用以后,加载器会检查调用入口函数前和后的ESP的值,如果不同,dll的初始化函数就会报错。检查完ESP后,还会检查入口函数的返回值,如果是零,说明初始化的时候出现了什么问题。并且系统会报错并停止调用dll。在第三部分的最后,在初始化完成后,如果exe进程已经拥有了TLS,并且隐式调用的dll已经被初始化,那么会调用_LdrpCallTlsInitializers。1 \& b. k" B9 E! G1 n
四:第四部分代码是清理代码,象_RtlAllocateHeap 分配的pInitNodeArray的内存需要被释放。释放代码出现在_finally块中,调用了_RtlFreeHeap 。
您需要登录后才可以回帖 登录 | 加入计匠网

本版积分规则

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

GMT+8, 2024-11-15 15:15 , Processed in 0.040762 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

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