首页
社区
课程
招聘
[原创] X大师逆向分析及利用
发表于: 1天前 211

[原创] X大师逆向分析及利用

1天前
211

一、  背景:

        众所周知,X大师的硬件检测功能很强,于是有了想分析下原理的想法。


二、   逆向:


         经过分析,X大师分析硬件的方法有些复杂,要还原其功能,然后自己重写,工作量很大。所以采用调用X大师二进制文件接口的方法,获取硬件信息。下面着重介绍分析思路:


1.      获得”硬件总览信息”:

        老规矩,先看X大师的日志文件,找到下面的信息:


       有关键字GetOverviewJson,下面的信息是CPU、主板、供应商,说明是”硬件总览信息”对应的日志信息。

       然后分析X大师的进程和文件,发现有下面几个进程:



       其中ComputerZ_CN.exe是界面进程,ComputerZService.exe是个服务进程:

      猜测ComputerZService.exe调用一些dll进行实际的硬件检测等功能,ComputerZ_CN.exe负责展示。

再看文件:

      看名字ComputerZ_HardwareDll.dll像是实现硬件检测功能的DLL,需重点分析。

      基于前面的日志文件,上IDA打开ComputerZ_HardwareDll.dll,搜索GetOverviewJson字符串:


找到引用的地方:

      可以看到Machine、OS、CPU、Motherboard等字符串。说明此处正是”硬件总览信息”对应的代码位置。

      首先想到的是直接构造参数调用此函数。但经过尝试,程序各种报错。想到是X大师在调用此函数前做了些初始化工作,而直接调用是没有经过初始化的。后面也分析出了好多初始化操作,补上后,程序还是报错。

       于是尝试调用X大师靠上层的一些函数接口,也就是一开始扫描硬件就会调用的函数。

看ComputerZ_HardwareDll.dll的导出函数:


       看名称,就一个CreateHdwInfo函数是创建硬件信息的,应该是扫描硬件前调用的。并没有扫描的函数。

       ComputerZ_HardwareDll.dll找不到,我们就去调用ComputerZ_HardwareDll.dll的exe,也就是ComputerZService.exe中去找。看它调用了          ComputerZ_HardwareDll.dll哪些函数。IDA中搜“ComputerZ_HardwareDll.dll”关键字:

      可以看到并未搜索到,但是这个字符串是存在的:

      看到是调用了dll的CreateHdwInfo和hardware_info_main函数。hardware_info_main函数在dll中如下:

      我们调用hardware_info_main时,第一个参数需要传this指针,至于this指针应该填充什么,也是在IDA中分析调用的上下文得出的。下图是分析出填充的this指针内容:

        ComputerZService.exe最后调用了:

在sub_34D680函数中调用了ComputerZ_HardwareDll.dll扫描函数。为什么是

(*(*v14 + 0x1C))(); 这里调用了扫描函数呢?答案是在ComputerZ_HardwareDll.dll的g_GetOverviewJson(识别出来后重命名的)处下断点,然后栈回溯到ComputerZService.exe的调用空间:

于是写代码依次调用这3个函数,并在g_GetOverviewJson中Hook一下,就能拿到"硬件总览信息”了:

拿到本机的信息如下:


2.  获得GPU使用率和温度:

       最简单、最直接的方法是使用Cheat Engine搜索数值,然后对数值下硬件写入断点,就能定位到代码位置。

因为GPU的使用率和温度数值是变化的:

用CE的First Scan、Next Scan可以搜索到变化的值。所以是最简单的方法:

搜索到后定位到下图的代码处:

于是Hook就可以拿到GPU的使用率和温度数据了。


3. 获得CPU使用率和温度:

 在CE搜索CPU使用率时,刚开始对搜索结果进行了排除,只留了一个数据。导致定位时没找到地方,只找到了ComputerZService.exe对数据的处理部分:

其实是3个数据:

依次查看这3条数据:在数据位置点击右键->find out what writes to this address.

第一条数据是在ComputerZService.exe中,排除。

第二条数据也是在ComputerZService.exe中,排除。

第三条数据是在ComputerZ_HardwareDll.dll中。于是IDA中定位到代码位置:

Hook之拿数据。

CPU温度的获取,由于数值变化较大,不好用CE精确搜索数值。首次精确搜索后,后续采用变化的数值进行搜索:

定位到如下代码位置:

HOOK之。


4.      获得CPU温度时的问题:

      测试自己写的拿X大师CPU温度时遇到个问题:先打开x大师,后运行自己的程序时,CPU温度拿不到。先打开自己运行的程序,X大师拿不到CPU温度。

      问题的原因,初步认为时程序独占代打开了CPU温度的资源。由于CPU温度的获取要用驱动程序实现,猜测是打开驱动时做了限制。

      从CPU温度赋值的地方,往上查找”v138”的交叉引用:

找到是v84赋值过去的

使用IDA的动态调试跟入(*(*v83 + 128))(v83)这个函数调用:

不打开x大师,运行自己的程序时,this+0x70和this+0x74是有值的,分别指向IntelCpuKernelTemperature和Ref_count_obj@VCIntelCpuKernelTemperature对象地址:

然而打开x大师,运行自己的程序时,this+0x70和this+0x74都是0。

对this+0x70和this+0x74下硬件写入断点,断到如下代码处:

然后sub_7BD28CB0往下翻:

      看到是调用了g_GetIntelCpuTemperatureFromDriver(我分析出来后重命名的)函数,然后判断返回值为0则把+0x70、0x74清零了,跟入GetIntelCpuTemperatureFromDriver函数:

调用了g_GetCpuTemperatureFromDriver函数,跟入:

调用了DeviceIoControl,发现x大师开启时开启自己的程序时,GetLastError()返回6:句柄无效。

这里可以看出是和驱动通信了,且句柄无效了。于是定位加载、打开驱动的地方:

       此处是打开驱动符号链接"\\\\.\\ComputerZ"的地方,打开的是ComputerZ_x64.sys驱动。经过IDA F5级别的动态调试发现成功CreateFileW后,会调用g_StopAndDeleteService函数给停止并删除驱动,然后又调用CreateFileW,造成打开失败。也就导致上面分析的DeviceIoControl失败,无效句柄。

       至于具体为什么要调用g_StopAndDeleteService函数,由于时间原因,我没有分析。

       于是自己的程序里屏蔽下StopAndDeleteService函数:Hook下g_StopAndDeleteService函数,入口处retn掉就行。

5.      有些电脑获得CPU温度失效的问题:

上面分析后,写成代码,然后删除无用的x大师文件,最后x大师的二进制文件只保留如下:

整个文件大小是12MB。

但测试时发现,有的电脑获取CPU温度会失败。原因是ComputerZ.set文件内容的如

下字段,若是上面的值,会失效。

改成

就好了。

 我们来定位原因,CE搜索131185

然后把搜到的数据地址在x32dbg中下硬件访问断点,对应到IDA中定位到如下代码处:

然后查找函数调用处:

接着sub_7B01F280函数往下翻:

看到上文的数字:268435456,若是这个数字则直接返回了,不走sub_7B01F280下面的Hook逻辑了:

所以把268435456改成131185就好了。


三、   代码实现:

     我写了个程序,实现了上述逆向的过程,获取硬件信息概览,以及实时获取CPU、GPU使用率和温度。代码如下:

// LuDaShiTest.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
#include <iostream>
#include <string.h>
using namespace std;

#include "utf82ascii.h"

#pragma warning(disable:4996)

//记住要以管理员权限运行,否则取不到CPU温度

DWORD g_JmpOverviewRetn = 0, g_JmpGpuUsageRetn = 0, g_JmpCpuUsageRetn = 0,
	g_JmpCpuTemperatureRetn = 0;

