吾愛破解 - LCG - LSG |安卓破解|病毒分析|huihengkj.com

 找回密碼
 注冊[Register]

QQ登錄

只需一步,快速開始

搜索
查看: 17117|回復: 302

[游戲安全] 驅動外掛的原理及檢測手段

    [復制鏈接]
樓主
iTruth 發表于 2020-7-13 14:31 回帖獎勵
本帖最后由 iTruth 于 2020-7-14 09:17 編輯

驅動外掛的原理及檢測手段

因為PatchGuard技術的存在導致游戲在驅動層的保護不能像以前那樣通過SSDT Hook或者IDT Hook來做了,游戲廠家不能擅自關閉PatchGuard來強行Hook.這樣留給驅動掛的空間就很大了
我將以一個自瞄掛的原理為例子展示驅動外掛的幾種實現方式及檢測手段

相同點

要想實現自瞄驅動掛基本上都是讀取游戲數據然后直接操作鼠標,不同之處就是操縱鼠標的方式
500彩票邀请码 下面是所有驅動掛的幾個相同之處

相同點1 - 獲取鼠標的驅動對象

一個自瞄驅動掛的實現首先必然需要獲得鼠標的驅動對象,在任何驅動掛里應該都是一樣的
可以通過ObReferenceObjectByName來獲取鼠標驅動對象
其中ObReferenceObjectByName是未公開的函數,聲明一下就能用.其聲明如下

NTSTATUS ObReferenceObjectByName(
        PUNICODE_STRING ObjectName,
        ULONG Attributes,
        PACCESS_STATE AccessState,
        ACCESS_MASK DesiredAccess,
        POBJECT_TYPE ObjectType,
        KPROCESSOR_MODE AccessMode,
        PVOID ParseContest,
        PVOID* Object
);

extern POBJECT_TYPE* IoDriverObjectType; // 這是ObjectType參數的實參

其中鼠標驅動的名稱為\\Driver\\MouClass
那么具體獲取鼠標驅動對象過程如下:

        NTSTATUS status = STATUS_SUCCESS;
  UNICODE_STRING mouseName;
  RtlInitUnicodeString(&mouseName, L"\\Driver\\MouClass");
  // 獲取到鼠標驅動對象將保存至此
  PDRIVER_OBJECT mouseDriver;

  status = ObReferenceObjectByName(&mouseName, OBJ_CASE_INSENSITIVE, NULL, 0, *IoDriverObjectType, KernelMode, NULL, &mouseDriver);
  if (!NT_SUCCESS(status)) {
    return status;
  } else {
    // 獲取失敗了需要解引用
    ObDereferenceObject(mouseDriver);
  }

這樣一個鼠標驅動對象就在mouseDriver變量中了

相同點2 - 控制驅動掛的手段

用戶要想控制驅動首先要通過CreateFile來打開驅動設備
也就是說在驅動里必然先要創建好一個設備供用戶打開,具體步驟如下

// 設備名和符號名的定義
#define ITRUTH_DEVICE_NAME L"\\Device\\iTruth_Device_20d04fe0"
#define ITRUTH_SYMB_NAME L"\\DosDevice\\iTruth_Device"

  // 函數里設備創建過程
  UNICODE_STRING dev_name;
        RtlInitUnicodeString(&dev_name, ITRUTH_DEVICE_NAME);
        UNICODE_STRING sddl;
  // SDDL語法請參考http://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/sddl-for-device-objects
        RtlInitUnicodeString(&sddl, L"D:P(A;;GA;;;BU)");
  // 這個請自己定,每個驅動都不能一樣
        GUID dev_guid = { 0x3cff2c3aL, 0x320f, 0xf5aa, "iTruth" };

  // 創建設備
        status = IoCreateDeviceSecure(
                DriverObject,
                0,
                &dev_name,
                FILE_DEVICE_UNKNOWN,
                FILE_DEVICE_SECURE_OPEN,
                FALSE,
                &sddl,
                &dev_guid,
                &iTruth_Device
        );

        if (NT_SUCCESS(status)) {
                UNICODE_STRING dos_dev_name;
                RtlInitUnicodeString(&dos_dev_name, ITRUTH_SYMB_NAME);
    // 為設備綁定符號鏈接,用戶只能通過這個符號鏈接打開設備
                IoCreateSymbolicLink(&dos_dev_name, &dev_name);

    // 綁定處理函數
                DriverObject->MajorFunction[IRP_MJ_CREATE] = iTruth_DriverDispatch;
                DriverObject->MajorFunction[IRP_MJ_CLOSE] = iTruth_DriverDispatch;
                DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = iTruth_DriverDispatch;
        }

