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

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

[复制链接]
发表于 2007-11-16 12:16:34 | 显示全部楼层 |阅读模式
来自:[url]http://www.whitecell.org/forums/viewthread.php?tid=34[/url]; q+ |# d2 k3 d7 {9 w
& \  w6 x' f8 ^/ t
WINDOWS 2K Dll 加载过程
: E, B. g% y$ M  u  r8 Tjefong by 2005/03/30
" k2 L6 ^0 m& B$ U% `3 p0 T3 a这片文章是我在阅读完MSJ September 1999 Under the Hood后的总结。) H0 |3 \4 T5 X* H" E0 E
在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”。
9 S7 c* y' c* K# a: |- O' U你的函数正在执行一个初始化任务,例如设置TLS,创建同步对象或打开一个文件。那么你在函数中一定不要调用LoadLibrary函数,因为dll加载命令会创建一个依赖循环。这点会导致在系统执行dll的初始化代码前就已经调用了dll的函数。例如,你不能在入口函数中调用FreeLibrary函数,因为这样会使系统在已经结束了dll后还调用dll中的操作,引起严重错误。/ |+ a% V  K& J( k2 O) o" y
初始化任务时调用Win32函数也会引起错误,例如调用User,Shell和COM函数可能会引起存储无效的错误,因为dll中一些函数会调用LoadLibrary来加载别的系统组件。
3 r& K0 R7 Z( S: R" t; ^  当你在你的DllMain函数中读一个注册表键值,这样做会被限制,因为在正常情况下ADVAPI32.DLL在你执行DllMain代码时还没被初始化,所以你调用的读注册表的函数会失败。
  k- W+ m9 s6 e0 s! c; H; `4 {5 H  在文档中初始化部分使用LoadLibrary函数是严格限制的,但是存在特殊的情况,在WindowsNT中USER32.DLL是忽略上面的限制的。这样一来好像与上面所说的相背了,在USER32.DLL的初始化部分出现了调用LoadLibrary加载dll的部分,但是没有出现问题。这是因为AppInit_Dlls的原因,AppInit_Dlls可以为任一个进程调用一个dll列表。所以,如果你的USER32.dll调用出现问题,那一定是AppInit_Dlls没有工作。9 G( Q% @& _$ K# p
  接下来,我们来看看dll的加载和初始化是怎样完成的。操作系统有一个加载器,加载一个模块通常有两个步骤:1.把exe或dll映象到内存中,这时,加载器会检查模块的导入地址表(IAT),看模块是否依赖于附加的dll。如果dll还没有被加载到进程中,那么加载器就把dll映象到内存。直到所有的未加载的模块都被映象到内存。2.初始化所有的dll。在windows NT中,系统调用exe和dll入口函数的程序会先调用LdrpRunInitializeRoutines函数,也就是说当你调用LoadLibrary时会调用LdrpRunInitializeRoutines,当调用LdrpRunInitializeRoutines时会首先检查已经映射到内存的dll是否已经被初始化。我们来看下面的代码(Matt的LdrpRunInitializeRoutines伪代码):
* P3 j% q- e# Q; o//=============================================================================
% k! q8 F( w0 J. W" y// Matt Pietrek, September 1999 Microsoft Systems Journal7 z1 s' Q2 I- t0 n% W
// 中文注释部分为jefong翻译
9 r- Y. I5 v) t//
, z$ g2 y, O9 @" ]& C// Pseudocode for LdrpRunInitializeRoutines in NTDLL.DLL (NT 4, SP3)
! H$ |- A6 X7 _4 Q/ X6 w' d6 I//
6 X0 `' F2 F: }// 当LdrpRunInitializeRoutines 在一个进程中第一次被调用时(这个进程的隐式链接模块已经被初始化),bImplicitLoad 参数是非零。当使用LoadLibrary调用dll时,bImplicitLoad 参数是零;- z& i5 ~$ Q. n0 |) F
//=============================================================================
9 H8 N! @* z, t4 S
; _2 ~, r! e- M#include <ntexapi.h>    // For HardError defines near the end
9 \# G( w( w3 b9 D3 ]& G5 ~6 L
1 E$ r* ^/ E- q% k% V// Global symbols (name is accurate, and comes from NTDLL.DBG)) r9 b! x: X( X5 c1 V. p) V
//  _NtdllBaseTag
, E  X2 |- Y: P/ }% V# o1 N//  _ShowSnaps9 T. R" w; W4 e7 N" t
//  _SaveSp
9 p- e" i; x1 C: @, ?" a//  _CurSp* P9 G2 e3 `; l# Y9 [% G1 l
//  _LdrpInLdrInit$ a0 A6 k+ i, J# M9 @. W, |
//  _LdrpFatalHardErrorCount# ]8 g' e4 m1 N$ |7 L& O% k
//  _LdrpImageHasTls
6 _8 V; X; i0 W7 l# c7 z
8 D1 B6 }, L8 a4 l4 O1 w) \( eNTSTATUS
1 }+ f8 m2 ^# Y8 {LdrpRunInitializeRoutines( DWORD bImplicitLoad )
5 K5 a* t! |0 H, ?% V* _2 b3 b6 V; H{. Y. G- I. r2 N  V$ _" n0 Q# u5 K
    // 第一部分,得到可能需要初始化的模块的数目。一些模块可能已经被初始化过了0 \7 b4 n6 w# W7 U$ Z2 X1 o% C
    unsigned nRoutinesToRun = _LdrpClearLoadInProgress();9 g8 M6 T& K0 G( z2 v5 L
/ {$ g1 c4 Q) M( ^$ f4 _7 q
    if ( nRoutinesToRun )
% H9 p& g8 J% _; H3 ~( L& `4 q    {' U/ t1 L6 e& J1 b0 n6 b. }
        // 如果有需要初始化的模块,为它们分配一个队列,用来装载各模块信息。
8 s& K2 [- x$ D1 X' Q        pInitNodeArray = _RtlAllocateHeap(GetProcessHeap(),; k; O" b4 p; w! Q+ C
                                            _NtdllBaseTag + 0x60000,
& \* Z' j' [) R, O9 m                                            nRoutinesToRun * 4 );
. D, q& O9 \: w* M1 |! c                            ; Q$ N3 N- h& q2 s4 m# u; O
        if ( 0 == pInitNodeArray )    // Make sure allocation worked
1 L3 ?2 ?( K% Q% h: p. E. v9 P            return STATUS_NO_MEMORY;
6 }! l8 t: j4 \5 q# g$ |. u+ o! _+ X    }
! h* q% o( E. m7 x    else
, t/ {" U1 A- e  f, F$ v9 u        pInitNodeArray = 0;' p4 r7 V6 A; t( g
4 _2 q( N. p. N+ m6 J) X% f* K
    //第二部分;
+ Z3 O% u' ^) N1 W4 i0 t    //进程环境块(Peb),包含一个指向新加载模块的链接列表的指针。
. S5 Q) y$ i- @+ [9 n# s    pCurrNode = *(pCurrentPeb->ModuleLoaderInfoHead);
0 `. G$ v8 M7 t% s0 V2 A# u    ModuleLoaderInfoHead = pCurrentPeb->ModuleLoaderInfoHead;
6 I" T1 ?9 ~* h0 P! E; B4 {        
& k5 A8 W+ x2 W    if ( _ShowSnaps )
, _: [9 n$ P  L" L    {  B. K0 L4 f: K0 u6 m; {8 X& w
        _DbgPrint( "LDR: Real INIT LIST\n" );
9 I+ ]6 w( U& ], A5 E9 V1 {- A    }
6 g! ~2 p- g# j' d: {/ H4 K3 N5 ]: u9 H  r- R, a& r
    nModulesInitedSoFar = 0;
) ]  E6 f  e% _1 m% i- g* |. e( e  Q  B, L, ^2 `5 t3 F' ^) Y
    if ( pCurrNode != ModuleLoaderInfoHead ) //判断是否有新加载的模块# o" p% L2 [6 k: p1 s1 E" {% [
    {; u5 A) F2 r/ L, G; u. g
        
7 K9 l8 U$ r$ V7 i        while ( pCurrNode != ModuleLoaderInfoHead ) //遍历所有新加载的模块$ c4 t- I! M1 B5 t! `7 S7 g" t5 r
        {! H9 f& N% T& i* v+ V$ P7 s
            ModuleLoaderInfo  pModuleLoaderInfo;
. L  o6 j" g" j. H( b+ V8 C            
7 ?5 M$ k1 C: G( Y$ W- y            //
- u- ^+ [. `. ]! V            //一个ModuleLoaderInfo结构节点的大小为0X10字节
. x' U* i' [9 v            pModuleLoaderInfo = &NextNode - 0x10;& _! G5 {* y: x+ R% ?6 n
            & k1 ^2 }9 {% E$ Q- S
            localVar3C = pModuleLoaderInfo;         
+ ?) C8 n2 c' {  a9 n, N0 M% v4 P% \8 k! d! g! e9 e, R0 }: c" [, ?: n
            //
: b# L+ J2 {4 @            // 如果模块已经被初始化,就忽略- J9 N4 T) y3 O7 _& c
            // X_LOADER_SAW_MODULE = 0x40 已被初始化
5 l6 O, ~% j+ g0 @% r            if ( !(pModuleLoaderInfo->Flags35 & X_LOADER_SAW_MODULE) )6 e: E* c) E3 e
            {
, [! F8 m3 ]( w- V  C0 k                //1 W9 E: n8 E  c
                // 模块没有被初始化,判断是否具有入口函数0 B- P! f9 H1 P' }
                //
" \7 ^& a; X/ ]5 c0 n# T; i                if ( pModuleLoaderInfo->EntryPoint )
3 J/ @) R! _- h# N% N! p, P" M; Q                {8 B4 [8 ]- `" F( u8 B  i# U: a
                    //
) r- X7 R% K; }9 O7 l: F8 u2 G) x& r                    // 具有初始化函数,添加到模块列表中,等待进行初始化/ a  w) U4 x1 o/ d
                    pInitNodeArray[nModulesInitedSoFar] =pModuleLoaderInfo;
" p) Z- t/ q( Z7 Q5 o0 g$ Q
& Z( D  A4 k  K- J0 |4 I                    // 如果ShowSnaps为非零,那么打印出模块的路径和入口函数的地址
; f" b% F8 p/ a+ R      // 例如:8 X. o- \' e4 f2 N9 l/ K0 I3 Y( v
                    // C:\WINNT\system32\KERNEL32.dll init routine 77f01000! {; J7 R5 p! e& m/ x- J
                    if ( _ShowSnaps )
: Z& j$ v' O4 Q5 s                    {  [3 S! C% y' v' _
                        _DbgPrint(  "%wZ init routine %x\n",7 y* g* R/ U5 c+ ]. a1 O3 S6 Y
                                    &pModuleLoaderInfo->24,
+ M) R  @( p3 B2 z& h  B: B                                    pModuleLoaderInfo->EntryPoint );6 E' U! B- r: \% Q' X
                    }* W! h3 y- A$ b6 u+ |

. M+ p0 k- `* Q- \                    nModulesInitedSoFar++;
2 w3 z3 a( q3 Y- m8 o                }
0 W% S6 h& f4 P1 y            }
# _8 T2 R7 t) I8 s2 A+ J% j# M. V* q8 S9 L
            // 设置模块的X_LOADER_SAW_MODULE标志。说明这个模块还没有被初始化。: \0 ]  g5 L# @/ f2 e
            pModuleLoaderInfo->Flags35 &= X_LOADER_SAW_MODULE;. g. k! h% B( j6 x: M! c) d
. `! Q7 [! p! y6 C8 h1 k
            // 处理下一个模块节点$ M9 K2 A& e8 g- _8 Q
            pCurrNode = pCurrNode->pNext
: y; Q+ G0 X' q6 i* }- _/ F6 r        }7 w8 d2 C- i% x: w$ k3 j
    }" i: b/ U, v2 b  C* q* s( o
    else
2 r: |+ r) m* M, H- R: B& H    {
9 a4 d& r" j0 w* d. g0 J% e        pModuleLoaderInfo = localVar3C;     // May not be initialized???1 Z8 _: h- y4 t' K- g& V
    }
0 W5 Z' s+ M5 v" g( l2 `% }    3 F) S+ \& X: r6 r/ O  C
    if ( 0 == pInitNodeArray ): ], Z, G: K0 M2 \/ r% F
        return STATUS_SUCCESS;
6 b; D! Q7 q! x" R0 L! P
8 E0 ~' ]( C2 x$ g9 D3 {    // ************************* MSJ Layout! *****************. I6 p# T- r8 R" F7 G% T9 ]
    // If you're going to split this code across pages, this is a great
2 L1 z/ z/ H9 ^4 q( n% v    // spot to split the code.  Just be sure to remove this comment
. g6 I( z" u- B    // ************************* MSJ Layout! *****************/ q( v& d5 ~% h4 L0 C
    3 h+ E4 J5 ?: B. e, i! r( m& S
    //. B! V% _6 Z2 w/ J
    // pInitNodeArray指针包含一个模块指针队列,这些模块还没有 DLL_PROCESS_ATTACH* }2 a2 A' I+ P  e$ K- G  D  F
    // 第三部分,调用初始化部分% ?& E2 \7 |# w
    try     // Wrap all this in a try block, in case the init routine faults! S6 a. G! M, [
    {8 S9 o0 T: N2 \, k7 T
        nModulesInitedSoFar = 0;  // Start at array element 0
& L  i( c, @6 G- r5 |6 K/ K  E  H3 ~7 f) u' y6 G1 L- p
        //
# t( t, [: R7 F& F; y        // 遍历模块队列
/ h; x0 X* k6 U( ^        //7 T0 P* O2 H7 K2 m+ `' G- u
        while ( nModulesInitedSoFar < nRoutinesToRun )
' A) b, Z7 f" K' e" r7 y0 X        {5 W. A+ L& T+ i- z8 C
            // 获得模块指针9 @( ^* c; X" L7 `
            pModuleLoaderInfo = pInitNodeArray[ nModulesInitedSoFar ];, a* [2 ~* ~( R+ k
& l' l9 n# N5 [! {1 s* j
            // This doesn't seem to do anything...+ Y4 p* y+ o* I9 P  N9 E
            localVar3C = pModuleLoaderInfo;# Z: T# M/ _' W- t0 X, X3 O/ [* {
            9 _  t: d  \  @. U  T
            nModulesInitedSoFar++;2 @* |6 d& O' ]5 f+ z$ Y* ~; o
                2 i7 H+ I7 y, R  n+ X' t8 j8 L
            // 保存初始化程序入口指针
" ?; ]  d2 H1 I8 e+ g$ z            pfnInitRoutine = pModuleLoaderInfo->EntryPoint;
8 K- f! h# S8 m, \3 g8 F! r            & Z! B+ {! i; R8 Y8 B: v9 G
            fBreakOnDllLoad = 0;    // Default is to not break on load5 ]) g, @# v2 L6 C/ f
2 J3 p1 J+ x/ X6 a0 n
            // 调试用8 N7 K" Q0 u8 S# q0 i
            // If this process is a debuggee, check to see if the loader
: W, v8 _5 S* l: L/ o4 p9 p0 X8 I            // should break into a debugger before calling the initialization.: E2 P# `0 v/ H' q) q# T
            //
2 }7 R! @+ s4 c2 u4 r            // DebuggerPresent (offset 2 in PEB) is what IsDebuggerPresent()4 c! j- w0 R. E+ o4 [0 s
            // returns. IsDebuggerPresent is an NT only API.- _8 E- K- W  {3 q1 Q+ Q
            //
! ?" c( |4 o7 p" _7 u& n            if ( pCurrentPeb->DebuggerPresent || pCurrentPeb->1 )6 b' o  J' K+ N  i$ [3 v$ }! T
            {% o" b# t. j& H6 b* p" ?
                LONG retCode;
  i' F6 m5 A  R4 k- R: Q
' S. E" A) T% a; R1 `                //              ; y& N6 ~% w1 O
                // Query the "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\
4 W  h# d- z. w% F                // Windows NT\CurrentVersion\Image File Execution Options"+ h$ O" P  c8 L
                // registry key.  If a a subkey entry with the name of+ y5 M" ]) B' T7 M
                // the executable exists, check for the BreakOnDllLoad value.
* ?% }2 ~# R* N                //* U+ h  ?8 ^4 |# }" y5 ?. K( i6 v! N. P
                retCode =
! ^- w# d- c% ~3 @3 m  z5 b5 {  E- i                    _LdrQueryImageFileExecutionOptions(2 h* Z% h/ ?0 S/ ^5 b
                                pModuleLoaderInfo->pwszDllName,
2 N, c  \* r; Z2 V                                "BreakOnDllLoad",pInitNodeArray
: z* [# |' ?5 _. w8 |                                REG_DWORD,1 \2 s- H7 G4 J, l; C
                                &fBreakOnDllLoad,
, Y$ B* {: @( r. B' h2 Q& u                                sizeof(DWORD),; A# j/ m8 x- `9 C8 E& H3 D& c
                                0 );5 {3 a5 o1 f! p# c6 ?$ I

7 t5 j9 ^- m- O) ]                // If reg value not found (usually the case), then don't
5 Z& D  P, \4 U5 j* L                // break on this DLL init
# u, d8 j$ A4 |                if ( retCode <= STATUS_SUCCESS )
  `) }& k) c4 X2 W                    fBreakOnDllLoad = 0;pInitNodeArray
  O& r1 J% S! K1 ]            }
4 k( Q7 Z) G$ g6 k- }8 v0 u; J            9 X& B* L0 p6 s: z" Z
            if ( fBreakOnDllLoad )
5 g0 |6 d3 u4 ~2 k* g            {           
! B# Z& v/ j; L$ m) {+ v) g2 P                if ( _ShowSnaps )
4 M2 N8 q6 O5 _* S                {
: O) b0 L1 M6 Z0 a4 s7 T8 ~# Q! F                    // Inform the debug output stream of the module name; v; q  ^* Z7 b; S# [% ]3 a) l& u
                    // and the init routine address before actually breaking
# p$ {9 P. S7 E' i3 I                    // into the debugger8 h7 g$ f$ P% r. j
+ G) p2 M% @1 Z' C
                    _DbgPrint(  "LDR: %wZ loaded.",
4 o6 }$ I. A! U) i  C! y& i! J                                &pModuleLoaderInfo->pModuleLoaderInfo );) @& u! }. o/ F% m! g" A& K1 ]9 o( g
                    
% f5 p+ N4 }0 W  v                    _DbgPrint(  "- About to call init routine at %lx\n",
4 t- G" N% K3 r/ o                                pfnInitRoutine )- r/ e: A, ^# L( K2 m$ `& G
                }
% P3 R4 k4 o- {# Z3 s( I                - T: ?# E7 |+ p6 V. `
                // Break into the debugger                              * c5 B! Q( p: g) n0 K0 @1 G5 ]/ q8 G) {
                _DbgBreakPoint();   // An INT 3, followed by a RET
9 v+ c$ n1 E- i, t6 N1 [/ E9 \! N            }
7 o* u0 ^: o0 ?2 ^7 t" z% N            else if ( _ShowSnaps && pfnInitRoutine )
% Q  H- X- ]8 y9 t  L5 [            {
: t+ c9 y, G9 O8 J/ @3 X" p                // Inform the debug output stream of the module name
9 I; B" o: g) I                // and the init routine address before calling it               
; c5 @6 c" i; f/ d5 B                _DbgPrint(  "LDR: %wZ loaded.",
+ W$ M1 t% N! L4 D& D8 S                            pModuleLoaderInfo->pModuleLoaderInfo );! H# P/ S, L; [9 O: S5 e  f

9 j% M, R' L' V' B; q( ~                _DbgPrint("- Calling init routine at %lx\n", pfnInitRoutine);
. q. x+ j. R  `6 S, b  {            }  C5 m& l" {1 v( G
                    2 u4 K* W1 l& G  w2 R3 ~
            if ( pfnInitRoutine )
% l2 ]1 b0 U* u; m            {4 M/ ~) P7 H+ W. S7 [1 w) R3 ~
                // 设置DLL_PROCESS_ATTACH标志3 X5 N7 {& Q% _) q' h7 \  K
                //
# V0 H# Q$ P# d1 q: @: ^; }                // (Shouldn't this come *after* the actual call?)/ a1 o( {( f( d( V. |( e
                //
- N* L3 [. b0 U1 J& O                // X_LOADER_CALLED_PROCESS_ATTACH = 0x8             & d# U9 A! x2 U" E6 s
                pModuleLoaderInfo->Flags36 |= X_LOADER_CALLED_PROCESS_ATTACH;
2 i3 u+ j# {' c: f: s' m8 g% \
* P* h0 W; p* ~4 @% `+ u                //% Z+ ]3 H5 F6 {# ~
                // If there's Thread Local Storage (TLS) for this module,& [9 o; X4 L. {$ x, i( R; x( d
                // call the TLS init functions.  *** NOTE *** This only
6 }7 S- V4 J* Z; x0 Q. w                // occurs during the first time this code is called (when
* K# L! z- u% J% D                // implicitly loaded DLLs are initialized).  Dynamically, W3 z  F) X! b+ c( p
                // loaded DLLs shouldn't use TLS declared vars, as per the/ P6 T, d1 O& r0 l( J2 F1 B
                // SDK documentation; \: o1 h& `/ z- `: l& J9 {) d
                // 如果模块需要分配TLS,调用TLS初始化函数0 I- O7 M! v/ H
  // 注意只有在第一次调时(bImplicitLoad!=0)才会分配TLS,就是隐式dll加载时! N& b6 ~1 S) L1 K1 v
  // 当动态加载时(bImplicitLoad==0)就不需要声明TLS变量
% |0 ^! h7 Z! [3 w: U6 `2 H                if ( pModuleLoaderInfo->bHasTLS && bImplicitLoad )
+ z$ |( E& I; y( {0 o                {
: Q- ?) D' O  t4 J/ U, A' L                    _LdrpCallTlsInitializers(   pModuleLoaderInfo->hModDLL,. f; j8 l- Y/ {9 x1 W
                                                DLL_PROCESS_ATTACH );
" Y5 F/ m* Z: e/ ?$ `# e  ~+ F                }
3 M8 B" p5 ~" W5 `+ ]# }                5 O9 {0 q7 X& Q; ]4 q+ T

4 N. I) S2 h! `                hModDLL = pModuleLoaderInfo->hModDLL
& a" l) ^. u7 K. h/ v( T; m8 I: t( z( c8 o) C: H7 h  M
                MOV     ESI,ESP // Save off the ESP register into ESI/ |* ^; P2 `7 o8 V: [0 x2 [
  , L; t' W  d& o1 I6 k) {2 E2 t
  // 设置入口函数指针                + x+ ~' e2 g) H. C
                MOV     EDI,DWORD PTR [pfnInitRoutine]                     4 A: U8 d/ v, `6 C3 \
0 [" a* T5 c" U/ t/ A5 S
                // In C++ code, the following ASM would look like:
0 q2 R% d2 ~& E8 w/ B8 Z, R                //% y# K& a( F' a
                // initRetValue =
4 [( s; j9 L3 G                // pfnInitRoutine(hInstDLL,DLL_PROCESS_ATTACH,bImplicitLoad);
$ h' t) b  A! k  Z7 F  S: r                //6 ^9 p( n0 l6 {+ j$ \7 g: v
, y, E: _& T; v
                PUSH    DWORD PTR [bImplicitLoad]! x, k8 U5 z( C; o8 c+ B- u" V( ~
                . I# C; c( B3 p* Y% d! |0 [1 c
                PUSH    DLL_PROCESS_ATTACH) U( p" n/ g  N
                0 W) K* H4 z5 P& _6 i, O' U
                PUSH    DWORD PTR [hModDLL]
0 z( O& C' R  ?3 b1 m               
8 D& O. A* z: [7 Q  S                CALL    EDI     // 调用入口函数
' a1 ?; ]# N; U               
( }# d) t' v  O8 D) z2 Z1 @                MOV     BYTE PTR [initRetValue],AL  // 保存入口函数返回值
3 K) k  h, K1 X
0 R: o' q; Z! |3 `5 ~$ ]5 ^: U                MOV     DWORD PTR [_SaveSp],ESI // Save stack values after the# T& P$ p+ m( e' q2 H( Z
                MOV     DWORD PTR [_CurSp],ESP  // entry point code returns
( ]9 E8 T! D* E& H; l; X
" t5 E! y/ E# U* W' ~4 t" L" Q$ ?                MOV     ESP,ESI     // Restore ESP to value before the call
, Z, P/ _: l' Z1 N2 x9 r+ c3 Q  X0 p* v7 M3 ^3 t4 A5 ]
                //# ?" k. Q( z4 {
                // 检查调用前后的ESP值是否一至
/ |3 e2 B! q5 b9 z) |  W4 J  // 8 N, r+ k5 O/ k  E
                if ( _CurSP != _SavSP )
  P) R. R2 L" R* z# Z" G                {
! H0 M0 U) ^/ H- v$ F1 C                    hardErrorParam = pModuleLoaderInfo->FullDllPath;" L9 s3 t# ~: ^
! R  D5 C+ S! a) h0 q
                    hardErrorRetCode = ( W2 i, ?/ X# s
                        _NtRaiseHardError(* e4 z  ]: m: Q: a4 Y
                            STATUS_BAD_DLL_ENTRYPOINT | 0x10000000,
7 `. n# c6 Y) \; v/ w5 [- T2 r                            1,  // Number of parameters* v2 d$ n" h- b4 {
                            1,  // UnicodeStringParametersMask,
$ `+ C9 C7 [! a# v8 W( D/ w, z, I                            &hardErrorParam,; n& b9 U9 ]7 N4 w" h+ u
                            OptionYesNo,    // Let user decide% w4 s. U/ N  W# J+ ?+ G9 p4 D
                            &hardErrorResponse );1 o! }0 b8 F8 H# x7 I
                                            
9 c% c  s# M, R1 Z. U; H# h                    if ( _LdrpInLdrInit )
- N4 l  x( G/ x+ Z                        _LdrpFatalHardErrorCount++;. @# p  l0 X/ j1 d1 G

0 y3 \3 ]' y; y+ Y" g                    if (    (hardErrorRetCode >= STATUS_SUCCESS)
5 w0 I& ]* W3 v& O) ~9 \                        &&  (ResponseYes == hardErrorResponse) )
' X3 M0 k- w/ a1 Y- z( Z' H                    {" L/ j2 I  ?; S  G& [; \
                        return STATUS_DLL_INIT_FAILED;
) ~" ~; d& R  p  b; A, A8 V                    }
! Y% \! b) Y5 i# R) O$ B                }
7 P* o$ u' X" j+ z, u5 h. |
1 m7 Z! N' ~: W& g" t                //
- I6 j" r4 v( _, A  _0 m                // 入口函数返回0,错误
/ Z9 x4 Q3 |; W  h3 `                //& l) t* l8 v7 e0 m8 K/ |$ k
                if ( 0 == initRetValue )
1 y+ S2 N' }$ @+ `5 ^' o: g/ |                {
( J6 v+ Q9 a* G% ?/ H                    DWORD hardErrorParam2;
1 r# ^7 k. u0 h4 R  S6 p                    DWORD hardErrorResponse2;' p4 ]7 E+ d" v$ `8 Q0 S4 g$ n
                                        / K  U" w+ v+ O1 M: J$ K6 r! b" D
                    hardErrorParam2 = pModuleLoaderInfo->FullDllPath;
  F" ^9 `" T4 W5 n) E: }! ]/ J                    