const WCHAR* g_pszGpuUsage = L"GPU %s Usage:%d temperature:%d\r\n";
const char* g_pszCpuUsage = "CPU Usage:%d\r\n";
const char* g_pszCpuTemperature = "CPU Temperature:%d\r\n";


void call_back_func()
{
	printf("call_back_func");
}

BOOL WriteToFile(char* pszData)
{
	string strData = pszData;
	//英文系统会有乱码
	string strAscii = UTF_82ASCII(strData);
	printf(strAscii.c_str());

	FILE* fp = fopen("c:\\HostHardware.json" , "w");
	if (fp)
	{
		fwrite(strAscii.c_str(), 1, strAscii.length(), fp);
		fclose(fp);
	}

	return TRUE;
}

__declspec(naked) void  HookOverviewStub()
{
	__asm
	{
		pushfd
		pushad

		push esi
		//call printf
		call WriteToFile
		add esp, 4 //naked必须手动平衡栈

		popad
		popfd

		//HOOK前原始指令
		mov edi, [ebp - 0x10C4]

		jmp g_JmpOverviewRetn
	}
}

__declspec(naked) void  HookGpuUsageStub()
{
	__asm
	{
		pushfd
		pushad

		//push ebx
		//push g_pszCoreGpuUsage
		//call printf
		////call WriteToFile
		//add esp, 8 //naked必须手动平衡栈

		mov ebx, [ebp - 0x54] //GPU Name
		cmp ebx, 0 //ebx有可能是0
		jz __exit

		push [ebp - 0xBC] //温度 -1表示温度不存在(核显不存在温度,X大师也是这样的)
		push [ebp - 0xC0] //GPU Usage
		
		push [ebx]
		push g_pszGpuUsage
		call wprintf
		//call WriteToFile
		add esp, 0x10 //naked必须手动平衡栈

__exit:
		popad
		popfd

		//HOOK前原始指令
		lea ecx, [ebp - 0xC4]

		jmp g_JmpGpuUsageRetn
	}
}

__declspec(naked) void  HookCpuUsageStub()
{
	__asm
	{
		pushfd
		pushad

		push eax
		push g_pszCpuUsage
		call printf
		//call WriteToFile
		add esp, 8 //naked必须手动平衡栈

		popad
		popfd

		//HOOK前原始指令
		mov     esi, [ebp + 0xC]
		test    esi, esi

		jmp g_JmpCpuUsageRetn
	}
}

__declspec(naked) void  HookCpuTemperatureStub()
{
	__asm
	{
		pushfd
		pushad

		push edi
		push g_pszCpuTemperature
		call printf
		//call WriteToFile
		add esp, 8 //naked必须手动平衡栈

		popad
		popfd

		//HOOK前原始指令
		lock xadd[edx + 0Ch], eax

		jmp g_JmpCpuTemperatureRetn
	}
}