在用戶層控制驅動基本上就是DeviceIoControl函數了,此函數向指定驅動發送IO控制碼(CTL_CODE),其中控制碼可以由一個名為CTL_CODE的宏來定義,這個宏的聲明如下

#define CTL_CODE(DeviceType, Function, Method, Access) (
  ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method)
)

第一個參數是驅動的類型,驅動掛一般就填FILE_DEVICE_UNKNOWN即可.
第二個是操作碼,這個可以自已定.
第三個是傳遞緩沖區的方式,METHOD_BUFFERED是比較簡單的方式
第四個是完成此動作需要的權限,不知道就填FILE_ANY_ACCESS
下面是IOCTL的兩個例子:

#define IOCTL_ID_DBGPRINT CTL_CODE(FILE_DEVICE_UNKNOWN, 0x700, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_ID_FUNCHOOK CTL_CODE(FILE_DEVICE_UNKNOWN, 0x701, METHOD_BUFFERED, FILE_WRITE_DATA)

用戶層的CTL_CODE和內核里的應該是一樣的,所以這個控制碼的定義一般單獨放在一個頭文件
驅動的入口函數是DriverEntry,其中第一個參數是PDRIVER_OBJECT類型的代表了當前驅動
這個參數里有一個名為MajorFunction的數組,里面包含了各種驅動處理函數
這個數組第IRP_MJ_DEVICE_CONTROL(0x0e)個元素是處理IO的回調函數,這個值是自己指定的
500彩票邀请码 一個典型的處理IRP_MJ_DEVICE_CONTROL的函數如下

NTSTATUS DriverDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp)
{
        NTSTATUS status = STATUS_SUCCESS;
        PIO_STACK_LOCATION irp_loc = IoGetCurrentIrpStackLocation(Irp);

        if (DeviceObject == myDevice) {
                if (irp_loc->MajorFunction == IRP_MJ_CREATE || irp_loc->MajorFunction == IRP_MJ_CLOSE) {
                        IoCompleteRequest(Irp, IO_NO_INCREMENT);
                        return STATUS_SUCCESS;
                }

                for (PLIST_ENTRY pl = dev_list_head.Flink; pl != (PLIST_ENTRY)&dev_list_head.Flink; pl = pl->Flink) {
                        PITRUTH_DEV_ENTRY dev_entry = CONTAINING_RECORD(pl, ITRUTH_DEV_ENTRY, dev_list);
                        if (dev_entry->flt_dev_obj == DeviceObject) {
                                if (irp_loc->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
                                        switch (irp_loc->Parameters.DeviceIoControl.IoControlCode) {
                                        //這里可以看到我們判斷當前需要的功能部分
                                        case IOCTL_ID_DBGPRINT:
                                                KdPrint((Irp->AssociatedIrp.SystemBuffer));
                                                Irp->IoStatus.Status = STATUS_SUCCESS;
                                                break;
                                        case IOCTL_ID_FUNCHOOK:
                                                KdPrint(("Kernel: Hook\n"));
                                                Irp->IoStatus.Status = STATUS_SUCCESS;
                                                break;

                                        default:
                                                IoSkipCurrentIrpStackLocation(Irp);
                                                return IoCallDriver(dev_entry->next_dev_obj, Irp);
                                        }
                                }

                                IoSkipCurrentIrpStackLocation(Irp);
                                return IoCallDriver(dev_entry->next_dev_obj, Irp);
                        }
                }
        }
}