2 H# w8 @  m8 P" F1 c- L6 G) @                    _NtRaiseHardError(  STATUS_DLL_INIT_FAILED,/ F/ F; _8 g9 L5 {
                                        1,  // Number of parameters
+ {, E: e4 J7 h                                        1,  // UnicodeStringParametersMask
, t( u. }, ^- _( H/ L1 w                                        &hardErrorParam2,3 N" T! S5 e& a  _  _  O6 u, {
                                        OptionOk,   // OK is only response+ N# ^) ~( g6 B1 B4 U1 Z
                                        &hardErrorResponse2 );
2 z: |. \# U$ F                                                            
) Z& F# J' v( O3 ^  [  c                    if ( _LdrpInLdrInit )
. [( s7 c# {' O, f' z                        _LdrpFatalHardErrorCount++;1 t- V8 r4 N6 w1 u7 z

2 p- S$ O6 K4 S5 e2 y" ^: W. t                    return STATUS_DLL_INIT_FAILED;
$ n) N/ Z  t; P' s- g                }
/ f+ J/ l7 W" ?$ i; u            }, f) J  G: T' l  k9 G
        }- M4 |5 R8 a1 L$ ?2 z! k' z

3 |, ^0 x2 g: _3 F7 |! K" `1 s. C6 P        //, b; y7 A' f& h( P# w
        // 如果EXE已经拥有了TLS,那么调用TLS初始化函数,也是在进程第一次初始化dll时9 {: S/ `9 H; _# q# H+ y
        //      ' f" R$ k9 h2 X" \) N2 v; x
        if ( _LdrpImageHasTls && bImplicitLoad )
