2020TencentPC端决赛


2020年腾讯游戏安全大赛PC端决赛

0x0题目要求

winmine.exe是一个扫雷游戏程序,winmine.dmp是该程序的一份进程dump, 在这份dump中,有一个DLL作弊程序。

1, 请找到该作弊程序,给出模块名;(1分)

2, 并分析它所包含的4个作弊功能,给出实现作弊功能的函数的偏移,并说明其作弊功能是什么。(4分)

0x1 RING3 扫雷

2

dump拉进ida找到dll

1

修复段头

3

直接用工具查看 4个功能地址和作用直接就告诉我们了

0x2 RING0 要求

(1) 成功加载Driver.drv至驱动模块链表(0.5分)

(2) 自编写驱动加载程序,使用非服务方式加载Driver.drv驱动,加载后需要保证驱动路径为C:\Driver.drv(0.5分)

(3) 分析驱动接口,给出每个功能的调用控制码,输入,输出数据的结构(0.5分)

(4) 分析驱动接口,分析每个接口的作用(0.5分)

(5) 调用驱动接口,使Driver.drv在驱动模块中断链隐藏(只允许在应用层调用Driver.drv接口实现)(2分)

(6) 从Flag.fg中分析出Flag,此flag用于解密Part2.7z(1分)

0x3 小题(1)

13

找到判断函数,最后patch 为0

0x4 小题(2)

#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;               //start 3 表示手动开启
    DWORD type = 1;                   //type 1 表示 驱动
    DWORD errorControl = 1;
    UNICODE_STRING Name;

    HMODULE hNtdll = LoadLibraryW(L"ntdll.dll");   //引入ntdll

    RtlInitUnicodeString = (RTLINITUNICODESTRING)GetProcAddress(hNtdll, "RtlInitUnicodeString");  //获得dll里的函数
    ZwLoadDriver = (ZWLOADDRIVER)GetProcAddress(hNtdll, "ZwLoadDriver");   //定义函数

    RegDeleteKeyA(HKEY_LOCAL_MACHINE, szBuf);                  //先删除原来的key
    RegCreateKeyA(HKEY_LOCAL_MACHINE, szBuf, &hk);              //创建key
    RegSetValueExA(hk, "ImagePath", 0, REG_EXPAND_SZ, (LPBYTE)path, sizeof(path));  //地址
    RegSetValueExA(hk, "Start", 0, REG_DWORD, (LPBYTE)&start, sizeof(DWORD));   //start
    RegSetValueExA(hk, "Type", 0, REG_DWORD, (LPBYTE)&type, sizeof(DWORD));  //type
    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;
}

15

0x5小题(3-4)

5

所有接口控制码为 0x800 0x801 0x802 0x803

7

8

第一个接口作用

6

9

其他三个接口作用 都是加密 可以百度里面的参数

0x6 小题(5)断链

#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 _KLDR_DATA_TABLE_ENTRY   //定义DRIVER_SECTION 结构体
{
    LIST_ENTRY InLoadOrderLinks;
    PVOID ExceptionTable;
    ULONG ExceptionTableSize;
    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;

PCHAR pRetAddr;
PCHAR pDriverBase;
PCHAR* ppDriverObject;
PCHAR pDriverObject;


DWORD fun()
{
    pRetAddr = _ReturnAddress();  //返回函数执行的下一个地址  后面会放图片 下面的-0x2011 和 加0x4308 寻找 Driver Object是为什么
    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;   //把下一个的flink 和 blink指针提出来
    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,    //传入接口和要执行的函数  这里0x800<< 是因为 前面的算法
        &Input, (DWORD)sizeof(Input), &Output, (DWORD)sizeof(Output), &bytesReturned, NULL);

    if (!bRc)   //判断成功
    {
        printf("Error in DeviceIoControl : %d", GetLastError());
        return;
    }

    CloseHandle(hDevice);
}

10

DriverObject 赋值在4308的位置

11

call rsi 就是执行我们的函数,然后获取函数下一个地址就是2011

12

服务名称

0x7 小题(6)

#include <iostream>
#include <Windows.h>

int main()
{
    HANDLE hFile = CreateFileW(L"C:\\Flag.fg", FILE_GENERIC_READ | FILE_GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);//打开或截断一个文件,并返回一个能够被用来存取该文件的句柄
    auto size = 0;
    BYTE* buff = nullptr;  //定义空指针

    if (hFile != INVALID_HANDLE_VALUE) {
        size = GetFileSize(hFile, NULL);  //获取文件大小
        buff = (BYTE*)VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);//申请内存
        DWORD bytes = 0;
        ReadFile(hFile, buff, size, &bytes, NULL);//读取文件
        CloseHandle(hFile); //关闭句柄
    }
    else {
        printf("CreateFileW Error: %X\n", GetLastError());
        exit(0);
    }
    auto result = ((void* (*)())(buff))();//(void*(*)()) 强转函数定义  buff 数组做shellcode地址  (括起来)+ ()调用这个函数
}

然后在虚拟机里运行驱动

1

这里我自己不是分析出来,而是跟着wp走 所以没分析。(一个不能运行的软件,完全不知道怎么办,看了wp都是运行出来然后得到,还有一个方法是编译成PE完全不会)

0x8 总结

话不多说,直接奉上q神博客,想复现建议看q神博客(http://www.qfrost.com/)(倒背如流每天挂着看博客(滑稽))


文章作者: Blue
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Blue !
评论
  目录