可以看到里面有根據我們的CTL_CODE來執行功能的switch語句,這個就是具體的控制原理

不同的鼠標控制手段

最簡單的手段 - 通過過濾設備來控制鼠標

原理

這種手段本質上是使用名為IoAttachDeviceToDeviceStack的函數將自己創建的設備對象綁定到設備對象鏈中的最高層,然后使用自己Driver Dispatch回調函數來修改鼠標輸入
綁定過程如下:

  // 在相同點那里已經展示了鼠標驅動設備的獲取方式,所以這里省略
  // 獲取鼠標驅動設備鏈中的第一個設備
        targetDevice = mouseDriver->DeviceObject;
        // 遍歷設備鏈中的所有設備
        while (targetDevice)
        {
                // 創建一個過濾設備
                status = IoCreateDevice(pDriver, sizeof(DEV_EXTENSION), NULL, targetDevice->DeviceType, targetDevice->Characteristics, FALSE, &filterDevice);
                if (!NT_SUCCESS(status))
                {
                        filterDevice = targetDevice = NULL;
                        return status;
                }
                // 在這步綁定
                nextDevice = IoAttachDeviceToDeviceStack(filterDevice, targetDevice);
                if (!nextDevice)
                {
                        IoDeleteDevice(filterDevice);
                        filterDevice = NULL;
                        return status;
                }
                targetDevice = targetDevice->NextDevice;
        }

500彩票邀请码到了這步綁定好讀取的分發函數那么鼠標的讀取IRP就會發送到我們的驅動然后我們即可對其處理

pDriver->MajorFunction[IRP_MJ_READ] = MouseIRPMJRead;

500彩票邀请码下面是鼠標讀取IRP的處理

NTSTATUS MouseIRPMJRead(PDEVICE_OBJECT pDevice, PIRP pIrp, PVOID Context)
{
        UNREFERENCED_PARAMETER(pDevice);
        UNREFERENCED_PARAMETER(Context);
        PIO_STACK_LOCATION stack;
        PMOUSE_INPUT_DATA myData;
        stack = IoGetCurrentIrpStackLocation(pIrp);
        if (NT_SUCCESS(pIrp->IoStatus.Status))
        {
                // 獲取鼠標數據
                myData = pIrp->AssociatedIrp.SystemBuffer;
                // 這里即可開始讀取游戲數據并更改鼠標的IRP
        }
        if (pIrp->PendingReturned)
        {
                IoMarkIrpPending(pIrp);
        }
        return pIrp->IoStatus.Status;
}

檢測手段

畢竟是在設備棧上添加自己的設備,那么只需要一個設備黑名單即可.
通過遍歷設備棧只要找到了外掛創建的設備即判定非法
判斷的核心代碼:

// 設備對象的拓展結構
typedef struct _DEVICE_EXTENSION {
        PDEVICE_OBJECT pDevice;
        UNICODE_STRING ustrDeviceName;
        UNICODE_STRING ustrSymLinkName;
} DEVICE_EXTENSION, * PDEVICE_EXTENSION;

        // 檢測代碼
        targetDevice = mouseDriver->DeviceObject;
        // 遍歷設備鏈中的所有設備
        while (targetDevice)
        {
    PDEVICE_EXTENSION exdev = (PDEVICE_EXTENSION)targetDevice;
    // 現在targetDevice->ustrDeviceName就是設備名了,下面即可自行判斷這個設備名是否合法

                targetDevice = targetDevice->NextDevice;
        }

比較好的方式 - Hook或直接調用MouseServiceClassCallBack

