硬件ID查询欺骗驱动程序样本逆向分析练习IDA使用

偶然从万能的某宝得到了一个HWID欺骗样本,看大小20KB,1(咦)5!(无壳),直接拖入IDA分析一下,看看如何实现的,仅为练习ida使用方法进行分析。

看一下驱动入口函数,一共四个调用,先看第一个sub_140001178();

如图所示,一共12个8字节地址赋值,都调用了一个sub_14000112C()函数,怎么看的这么眼熟?很明显,是为了规避对导入表(IAT)可能的检测点自己手动导入相关API地址,然后call(相关开源项目有个LAZY_IMPORT挺好的,使用十分方便),进sub_14000112C()看一下;

哦吼,经典组合,MmGetSystemRoutineAddress 例程返回指向 SystemRoutineName 指定的函数的指针。不过好像只对ssdt内的函数有效,SSSDT内函数需要附加到UI进程才行,否则蓝屏,好像跟会话空间有关系。

第一个函数的作用就是初始化API导入表了,来看第二个函数sub_1400016DC();

IDA直接给出伪代码,好长,慢慢分析。

第一段伪代码,定义了一个符号链接,看来是硬盘相关驱动,自己构造call了一个api,回顾上边发现是ObReferenceObjectByName,可以根据驱动设备名得到PDRIVER_OBJECT,进而得到该驱动的PDEVICE_OBJECT指针。v20是128位,还有个没见过的LODWORD,经查询,

LOWORD为取低4字节(16位)值
LOWORD(0XAABBCCDDEEFF)返回的结果就是0xEEFF,也正好是一个word值.

初步判定,是IDA错误认定了字符串变量,应该是定义了一个char[16]数组,看一下ObReferenceObjectByName用法

NTSTATUS
ObReferenceObjectByName (
    __in PUNICODE_STRING ObjectName,
    __in ULONG Attributes,
    __in_opt PACCESS_STATE AccessState,
    __in_opt ACCESS_MASK DesiredAccess,
    __in POBJECT_TYPE ObjectType,
    __in KPROCESSOR_MODE AccessMode,
    __inout_opt PVOID ParseContext,
    __out PVOID *Object
    );
//函数声明
PDRIVER_OBJECT  driverObject;

UNICODE_STRING DeviceName;
RtlInitUnicodeString(&DeviceName, L"\\Driver\\Disk");

 status = ObReferenceObjectByName(&DeviceName,//设备名
                                     OBJ_CASE_INSENSITIVE,
                                     NULL,                 // access state
                                     FILE_ALL_ACCESS,                    // 权限
                                     *IoDriverObjectType,
                                     KernelMode,//内核模式
                                     NULL,                 // parse context
                                     &driverObject);//获取的驱动对象指针

获取DISK.SYS的驱动对象后,调用了_InterlockedExchange64,赋了一个值,函数原型如下

LONG64 InterlockedExchange64(
  [in, out] LONG64 volatile *Target,//指向要交换的值的指针。 函数将此变量设置为 Value,并返回其先前值。
  [in]      LONG64          Value//要与 Target 指向的值交换的值。
);//函数声明,函数返回 Target 参数的初始值。

赋值的指针是sub_14000109C函数,跟进去看一下内容;

__int64 __fastcall sub_14000109C(__int64 a1, __int64 a2)
{
  __int64 v4; // rcx
  int v5; // eax
  __int64 (__fastcall *v6)(); // r8

  v4 = *(_QWORD *)(a2 + 184);
  v5 = *(_DWORD *)(v4 + 24);
  if ( v5 == 315436 )
  {
    v6 = sub_140001000;
    goto LABEL_8;
  }
  if ( v5 == 508040 )
  {
    v6 = sub_140001650;
    goto LABEL_8;
  }
  if ( v5 == 2954240 && !**(_DWORD **)(a2 + 24) )
  {
    v6 = sub_140001CEC;
LABEL_8:
    sub_140001DA8(v4, a2, v6);
  }
  return qword_140004FF8(a1, a2);
}

结合上文,V5=v24+224,此时v3=&sub_14000109C,而qword_140004FF8内存储的是原V3值,即V24+224,在sub_14000109C()中最后调用了V24+224,有点混乱,先往下看,看到了经典特征码扫描函数,还是带模式串的,看来不是暴力比对,就不进入分析了,qword_140002710内存储的是特征码0x48 0x89,明显是一个函数,这个函数应该是disk中与查询硬件id相关的函数,众所周知,特征码搜索函数一般为如下写法(直接给出自己用的一份源码,其实大家好像都一样)

DWORD64 find_pattern_mask(DWORD64 addr, DWORD32 size, const char* pattern, const char* mask)
{
	size -= (DWORD32)strlen(mask);

	for (DWORD32 i = 0; i < size; i++)
	{
		if (pattern_check((const char*)addr + i, pattern, mask))
			return addr + i;
	}

	return 0;
}