int main(void)
{
	//typedef char(__stdcall*GetOverviewJson)(PVOID, __int64, int);
	typedef char(__stdcall* GetOverviewJson)(__int64, int);
	typedef char(__stdcall* GetOverviewJson1)(DWORD*,DWORD*);
	typedef DWORD*(__cdecl *CreateHdwInfo)();
	typedef int(*Init0)();
	typedef char* (*Init1)(unsigned int a1);
	DWORD oldflag=0,dwHook=0;
	//0xe2080
	HMODULE hComputerZ = LoadLibraryA("ComputerZ_HardwareDll.dll");
	if (hComputerZ)
	{
		HMODULE hKernel32 = LoadLibraryA("Kernel32.dll");


		//打开驱动符号链接成功后,会关闭删除服务,造成随后的打开、DeviceIoControl操作失败
		//Patch g_StopAndDeleteService 
		dwHook = (ULONG)hComputerZ + 0x1657b0;
		if (VirtualProtect((PVOID)dwHook, 5, PAGE_EXECUTE_READWRITE, &oldflag))
		{
			//改成retn
			*(BYTE*)dwHook = 0xC3;
		}

		//--------------------------------------------------------------------
		//Hook拿硬件Overview信息,Hook v13:
		//v11 = sub_7AC14BB0(COERCE__INT64((double)v21[1067]));
		//sub_7AC14840(v4, "memory_channel", v11);
		//sub_7AC14840((int)v3, "overview", v4);
		//v12 = sub_7AC14BB0(0x4042000000000000i64);
		//sub_7AC14840((int)v3, "type_id", v12);
		//v13 = (void*)sub_7AC15140(v3);               // 这里返回的是overview的json地址
		dwHook = (ULONG)hComputerZ + 0x9950c; //8B BD 3C EF FF FF   mov edi,[ebp+var_10C4]
		if (VirtualProtect((PVOID)dwHook, 6, PAGE_EXECUTE_READWRITE, &oldflag))
		{
			printf("VirtualProtect success,hook addr is:0x%x!\r\n", dwHook);
			*(BYTE*)dwHook = 0xe9;
			*(DWORD*)(dwHook + 1) = (DWORD)HookOverviewStub - dwHook - 5;
			*(char*)(dwHook + 5) = 0x90; //nop
			g_JmpOverviewRetn = dwHook + 6;
		}
		else
		{
			printf("memtype error %d\n", GetLastError());
		}

		//Hook拿各GPU使用率、温度:
		//v59 = sub_7B6B7890(v1 + 4, SubStr); // 这里返回值是CPU Usage, arg2是设备号
		//if (*(v78 + 164) != 1)
		//{
		//	v60 = sub_7B6B8070(SubStr);       // 温度
		//	v61 = sub_7B6B65C0(SubStr);       // GPU风扇
		//	sub_7B6BAAC0(&v58);
		//}
		//v16 = sub_7B6B2FD0();
		dwHook = (ULONG)hComputerZ + 0x26a1e5; //8D 8D 3C FF FF FF  lea ecx,[ebp+var_C4]
		if (VirtualProtect((PVOID)dwHook, 6, PAGE_EXECUTE_READWRITE, &oldflag))
		{
			printf("VirtualProtect success,hook addr is:0x%x!\r\n", dwHook);
			*(BYTE*)dwHook = 0xe9;
			*(DWORD*)(dwHook + 1) = (DWORD)HookGpuUsageStub - dwHook - 5;
			*(char*)(dwHook + 5) = 0x90; //nop
			g_JmpGpuUsageRetn = dwHook + 6;
		}
		else
		{
			printf("memtype error %d\n", GetLastError());
		}

		//
		//Hook拿各CPU使用率:
		//if (v38 > 100)
		//	v38 = 100;
		//result = 0;
		//if (v38 >= 0)
		//	result = v38;
		//v3[1] = result;  // 此处得到的CPU Usage 已验证是正确的
		dwHook = (ULONG)hComputerZ + 0x1a50ce; //8B 75 0C  mov esi,[ebp+arg_4]; 85 F6  test esi, esi
		if (VirtualProtect((PVOID)dwHook, 5, PAGE_EXECUTE_READWRITE, &oldflag))
		{
			printf("VirtualProtect success,hook addr is:0x%x!\r\n", dwHook);
			*(BYTE*)dwHook = 0xe9;
			*(DWORD*)(dwHook + 1) = (DWORD)HookCpuUsageStub - dwHook - 5;
			g_JmpCpuUsageRetn = dwHook + 5;
		}
		else
		{
			printf("memtype error %d\n", GetLastError());
		}

		//Hook拿各CPU温度:
		//LOBYTE(v186) = 10;
		//v143 = (v160 - 8);
		//v174[9] = v137;     // 这里写的CPU温度
		dwHook = (ULONG)hComputerZ + 0x311093; //F0 0F C1 42 0C  lock xadd [edx+0Ch], eax
		if (VirtualProtect((PVOID)dwHook, 5, PAGE_EXECUTE_READWRITE, &oldflag))
		{
			printf("VirtualProtect success,hook addr is:0x%x!\r\n", dwHook);
			*(BYTE*)dwHook = 0xe9;
			*(DWORD*)(dwHook + 1) = (DWORD)HookCpuTemperatureStub - dwHook - 5;
			g_JmpCpuTemperatureRetn = dwHook + 5;
		}
		else
		{
			printf("memtype error %d\n", GetLastError());
		}

		CreateHdwInfo createHdwInfo = (CreateHdwInfo)((ULONG)hComputerZ + 0x9bcb0);
		ULONG_PTR ulRetCreateHdwInfo = (ULONG_PTR)createHdwInfo();

		/*Init0 init0 = (Init0)((ULONG)hComputerZ + 0x2862f0);
		int nTest=init0();

		Init0 init2 = (Init0)((ULONG)hComputerZ + 0xe0a60);
		init2();

		Init0 init3 = (Init0)((ULONG)hComputerZ + 0x22def0);
		init3();

		Init0 init4 = (Init0)((ULONG)hComputerZ + 0x3f8b70);
		init4();*/
		
		try
		{
			char dwTmp[416] = {0};
			DWORD *dwThis = (DWORD*)&dwTmp;
			dwThis[2] = ulRetCreateHdwInfo;
			dwThis[3] = (ULONG)hComputerZ + 0x9bcb0; //CreateHdwInfo
			dwThis[4] = (ULONG)hComputerZ + 0x9c2e0; //ReleaseHdwInfo
			dwThis[5] = (ULONG)hComputerZ;
			dwThis[6] = (DWORD)&call_back_func;
			dwThis[6] = (DWORD)&dwThis[6];

			//hardware_info_main
			//(*(void(__thiscall**)(int, DWORD*))(*(DWORD*)ulRetCreateHdwInfo + 4))(ulRetCreateHdwInfo, dwThis);
			_asm
			{
				pushfd
				pushad

				mov ecx, ulRetCreateHdwInfo
				mov eax, [ecx]
				push dwThis
				call dword ptr[eax + 4]

				popad
				popfd
			}

			// 调用ComputerZ_HardwareDll.dll扫描函数
			//(*(int (**)(void))(*(DWORD*)ulRetCreateHdwInfo + 28))(&dwThis);
			//(*(int (__thiscall**)(DWORD*))(*(DWORD*)ulRetCreateHdwInfo + 28))(&dwThis);
			_asm
			{
				pushfd
				pushad

				mov ecx, ulRetCreateHdwInfo
				mov eax, [ecx]
				call dword ptr[eax + 0x1C]

				popad
				popfd
			}
		}
		catch (...)
		{
			
		}
		printf("请等待开始获取...\r\n");
		//system("pause");
		while (true)
		{
			Sleep(1000);
		}
	}
	else
		printf("找不到ComputerZ_HardwareDll.dll!");
}

实现的效果如下图:

好了,分析和利用暂时到此了。附件会里有代码和可利用二进制文件,逆向的idb文件太大,就不上传了



[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 1天前 被yirucandy编辑 ,原因:
上传的附件:
收藏
免费 1
支持
分享
最新回复 (3)
雪    币: 2528
活跃值: (2470)
能力值: (RANK:400 )
在线值:
发帖
回帖
粉丝
2
牛逼,学习一波!
1天前
0
雪    币: 3751
活跃值: (3407)
能力值: ( LV12,RANK:330 )
在线值:
发帖
回帖
粉丝
3
莫灰灰 牛逼,学习一波!
飞哥,好久不出来吹水了
1天前
0
雪    币: 4716
活跃值: (5076)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
厉害了,很详细。
3小时前
0
游客
登录 | 注册 方可回帖
返回