2020年腾讯游戏安全大赛PC端决赛
0x0题目要求
winmine.exe是一个扫雷游戏程序,winmine.dmp是该程序的一份进程dump, 在这份dump中,有一个DLL作弊程序。
1, 请找到该作弊程序,给出模块名;(1分)
2, 并分析它所包含的4个作弊功能,给出实现作弊功能的函数的偏移,并说明其作弊功能是什么。(4分)
0x1 RING3 扫雷
dump拉进ida找到dll
修复段头
直接用工具查看 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)
找到判断函数,最后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;
}
0x5小题(3-4)
所有接口控制码为 0x800 0x801 0x802 0x803
第一个接口作用
其他三个接口作用 都是加密 可以百度里面的参数
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);
}
DriverObject 赋值在4308的位置
call rsi 就是执行我们的函数,然后获取函数下一个地址就是2011
服务名称
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地址 (括起来)+ ()调用这个函数
}
然后在虚拟机里运行驱动
这里我自己不是分析出来,而是跟着wp走 所以没分析。(一个不能运行的软件,完全不知道怎么办,看了wp都是运行出来然后得到,还有一个方法是编译成PE完全不会)
0x8 总结
话不多说,直接奉上q神博客,想复现建议看q神博客(http://www.qfrost.com/)(倒背如流每天挂着看博客(滑稽))