DWORD64 find_pattern(DWORD64 addr, const char* pattern, const char* mask)
{
	PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)addr;
	if (dos->e_magic != IMAGE_DOS_SIGNATURE) return 0;

	PIMAGE_NT_HEADERS64 nt = (PIMAGE_NT_HEADERS64)(addr + dos->e_lfanew);
	if (nt->Signature != IMAGE_NT_SIGNATURE) return 0;

	PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(nt);

	for (unsigned short i = 0; i < nt->FileHeader.NumberOfSections; i++)
	{
		PIMAGE_SECTION_HEADER p = &section[i];

		if (strstr((const char*)p->Name, ".text") || 'EGAP' == *reinterpret_cast<int*>(p->Name))
		{
			DWORD64 res = find_pattern_mask(addr + p->VirtualAddress, p->Misc.VirtualSize, pattern, mask);
			if (res) return res;
		}
	}

	return 0;
}

参数 *(_QWORD *)(v6 + 24)确定为搜索开始的地址,再向上一行,V6=V24,v24是一个指针地址,结构体指针,还不能确定是哪个结构体,因为ObReferenceObjectByName调用的参数明显不对,IDA中还出现了UNK字段,F5也不是那么好用啊,先向下看qword_140007050对应的函数应该是IoEnumerateDeviceObjectList,调用例子

status = IoEnumerateDeviceObjectList(driver_object, device_object_list, sizeof(device_object_list), &number_of_device_objects);
if (!NT_SUCCESS(status))
{
	ObDereferenceObject(driver_object);
	return false;
}

V24结构体应该就是PDRIVER_OBJECT了,定义如下

typedef struct _DRIVER_OBJECT {
    CSHORT Type;
    CSHORT Size;
    PDEVICE_OBJECT DeviceObject;
    ULONG Flags;
    PVOID DriverStart;
    ULONG DriverSize;
    PVOID DriverSection;
    PDRIVER_EXTENSION DriverExtension;
    PUNICODE_STRING HardwareDatabase;
    PFAST_IO_DISPATCH FastIoDispatch;
    PDRIVER_INITIALIZE DriverInit;
    PDRIVER_STARTIO DriverStartIo;
    PDRIVER_UNLOAD DriverUnload;
    PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];

} DRIVER_OBJECT;
typedef struct _DRIVER_OBJECT *PDRIVER_OBJECT; 

2+2+8+4+8=24,V24+24对应的结构体成员即为DriverStart,是disk驱动对象的地址。继续向下看,qword_1400070A0对应的函数为ExAllocatePoolWithTag,分配了一块内存,结合IoEnumerateDeviceObjectList的参数,分配的内存是给OBJECTLIST[]准备的,到此,大致代码还原如下

PDEVICE_OBJECT Device_Object_List[100]{ 0 };
RtlZeroMemory(Device_Object_List, sizeof(Device_Object_List));

ULONG objects_number = 0;
status = IoEnumerateDeviceObjectList(Driver_Object, Device_Object_List, sizeof(Device_Object_List), &objects_number);

接下来的FOR循环就是遍历OBJ链,

 if ( v9 )
          {
            if ( (int)IoEnumerateDeviceObjectList(v24, v9, v8, &v23) >= 0 )
            {
              if ( v23 )
              {
                for ( i = 0i64; (unsigned int)i < v23; i = (unsigned int)(i + 1) )
                {
                  v12 = *(_QWORD *)(v10 + 8 * i);
                  v13 =IoGetAttachedDeviceReference(v12);
                  if ( v13 )
                  {
                    v22 = 0i64;
                    v21 = 0i64;
                    KeInitializeEvent(&v21, 0i64, 0i64);
                    LOBYTE(v19) = 0;
                    v14 = IoBuildDeviceIoControlRequest(459072i64, v13, 0i64, 0i64, 0i64, 0, v19, &v21, 0i64);
                    if ( v14 && (unsigned int)IofCallDriver(v13, v14) == 259 )
                      KeWaitForSingleObject(&v21, 0i64, 0i64, 0i64, 0i64);
                    ObfDereferenceObject(v13);
                  }
                  v15 = *(_QWORD *)(v12 + 64);
                  if ( v15 )
                  {
                    v16 = (char *)&unk_140005028;
                    v17 = (_BYTE *)(*(_QWORD *)(v15 + 520) + *(unsigned int *)(*(_QWORD *)(v15 + 520) + 24i64));
                    do
                    {
                      v18 = *v16++;
                      *v17++ = v18;
                    }
                    while ( v18 );
                    v7(v15, 0i64, v16);
                  }
                  ObfDereferenceObject(v12);
                }
              }
            }
            qword_140007080(v10, 0i64);
          }

IoBuildDeviceIoControlRequest 例程为同步处理的设备控制请求分配和设置 IRP。IofCallDriver将 IRP 发送到与指定设备对象关联的驱动程序。v7为我们要call的用特征码从disk驱动中搜索到的函数。经过查找资料,DiskEnableDisableFailurePrediction是符合我们要求的,作用为关闭硬盘的S.M.A.R.T功能。

分析到这里可以确定这个驱动为开源的写法,通过关闭smart,hookirp函数达到hwid欺骗的目的,再查询过程中直接找到了个项目的开源地址,哈哈,不过我是为了熟悉ida操作来分析的,无所谓咯。某宝上真的是奸商拿垃圾来骗人哈哈哈。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值