MouseClassServiceCallback是Mouclass提供的類服務回調函數。 驅動程序在其ISR dispatch completion routine中調用類服務回調。 類服務回調將輸入數據從設備的輸入數據緩沖區傳輸到類數據隊列。
所以這種方式更底層,目前見過的所有模擬鼠標的驅動基本也都是用的這種方式
關鍵在于找到這個函數,尋找這個函數可以遍歷設備對象也可以搜特征碼
遍歷設備對象的尋找目標函數的方式如下

// 全局定義MouseClassServiceCallback
typedef VOID
(*MouseClassServiceCallback) (
    PDEVICE_OBJECT  DeviceObject,
    PMOUSE_INPUT_DATA  InputDataStart,
    PMOUSE_INPUT_DATA  InputDataEnd,
    PULONG  InputDataConsumed
    );
// 保存原始函數
MouseClassServiceCallback orig_MouseClassServiceCallback = NULL;

// 我們已經獲取過鼠標的設備驅動保存在了mouseDriver中,現在獲取鼠標的端口驅動
UNICODE_STRING mouNtName;
PDRIVER_OBJECT mouhidDriverObj;
RtlInitUnicodeString(&mouNtName, L"\\Driver\\Mouhid");
status = ObReferenceObjectByName(
    &mouNtName,
    OBJ_CASE_INSENSITIVE,
    NULL,
    0,
    IoDriverObjectType,
    KernelMode,
    NULL,
    &mouhidDriverObj
    );
if (!NT_SUCCESS(status)) {
  return status;
}
// 遍歷mouclass下所有設備
PDRIVER_OBJECT targetDriverObj = mouseDriver->DeviceObject;
ULONG mouDriverStart = (ULONG)GetModlueBaseAdress("mouclass.sys", 0);
ULONG mouDriverSize = 0x2000;
MouseClassServiceCallback* MouSrvAddr = NULL;
while(targetDriverObj)
{
  // 遍歷我們先找到的端口驅動的設備擴展下的每個指針
  for(PBYTE exdev = (PBYTE)mouhidDriverObj; i<4096; ++i; exdev+=sizeof(PBYTE))
  {
    if (!MmIsAddressValid(exdev)) {
      break;
    }

    //如果在設備擴展中找到一個地址位于mouClass模塊中,就認為這是我們要的回調函數地址
    PVOID tmp = *(PVOID*)exdev;

    if ((tmp > mouDriverStart)&&(tmp < (PBYTE)mouDriverStart+mouDriverSize)) {
      orig_MouseClassServiceCallback  = (MouseClassServiceCallback)tmp;
      MouSrvAddr = (PVOID*)exdev;
      goto Done;
    }
  }
  targetDriverObj = targetDriverObj->NextDevice;
}
Done:
// 這里就獲取到了哪個函數的地址并保存到了orig_MouseClassServiceCallback中

500彩票邀请码現在我介紹那兩種方式以及檢測手段

Hook方式
原理

直接改函數地址即可

*MouSrvAddr = myHookFuncAddr;

500彩票邀请码這種方式比較方便的地方是不用自己構建MOUSE_INPUT_DATA

檢測手段

500彩票邀请码這種方式直接檢測函數地址即可

直接調用方式
原理

我們剛剛獲取了函數指針那么直接調用就能使鼠標移動,麻煩的是要自己構造MOUSE_INPUT_DATA

檢測手段

這種方式目前沒有很好的檢測手段,如果各位大佬有辦法請務必讓本菜見識下

雜談

當然也是有通過Hook IDT的鼠標中斷來實現的,這種方式麻煩的地方在于要為CPU里每個核心都做一遍Hook操作.而且也能簡單的通過特征碼的方式檢測出來
500彩票邀请码 最重要的一點其實還在于如果做IDT Hook那么還不如直接修改空閑中斷的DPL和中斷程序地址來做中斷提權,讓我們的外掛程序有Ring0權限.我感覺這樣才是更好的辦法

免費評分

