免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 968 | 回复: 0
打印 上一主题 下一主题

[Anti Virus专题] 1.2 - 2.kernel32基地址获得 (ZT) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-12-21 08:43 |只看该作者 |倒序浏览
1.2 2.kernel32基地址获得

    

     kernel32基地址的获得也是病毒中必不可少的技术,因为在win32环境中一般初始化PE文件的时候,我们的ntdll.dll, kernel32.dll是随进程以及线程初始化时候加载的。所以即使程

序中我们不引入任何输入表结构,这两个DLL在我们程序进程以及线程初始化时也必须加载。所以我们今天这篇文章的目的就是在我们的进程内存空间中来获得加载kernel32.dll的基地址。因为我们获得

kernel32.dll的基地址后,就可以获得LoadLibray函数来继续加载其他的动态链接库,然后再通过我们的GetProcAdress函数来获得相应需要的api函数地址,这样也就做到了可移植性的要求,所有的函数

均是自己动态获取并填充。

    

     今天我给大家介绍三种获取kernel32.dll基地址的技术,其实还有部分方法也可以获得,只是这3种比较通用,也是病毒中经常用到的。它们分别是

     1. 通过线程初始化时, 获得esp堆栈指针中的ExitThread函数的地址,然后通过搜索获得kernel32.dll的基地址。

     2. 遍历seh异常链,然后获得EXCEPTION_REGISTRATION结构prev为-1的异常处理过程地址,这个异常处理过程地址是位于kernel32.dll,通过它搜索得到kernel32.dll的基地址。

     3. 通过TEB获得PEB结构地址,然后再获得PEB_LDR_DATA结构地址,然后遍历模块列表,查找kernel32.dll模块的基地址。
    
   1.

第一种方法:

   我们上面介绍了线程在被初始化的时,其堆栈指针指向ExitThread函数的地址,windows这样做是为了通过ret返回时来调用ExitThread地址。所以一般我们可以在我们主线程的起始位置(也就是

程序入口点处)通过获得堆栈指针中ExitThread函数(当然你要想创建一个线程时候获得也可以o(∩_∩)o...哈哈)。

   我们直接通过
   mov   edx, [esp] ;获得堆栈指针中ExitThread地址到edx寄存器。因为ExitThread地址在kernel32.dll空间中,所以我们可以通过它往上搜索来获得基地址。

   分析过kernel32.dll的朋友应该都知道kernel32.dll的块对齐值是00001000h, 并且一般DLL以1M为边界,所以我们可以通过10000h (64k) 作为跨度,这样可以增加搜索速度。我们如何确定这个

地址是基地址,我们都知道我们判断这个地址的前两个字节是否是"MZ",然后定位到PE头结构,然后判断是否是"PE",如果这两个都符合的话则表示我们的地址则是基地址了。。

   有了以上的了解我们就可以来写代码了。


   mov   edx, [esp]
.Next:  
   dec   edx   ;
   xor   dx, dx   ; 减去跨度
   cmp   word [edx], "MZ"
   jz   .IsPe
   jmp   .Next
  
.IsPe:
   mov   eax, [edx+3ch]
   cmp   word [eax+edx], 'PE'
   jnz   .Next
   xchg   eax, edx    ; eax = kernel32 基地址


这里贴一个封装过的过程。

代码:
 ;++
;
; int
;    GetKrnlBase(
;       int KrnlApiAddress
;    )
;
; Routine Description:
;
;     获得kernel32基地址
;
; Arguments:
;
;     (esp)           - return address
;
;     Data    (esp+4) - KrnlApiAddress, ptr ApiAddress
;
;
; Return Value:
;
;     eax =   krnl32 base
;
;--

GetKrnlBase:
    pop   eax
    pop   edx
    push   eax
   
.Next:
    cmp   word [edx], 'MZ'
    jz   .IsPe
   dec   edx
   xor   dx, dx
    jmp   .Next

.IsPe:  
    mov   eax, [edx+3ch]
    cmp   word [eax+edx], 'PE'
    jnz   .Next
    xchg   eax, edx
    ret
  