# \  [7 c1 M2 q8 D        {
' f4 S7 G1 U$ ~) N            _LdrpCallTlsInitializers(   pCurrentPeb->ProcessImageBase,; m2 z! s, ^5 I3 f
                                        DLL_PROCESS_ATTACH );8 q4 Y( _2 ~9 R* E
        }
6 @$ A' `0 h3 A, J8 d; r9 y    }
7 r1 J2 A2 O/ A* O% n    __finally( q! x; P# K  c" p* t8 U
    {
7 {9 Z  {) w* D- s        //6 Z2 P$ w) R- ~; }% y  J: G3 K5 \7 ?
        // 第四部分;
! K# Z2 `6 X0 @+ F        // 清除分配的内存% Z" O& c% [6 A/ L
        _RtlFreeHeap( GetProcessHeap(), 0, pInitNodeArray );) U- W1 x2 L4 w
    }& W. x* [) r3 F
2 ?. S9 P( b/ N* I, o! E. b
    return STATUS_SUCCESS;
3 g% o) ^+ c  M3 m7 c}   2 D6 Y: j5 w0 [) i0 ~) o

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

本版积分规则

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

GMT+8, 2024-11-15 14:55 , Processed in 0.039457 second(s), 17 queries .

Powered by Discuz! X3.5

© 2001-2023 Discuz! Team.

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