參與人數 131威望 +2 吾愛幣 +217 熱心值 +112 收起 理由
吳劍先 + 1 + 1 我很贊同!
xiahhhr + 1 + 1 用心討論,共獲提升!
鶴舞九月天 + 1 + 1 我很贊同!
NGUlyb + 1 + 1 我很贊同!
憂花殤傾城淚 + 1 + 1 nice,666
邱淑貞 + 1 + 1 用心討論,共獲提升!
liujia + 1 我很贊同!
月六點年一倍 + 1 用心討論,共獲提升!
jinfangfa + 1 + 1 我很贊同!
玄冥嵐 + 1 + 1 我很贊同!
vistaer + 1 + 1 我很贊同!
Aperson + 1 + 1 熱心回復!
15088472275 + 1 + 1 我很贊同!
Po1estar__ + 1 我很贊同!
yangkaicheng + 1 鼓勵轉貼優秀軟件安全工具和文檔!
TIANW + 1 我很贊同!
liyqckli + 1 + 1 我很贊同!
guazi1990 + 1 我很贊同!
一乘 + 1 + 1 用心討論,共獲提升!
空如此生 + 1 我很贊同!
Imagon + 1 + 1 謝謝@Thanks!
啦啦啦666 + 1 感覺還是有幫助的
Jack2002 + 1 + 1 謝謝@Thanks!
葉一葦 + 1 用心討論,共獲提升!
flesy + 1 + 1 我很贊同!
manypepper + 1 + 1 謝謝@Thanks!
w079967 + 1 + 1 我很贊同!
Sya97 + 1 + 1 我很贊同!
技能管家 + 1 + 1 熱心回復!
xhymq + 1 + 1 謝謝@Thanks!
mfkghdtjh + 1 + 1 我很贊同!
xiaoyin2018 + 1 用心討論,共獲提升!
優雅的佩奇 + 1 熱心回復!
net6sji4 + 1 用心討論,共獲提升!
清蒸麒麟臂 + 1 + 1 強啊
z909022684 + 1 + 1 我很贊同!
kasen + 1 + 1 用心討論,共獲提升!
qqtzong + 1 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
hgfty1 + 1 + 1 我很贊同!
小淺88 + 1 謝謝@Thanks!
nicecone + 1 + 1 用心討論,共獲提升!
下不完的雨 + 1 + 1 謝謝@Thanks!
大頭憨憨 + 1 + 1 我很贊同!
leaven + 1 用心討論,共獲提升!
歐尼撒馬 + 1 + 1 大佬,牛批
GaaraZL + 1 + 1 謝謝@Thanks!
河瞬 + 1 + 1 熱心回復!
lazyxi + 1 我很贊同!
龍翔dh + 1 + 1 熱心回復!
xuwenyi123 + 1 熱心回復!
魔法少女伊莉雅 + 1 + 1 用心討論,共獲提升!
夏小宇 + 1 我很贊同!
jqcipojie + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
lyj17335671675 + 1 用心討論,共獲提升!
sexyback_123 + 1 用心討論,共獲提升!
young1313 + 1 小白表示看不懂
7335379 + 1 + 1 我很贊同!
z77 + 1 + 1 鼓勵轉貼優秀軟件安全工具和文檔!
1732501894 + 1 難道不是做外掛的?然后向大撈尋求檢測機制再進行改進?
二十九殺 + 1 + 1 我很贊同!
yixi + 1 + 1 謝謝@Thanks!
沐柒乀 + 1 + 1 不明覺厲,漲姿勢了
奶味小可愛 + 1 + 1 謝謝@Thanks!
fengbolee + 1 + 1 用心討論,共獲提升!
gh0st_ + 1 用心討論,共獲提升!
若凡 + 1 + 1 雖然看不懂但是不明覺厲
app740520 + 1 + 1 謝謝@Thanks!
Lugia + 1 + 1 謝謝@Thanks!
gaosld + 1 + 1 用心討論,共獲提升!
放藥不放糖 + 1 + 1 謝謝@Thanks!
一杯只姝小屁 + 1 + 1 我很贊同!
wws天池 + 1 + 1 用心討論,共獲提升!
2293231150 + 1 + 1 用心討論,共獲提升!
DaveiH + 1 + 1 我很贊同!
獨行風云 + 1 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
神臨人間 + 1 歡迎分析討論交流,吾愛破解論壇有你更精彩!
七個漲停一倍 + 1 我很贊同!
Amosll + 1 熱心回復!
LGY龍淵 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
NSYGHIHI + 1 謝謝@Thanks!
養雞場廠長 + 1 + 1 用心討論,共獲提升!
殤丶憶年 + 1 &amp;lt;font style=&amp;quot;vertical-align: inherit;&amp;quot;&amp;gt;&amp;lt;font style=
二詩的Adidas + 1 + 1 謝謝@Thanks!
CHILAS_LEE + 1 我很贊同!
chenchen_82482 + 1 用心討論,共獲提升!
qws1855 + 1 + 1 用心討論,共獲提升!
uqu點xyz + 1 + 1 熱心回復!
lookerJ + 1 + 1 熱心回復!
CanYouSeeMe + 1 + 1 鼓勵轉貼優秀軟件安全工具和文檔!
submarine1620 + 1 + 1 用心討論,共獲提升!
dreamlivemeng + 1 + 1 鼓勵轉貼優秀軟件安全工具和文檔!
阿炯 + 1 + 1 我很贊同!
Hmily + 2 + 100 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
天上一朵云 + 1 + 1 用心討論,共獲提升!
LOLQAQ + 1 + 1 我很贊同!
Minesa + 1 + 1 用心討論,共獲提升!
生有涯知無涯 + 2 + 1 我很贊同!
181cm71kg24cm + 1 + 1 用心討論,共獲提升!
tangqixuang + 1 + 1 用心討論,共獲提升!
qa132465 + 1 用心討論,共獲提升!