2.
   第二种方法:
  
   这个方法是遍历遍历seh异常链,然后获得EXCEPTION_REGISTRATION结构prev为-1的异常处理过程地址,这个异常处理过程地址是位于kernel32.dll,通过它搜索得到kernel32.dll的基地址。
搜索的方法在上面我已经说了,通过减去跨度,然后判断地址前两字节是否是"MZ",是的话,继续定位到PE头结构,然后判断前两个字节是否是"PE",不是的话继续减去跨度搜索。直到是为止。

   struct EXCEPTION_REGISTRATION
         prev dd ?                       
         handler dd ?                   
   ends

   遍历的方法也很简单,我们都知道[fs:0]的ExceptionList 指向EXCEPTION_REGISTRATION结构,所以通过[fs:0]获得EXCEPTION_REGISTRATION结构后,判断prev成员是否是-1,如果是的话则取

异常处理过程地址,然后进行搜索。
  
  
   有了思路,我们开始写代码。

   mov   edx, [fs:0]   ;获得EXCEPTION_REGISTRATION结构地址
.Next:
   inc   dword [edx]   ;将prev成员 + 1
   jz   .Krnl     ;如果ZF = 1, 则跳转.Krnl
   dec   dword [edx]
   mov   edx,   [edx]
   jmp   .Next
.Krnl:
   dec   dword [edx]   ;恢复 -1
   mov   edx, [edx+4]   ;获得handler, 然后下面进行搜索..
.Loop:
   cmp   word [edx], 'MZ'
    jz   .IsPe
   dec   edx
   xor   dx, dx
   jmp   .Loop

.IsPe:  
    mov   eax, [edx+3ch]
    cmp   word [eax+edx], 'PE'
    jnz   .Next
    xchg   eax, edx   ; eax = kernel32 基地址

一个封装过的过程:
代码:
 ;++
;
; int
;    GetKrnlBase2(
;     void
;    )
;
; Routine Description:
;
;     获得kernel32基地址
;
; Arguments:
;
;     (esp)           - return address
;
;
; Return Value:
;
;     eax =   krnl32 base
;
;--

GetKrnlBase2:
    mov   edx, [fs:0]
.Next:
   inc   dword [edx]
   jz   .Krnl
   dec   dword [edx]
   mov   edx,   [edx]
   jmp   .Next
.Krnl:
   dec   dword [edx]
   mov   edx, [edx+4]
.Loop:
   cmp   word [edx], 'MZ'
    jz   .IsPe
   dec   edx
   xor   dx, dx
   jmp   .Loop

.IsPe:  
    mov   eax, [edx+3ch]
    cmp   word [eax+edx], 'PE'
    jnz   .Next
    xchg   eax, edx
    ret
3.

第三种方法:

   此方法是通过TEB获得PEB结构地址,然后再获得PEB_LDR_DATA结构地址,然后遍历模块列表,查找kernel32.dll模块的基地址。

   TEB是线程环境块(Thread Environment Block)结构, 我们的fs段选择子所对应的段指向TEB,也就是fs:0(注意这里可不是“[fs:0]”)指向TEB.那么TEB的ProcessEnvironmentBlock结构成员指

向我们的PEB进程环境块结构(Process Environment Block),然后通过PEB结构来获得PEB_LDR_DATA。 接下来我们通过windbg来查看下相关结构。

我们首先来看下TEB结构,通过windbg的dt命令。

lkd> dt _TEB
nt!_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   ;;;;;;;;;;

    ......省略


我们可以看到TEB结构的0x30偏移处存储的我们的PEB结构的地址。。

    然后接下来我们来看PEB结构。

lkd> dt _PEB
nt!_PEB
    +0x000 InheritedAddressSpace : UChar
    +0x001 ReadImageFileExecOptions : UChar
    +0x002 BeingDebugged     : UChar
    +0x003 SpareBool         : UChar
    +0x004 Mutant            : Ptr32 Void
    +0x008 ImageBaseAddress : Ptr32 Void
    +0x00c Ldr               : Ptr32 _PEB_LDR_DATA
    ..........省略

