• 旧书重温:0day2【2】 实验:三种获取kernel32.dll基址的方法


    0x01 找kernel32基地址的方法一般有三种:

    暴力搜索法、异常处理链表搜索法、PEB法。

    0x02 基本原理

    暴力搜索法是最早的动态查找kernel32基地址的方法。它的原理是几乎所有的win32可执行文件(pe格式文件)运行的时候都加载kernel32.dll,可执行文件进入入口点执行后esp
    存放的一般是Kernel32.DLL 中的某个地址,所以沿着这个地址向上查找就可以找到kernel32的基地址。
    那么如何知道我们找到的地址是kernel32的基地址呢?
    因为kernel32.dll也是标准的pe结构文件,pe结构文件的开始是IMAGE_DOS_HEADER结构,IMAGE_DOS_HEADER结构的第一个字段是e_magic,它的值为’MZ’用于证明这是DOS兼容的
    文件类型,所以如果我们找到的地址所指向的字符串为’MZ’,那么我们可以确信这是kernel32的基地址

    所谓异常处理链表就是系统提供的处理异常的机制,当系统
    遇到一个不知道如何处理的异常时就会查找异常处理链表,找到对应的异常处理程序,把保存的处理程序地址赋给eip,并执行处理程序,避免系统崩溃,异常处理链表的最后一项
    是默认异常处理函数UnhandledExceptionFilter,因为UnhandledExceptionFilter在kernel32中,所以从UNhandledExceptionFilter地址向上搜索即可找到kernel32的基地址

    PEB法

    TEB结构

     1 //
     2 // Thread Environment Block (TEB)
     3 //
     4 typedef struct _TEB
     5 {
     6     NT_TIB Tib;                             /* 00h */
     7     PVOID EnvironmentPointer;               /* 1Ch */
     8     CLIENT_ID Cid;                          /* 20h */
     9     PVOID ActiveRpcHandle;                  /* 28h */
    10     PVOID ThreadLocalStoragePointer;        /* 2Ch */
    11     struct _PEB *ProcessEnvironmentBlock;   /* 30h */
    12     ULONG LastErrorValue;                   /* 34h */
    13     ULONG CountOfOwnedCriticalSections;     /* 38h */
    14     PVOID CsrClientThread;                  /* 3Ch */
    15     struct _W32THREAD* Win32ThreadInfo;     /* 40h */
    16     ULONG User32Reserved[0x1A];             /* 44h */
    17     ULONG UserReserved[5];                  /* ACh */
    18     PVOID WOW32Reserved;                    /* C0h */
    19     LCID CurrentLocale;                     /* C4h */
    20     ULONG FpSoftwareStatusRegister;         /* C8h */
    21     PVOID SystemReserved1[0x36];            /* CCh */
    22     LONG ExceptionCode;                     /* 1A4h */
    23     struct _ACTIVATION_CONTEXT_STACK *ActivationContextStackPointer; /* 1A8h */
    24     UCHAR SpareBytes1[0x28];                /* 1ACh */
    25     GDI_TEB_BATCH GdiTebBatch;              /* 1D4h */
    26     CLIENT_ID RealClientId;                 /* 6B4h */
    27     PVOID GdiCachedProcessHandle;           /* 6BCh */
    28     ULONG GdiClientPID;                     /* 6C0h */
    29     ULONG GdiClientTID;                     /* 6C4h */
    30     PVOID GdiThreadLocalInfo;               /* 6C8h */
    31     ULONG Win32ClientInfo[62];              /* 6CCh */
    32     PVOID glDispatchTable[0xE9];            /* 7C4h */
    33     ULONG glReserved1[0x1D];                /* B68h */
    34     PVOID glReserved2;                      /* BDCh */
    35     PVOID glSectionInfo;                    /* BE0h */
    36     PVOID glSection;                        /* BE4h */
    37     PVOID glTable;                          /* BE8h */
    38     PVOID glCurrentRC;                      /* BECh */
    39     PVOID glContext;                        /* BF0h */
    40     NTSTATUS LastStatusValue;               /* BF4h */
    41     UNICODE_STRING StaticUnicodeString;     /* BF8h */
    42     WCHAR StaticUnicodeBuffer[0x105];       /* C00h */
    43     PVOID DeallocationStack;                /* E0Ch */
    44     PVOID TlsSlots[0x40];                   /* E10h */
    45     LIST_ENTRY TlsLinks;                    /* F10h */
    46     PVOID Vdm;                              /* F18h */
    47     PVOID ReservedForNtRpc;                 /* F1Ch */
    48     PVOID DbgSsReserved[0x2];               /* F20h */
    49     ULONG HardErrorDisabled;                /* F28h */
    50     PVOID Instrumentation[14];              /* F2Ch */
    51     PVOID SubProcessTag;                    /* F64h */
    52     PVOID EtwTraceData;                     /* F68h */
    53     PVOID WinSockData;                      /* F6Ch */
    54     ULONG GdiBatchCount;                    /* F70h */
    55     BOOLEAN InDbgPrint;                     /* F74h */
    56     BOOLEAN FreeStackOnTermination;         /* F75h */
    57     BOOLEAN HasFiberData;                   /* F76h */
    58     UCHAR IdealProcessor;                   /* F77h */
    59     ULONG GuaranteedStackBytes;             /* F78h */
    60     PVOID ReservedForPerf;                  /* F7Ch */
    61     PVOID ReservedForOle;                   /* F80h */
    62     ULONG WaitingOnLoaderLock;              /* F84h */
    63     ULONG SparePointer1;                    /* F88h */
    64     ULONG SoftPatchPtr1;                    /* F8Ch */
    65     ULONG SoftPatchPtr2;                    /* F90h */
    66     PVOID *TlsExpansionSlots;               /* F94h */
    67     ULONG ImpersionationLocale;             /* F98h */
    68     ULONG IsImpersonating;                  /* F9Ch */
    69     PVOID NlsCache;                         /* FA0h */
    70     PVOID pShimData;                        /* FA4h */
    71     ULONG HeapVirualAffinity;               /* FA8h */
    72     PVOID CurrentTransactionHandle;         /* FACh */
    73     PTEB_ACTIVE_FRAME ActiveFrame;          /* FB0h */
    74     PVOID FlsData;                          /* FB4h */
    75     UCHAR SafeThunkCall;                    /* FB8h */
    76     UCHAR BooleanSpare[3];                  /* FB9h */
    77 } TEB, *PTEB; 

    PEB结构

     1 typedef struct _PEB
     2 {
     3     UCHAR InheritedAddressSpace; // 00h
     4     UCHAR ReadImageFileExecOptions; // 01h
     5     UCHAR BeingDebugged; // 02h
     6     UCHAR Spare; // 03h
     7     PVOID Mutant; // 04h
     8     PVOID ImageBaseAddress; // 08h
     9     PPEB_LDR_DATA Ldr; // 0Ch
    10     PRTL_USER_PROCESS_PARAMETERS ProcessParameters; // 10h
    11     PVOID SubSystemData; // 14h
    12     PVOID ProcessHeap; // 18h
    13     PVOID FastPebLock; // 1Ch
    14     PPEBLOCKROUTINE FastPebLockRoutine; // 20h
    15     PPEBLOCKROUTINE FastPebUnlockRoutine; // 24h
    16     ULONG EnvironmentUpdateCount; // 28h
    17     PVOID* KernelCallbackTable; // 2Ch
    18     PVOID EventLogSection; // 30h
    19     PVOID EventLog; // 34h
    20     PPEB_FREE_BLOCK FreeList; // 38h
    21     ULONG TlsExpansionCounter; // 3Ch
    22     PVOID TlsBitmap; // 40h
    23     ULONG TlsBitmapBits[0x2]; // 44h
    24     PVOID ReadOnlySharedMemoryBase; // 4Ch
    25     PVOID ReadOnlySharedMemoryHeap; // 50h
    26     PVOID* ReadOnlyStaticServerData; // 54h
    27     PVOID AnsiCodePageData; // 58h
    28     PVOID OemCodePageData; // 5Ch
    29     PVOID UnicodeCaseTableData; // 60h
    30     ULONG NumberOfProcessors; // 64h
    31     ULONG NtGlobalFlag; // 68h
    32     UCHAR Spare2[0x4]; // 6Ch
    33     LARGE_INTEGER CriticalSectionTimeout; // 70h
    34     ULONG HeapSegmentReserve; // 78h
    35     ULONG HeapSegmentCommit; // 7Ch
    36     ULONG HeapDeCommitTotalFreeThreshold; // 80h
    37     ULONG HeapDeCommitFreeBlockThreshold; // 84h
    38     ULONG NumberOfHeaps; // 88h
    39     ULONG MaximumNumberOfHeaps; // 8Ch
    40     PVOID** ProcessHeaps; // 90h
    41     PVOID GdiSharedHandleTable; // 94h
    42     PVOID ProcessStarterHelper; // 98h
    43     PVOID GdiDCAttributeList; // 9Ch
    44     PVOID LoaderLock; // A0h
    45     ULONG OSMajorVersion; // A4h
    46     ULONG OSMinorVersion; // A8h
    47     ULONG OSBuildNumber; // ACh
    48     ULONG OSPlatformId; // B0h
    49     ULONG ImageSubSystem; // B4h
    50     ULONG ImageSubSystemMajorVersion; // B8h
    51     ULONG ImageSubSystemMinorVersion; // C0h
    52     ULONG GdiHandleBuffer[0x22]; // C4h
    53     PVOID ProcessWindowStation; // ???
    54 } PEB, *PPEB;

    原理:在NT内核系统中fs寄存器指向TEB结构,TEB+0x30处指向PEB结构,PEB+0x0c处指向PEB_LDR_DATA结构,

    PEB_LDR_DATA+0x1c处存放一些动态链接库地址,第一个指向ntdl.dll,第二个就是kernel32.dll的基地址了

    0x03 验证以上办法可行性

    现在我们就来研究下第一中方法暴力搜索法

    http://blog.csdn.net/syf442/article/details/4383254(更详细的介绍)

    ps:按照上面文章介绍不会触发 非法访问问题,实验证明(环境xp sp2 + vc++6.0) 确实有 非法访问的 异常

     1 #include "stdafx.h"
     2 #include <stdio.h>
     3 
     4 int main()
     5 {
     6 
     7 
     8 _asm { jmp Start }
     9 int ieax;
    10 
    11 
    12 
    13 _asm{
    14 Start:
    15 
    16 
    17 GetKernelBase:                    ;查找 kernel地址
    18         mov eax,7c800000h        ;因为有非法访问我直接把我本机的kerne32.dll地址(7c800000h) 给eax 就可以了
    19 
    20 Compare:
    21         cmp eax,80000000h
    22         jl    SearchFinal
    23         cmp word ptr[eax],'ZM'
    24         je FindedKernelBase
    25         add    eax,010000h
    26         jmp Compare
    27 
    28 
    29 }
    30 FindedKernelBase:
    31 {
    32         _asm{ mov ieax,eax}
    33         printf("kernel addr offset %x 
    ",ieax);
    34         return 0;
    35 }
    36 SearchFinal    :
    37 {            //;查找结束
    38         printf("find kernel faild 
     ");
    39         return 0;
    40 }
    41     return 0;
    42 }

    我刚开始按照老罗的思路,从栈顶向下搜索,有问题

    后来我就从8000000h搜索至70000000h处

    发现有非法访问

    为了确定我的发现

    我从从7000000h搜索至80000000h处

    还是有非法访问

    那我直接把我电脑的kernel32.dll地址 替换 7000000h 为7c800000h 

    直接可以了

    论证 暴力搜索法 不是太通用呀

    第二中办法 (SEH)异常处理链表搜索法

    其中要先补习下基础知识

    异常处理链表末端的处理结构体是系统最后为异常准备的处理(其中的下一个结构指针prev 为-1),就是咱们经常遇到的程序崩溃的提示。其地址是在kernel32 内存空间内部,我们只要找到最后的异常处理结构体,那么我们从

    这个地址找下去一定能找到 ‘MZ’标志(kernel32的地址);

    其中SEH链表位置:fs:[0]->线程信息块TIB,TIB.ExceptionList->SEH链表

    1 nt!_NT_TIB
    2    +0x000 ExceptionList    : Ptr32 _EXCEPTION_REGISTRATION_RECORD            ;SEH链表头
    3    +0x004 StackBase        : Ptr32 Void
    4    +0x008 StackLimit       : Ptr32 Void
    5    +0x00c SubSystemTib     : Ptr32 Void
    6    +0x010 FiberData        : Ptr32 Void
    7    +0x010 Version          : Uint4B
    8    +0x014 ArbitraryUserPointer : Ptr32 Void
    9    +0x018 Self             : Ptr32 _NT_TIB
    1 链表节点
    2 
    3 _EXCEPTION_REGISTRATION struc
    4 
    5     prev           dd ?                   ;下一个_EXCEPTION_REGISTRATION结构
    6 
    7     handler       dd ?                   ;异常处理函数地址
    8 
    9 _EXCEPTION_REGISTRATION ends
     1 #include <stdio.h>
     2 #include <windows.h>
     3 int main()
     4 {
     5  __asm  
     6     {  
     7 
     8 
     9 
    10         mov edx, fs:[0]     // 获得EXCEPTION_REGISTRATION结构地址  
    11 Next:  
    12         inc dword ptr [edx] // 将prev+1,如果是-1经过+1后等于0 (证明找到了 SEH链表的最后一项,也就达到了kernel的内存空间中了) 
                      // 其中第一次时:fs:[0]->线程信息块TIB,TIB.ExceptionList->SEH链表
    13 jz Krnl 14 dec dword ptr [edx] // 不为-1,还原 15 mov edx, [edx] // 获得prev指向的地址 16 jmp Next 17 18 Krnl: 19 dec dword ptr [edx] // 恢复 20 mov edx, [edx + 4] // 获得handle指向的地址 21 22 Looop: 23 cmp word ptr [edx], 'ZM' 24 jz IsPe 25 dec edx 26 xor dx, dx 27 jmp Looop 28 29 IsPe: 30 mov eax, dword ptr [edx + 3ch] 31 cmp word ptr [edx + eax], 'EP' 32 jnz Next 33 mov dwKrnlAddr, edx 34 } 35 printf(TEXT("Kernel32.dll address: %x "), dwKrnlAddr); 36 printf(TEXT("GetModuleHandle Kernel32.dll address: %x "), 37 GetModuleHandle(TEXT("kernel32.dll"))); 38 printf(TEXT("LoadLibrary Kernel32.dll address: %x "), 39 LoadLibrary(TEXT("kernel32.dll"))); 40 return 0; 41 }

              xp sp2 的运行图(圆满的达到目标)

          win7 x64 sp1 的运行结构(其中下边使用DEPENDS.EXE发现的 DLL的基址)目测没有达到预期目标,难道是win7有什么猫腻,下小节动态跟踪下

    但是论证了下发现此办法在xp sp2上 可以达到目的,而win7 x64 sp1 目测没有达到目的

    进一步验证 SEH异常链表搜索法 在 win7上失败的原因

     动态跟踪了下发现确实找到了MZ的标志,也验证存在PE标志,但是找的和 GetModuleHandle 、LoadLibrary 获取到的不对

    据推测可能是 异常链表最后一项的系统处理 (在win7 下)不再kernel32内存空间中,在其他dll内存空间中。。。

    win7下的 异常链表最后一项的系统处理  在ADVAPI32地址空间下(或许这样把)

    接下来 我们实验 第三种办法PEB法

    基础知识必须要学习点,其中TEB和PEB结构看上面。

     原理:在NT内核系统中fs寄存器指向TEB结构,TEB+0x30处指向PEB结构,PEB+0x0c处指向PEB_LDR_DATA结构,

    PEB_LDR_DATA+0x1c处存放一些动态链接库地址,第一个指向ntdl.dll,第二个就是kernel32.dll的基地址了

    我先尝试这写下汇编代码这次就直接在win7上调试(很久前,我调试过PEB找kernel32地址的代码,确实可行)

    下面代码通过PEB获取得到了kernel32地址,通过函数表 得到了 GetProcAddress函数地址,通过此函数地址 

    获取Beep()函数地址,来证明 win7下 是可行的。。。

        int    (*pv)(HINSTANCE,char*);
        //pv = GetProcAdr;
        //pv = GetProcAdr;
        DWORD pBeep = 0;
        DWORD pGetProcAddress = 0;
        DWORD pKernel32 =0;
    
        HINSTANCE hK = GetModuleHandle("kernel32.dll");
        //Beep
        printf(" Beep is %x 
    ",GetProcAddress(hK,"Beep"));
    
        _asm
        {
            push eax
                push esi
                push edx
                push ebp
    
                push esp
    
                sub esp,400h
    
    
                mov eax, fs:0x30 ;PEB的地址 
                mov eax, [eax + 0x0c] ;Ldr的地址 
                mov esi, [eax + 0x1c] ;Flink地址 
                lodsd  
                mov eax, [eax + 0x08] ;eax就是kernel32.dll的地址 
                mov pKernel32,eax
                mov ebp,eax
                mov eax, [ebp+3Ch] ;eax = PE首部 
                mov edx,[ebp+eax+78h] 
            add edx,ebp ;edx = 引出表地址 
                mov ecx , [edx+18h] ;ecx = 输出函数的个数 
                mov ebx,[edx+20h]  
            add ebx, ebp ;ebx =函数名地址,AddressOfName  
    search: 
            dec ecx 
                mov esi,[ebx+ecx*4]  
            add esi,ebp ;依次找每个函数名称 
                ;GetProcAddress 
                mov eax,0x50746547 
                cmp [esi], eax; 'PteG' 
                jne search 
                mov eax,0x41636f72 
                cmp [esi+4],eax; 'Acor' 
                jne search  
                ;如果是GetProcA,表示找到了  
                mov ebx,[edx+24h] 
            add ebx,ebp ;ebx = 序号数组地址,AddressOf 
                mov cx,[ebx+ecx*2] ;ecx = 计算出的序号值 
                mov ebx,[edx+1Ch] 
            add ebx,ebp ;ebx=函数地址的起始位置,AddressOfFunction 
                mov eax,[ebx+ecx*4]  
            add eax,ebp ;利用序号值,得到出GetProcAddress的地址 
    
                add esp,400h
                pop esp
                pop ebp
                pop edx
                pop esi
    
                //mov ebx,[eax + 3ch ] 
                //mov ebx,[eax + ebx + 78h]
                //add ebx,eax
                //mov ebx,[ebx+20h]
                //add ebx,eax
    
                mov pGetProcAddress,eax
                mov pv,eax
                pop eax 
    
                // yan zheng han GetAddress 正确性
                // beep
                //sub esp,90h
    
                //push 0x70656562
                //push hK
                //call pv
    
                //add esp,90h
                ////add esp,8h
                //mov pBeep,eax
        }
        int a = (pv)(hK,"Beep") ;
    
    
        printf(" Beep is %x 
    ", a   );
    
        printf("kernel32 addr is %x , PEB get GetProcAddress addr is %x 
    ",pKernel32,pGetProcAddress);
    
        printf("kernel32 addr is %x , 
     GetProcAddress() get GetProcAddress addr is %x 
    ",GetModuleHandle("kernel32.dll"),GetProcAddress(GetModuleHandle("kernel32.dll"),"GetProcAddress"));
    

    (以上代码 在 int a = (pv)(hK,"Beep") ; 存在 chkesp 提示,不知如何平衡堆栈,调试了好久 还请 高人指点 )

    虽然在 win7 搜索得到的GetProcAddress地址 和 用 GetProcAddress()函数获取得到的GetProcAddress地址不同;但是,通过搜索得到的 GetProcAddress地址 调用这个地址 获取到的Beep()函数都是相同的,Ollydby动态调试,也证实了以上结论!

    文章到这里就算结束了!!!!

  • 相关阅读:
    哈希表详解
    简单字典实现(KV问题)
    【数据结构】——搜索二叉树的插入,查找和删除(递归&非递归)
    【数据结构】——堆及其应用
    初学者的迷茫
    【数据结构】顺序表和链表
    程序运行机理
    回调函数到底是怎么一回事呢?
    C语言中函数可变参数解析
    《剑指offer》数组中出现次数超过数组长度一半的数字
  • 原文地址:https://www.cnblogs.com/witty/p/3457105.html
Copyright © 2020-2023  润新知