查看全部評分

本帖被以下淘專輯推薦:

發帖前要善用論壇搜索功能,那里可能會有你要找的答案或者已經有人發布過相同內容了,請勿重復發帖。

推薦
主騎士 發表于 2020-7-14 09:38
現在難道不是AI外掛了么。。。我看到過AI外掛。真的就是AI自己玩游戲,還會罵人。。我GIAO
推薦
yxdyxd163 發表于 2020-7-13 15:46
推薦
caicaiwuguo 發表于 2020-7-13 15:00
推薦
刀大喵 發表于 2020-7-13 15:07
顯然我是看不懂的 還是感謝無私分享
推薦
Groza 發表于 2020-7-13 15:03
大神,膜拜來學習
推薦
綾音 發表于 2020-7-29 18:01
學習  學習大佬
8#
netspirit 發表于 2020-7-13 14:42
itruth大神 論壇里的x64bdg就是你發的 現在開始研究游戲了么~~~~~~~
9#
shghe 發表于 2020-7-13 14:52
大佬,你這個一出,天下無敵呀~~~~~
10#
duchang 發表于 2020-7-13 15:10
感謝大佬無私的分享
11#
z55 發表于 2020-7-13 15:14
好像有點厲害的樣子
12#
可樂渴了 發表于 2020-7-13 15:15
長知識了 謝謝樓主
您需要登錄后才可以回帖 登錄 | 注冊[Register]

本版積分規則 警告:本版塊禁止灌水或回復與主題無關內容,違者重罰!

快速回復 收藏帖子 返回列表 搜索

RSS訂閱|小黑屋|處罰記錄|聯系我們|吾愛破解 - LCG - LSG ( )

GMT+8, 2020-8-7 04:15

Powered by Discuz!

500彩票邀请码Copyright © 2001-2020, Tencent Cloud.

快速回復 返回頂部 返回列表

500彩票邀請碼-彩經網