我们可以看到PEB结构的0x0c偏移处存储的我们的_PEB_LDR_DATA结构地址。

     好到这里我们可以先把获得_PEB_LDR_DATA结构地址的代码写出来。

     mov eax, [fs:30h] ;Get Peb
     mov eax, [eax+0ch] ;Get _PEB_LDR_DATA

然后我们再来查看 _PEB_LDR_DATA结构

lkd> dt _PEB_LDR_DATA
nt!_PEB_LDR_DATA
    +0x000 Length            : Uint4B
    +0x004 Initialized       : UChar
    +0x008 SsHandle          : Ptr32 Void
    +0x00c InLoadOrderModuleList : _LIST_ENTRY
    +0x014 InMemoryOrderModuleList : _LIST_ENTRY
    +0x01c InInitializationOrderModuleList : _LIST_ENTRY
    +0x024 EntryInProgress   : Ptr32 Void

我们看到这个结构中的模块立标有3个_LIST_ENTRY结构,它们分别是
InLoadOrderModuleList (加载顺序模块列表)
InMemoryOrderModuleList(内存顺序模块排列)
InInitializationOrderModuleList(初始化顺序模块列表)

然后我们继续查看这个结构。

nt!_LIST_ENTRY
    +0x000 Flink             : Ptr32 _LIST_ENTRY
    +0x004 Blink             : Ptr32 _LIST_ENTRY
  
这个结构我们可以看到它是一个双向链表,Flink表示从前往后, Blink表示从后往前。

并且这三个链表的结点是均是指向此结构

typedef struct _LDR_MODULE
{
     LIST_ENTRY         InLoadOrderModuleList;             // +0x00
     LIST_ENTRY         InMemoryOrderModuleList;           // +0x08
     LIST_ENTRY         InInitializationOrderModuleList;   // +0x10
     PVOID              BaseAddress;                       // +0x18
     PVOID              EntryPoint;                        // +0x1c
     ULONG              SizeOfImage;                       // +0x20
     UNICODE_STRING     FullDllName;                       // +0x24
     UNICODE_STRING     BaseDllName;                       // +0x2c
     ULONG              Flags;                             // +0x34
     SHORT              LoadCount;                         // +0x38
     SHORT              TlsIndex;                          // +0x3a
     LIST_ENTRY         HashTableEntry;                    // +0x3c
     ULONG              TimeDateStamp;                     // +0x44
                                                         // +0x48
} LDR_MODULE, *PLDR_MODULE;


我们一般取它的初始化顺序结构(InInitializationOrderModuleList)的Flink成员指向的_LDR_MODULE结构的BaseAddress成员则为我们需要的基地址,当然由于第一个是
ntdll,所以取第二个则为我们的Kernel32.dll。

OK我们开始写代码,上面我们已经写了取得_PEB_LDR_DATA结构地址的代码了。我们把它copy下来。

     mov eax, [fs:30h] ;Get Peb
     mov eax, [eax+0ch] ;Get _PEB_LDR_DATA
     mov eax, [eax+1ch];Get InInitializationOrderModuleList.Flink, 此时eax指向的是ntdll模块的InInitializationOrderModuleList线性地址。所以我们获得它的下一个则是 kernel32.dll
     mov eax, [eax]
     mov eax, [eax+8] ; 8 = sizeof.LIST_ENTRY
     ret

同样我们贴一个封装过的过程。
代码:
 ;++
;
; int
;    GetKrnlBase3(
;     void
;    )
;
; Routine Description:
;
;     获得kernel32基地址
;
; Arguments:
;
;     (esp)           - return address
;
;
; Return Value:
;
;     eax =   krnl32 base
;
;--

GetKrnlBase3:
    mov   eax, [fs:30h]
    mov   eax, [eax+0ch]
    mov   eax, [eax+1ch]
    mov   eax, [eax]
   mov   eax, [eax+8h]
   ret   

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP