冰凌汇编

 找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 86|回复: 0
收起左侧

[.Net逆向] 简单逆向某平台驱动

[复制链接]
4442222977 发表于 2022-6-12 16:00:21
emmm  今天在玩某平台的时候,发现重启电脑后驱动会自动加载(并没有运行平台),我就发现这是一个很流氓的行为,因为你不知道他驱动在干些啥,于是决定简单逆向一下
用到的工具:IDA,PChunter,CE

首先定位驱动入口,将驱动dump出来,首先看一下入口
简单逆向某平台驱动 - 4442222977_冰凌汇编

说实话IDA这个默认配色还是看得不太舒服,上CE看吧,后边的图除了反编译的图我都用ce
简单逆向某平台驱动 - 4442222977_冰凌汇编

这个就舒服太多了,很明显可以看到入口被vm了

既然被vm了索性就不分析入口了

我们首先看看驱动干了啥吧   用ark工具也就是pchunter来看

创建了三个系统回调用来收集信息 分别是进程,线程,最后一个比较少见,跟图片有关,盲猜是截图或者是检测透视窗口的
简单逆向某平台驱动 - 4442222977_冰凌汇编

创建了一个obj钩子  一般都是用来保护游戏,防止被第三方读写
简单逆向某平台驱动 - 4442222977_冰凌汇编

我就只分析一个进程的吧,要具体分析太杂太累,只是简单带过几笔

首先上IDA
简单逆向某平台驱动 - 4442222977_冰凌汇编

这就是有关进程的回调,如果一句一句看,得累死

我们简单看看具体框架就行

我们很明显发现两条绿色的线,点进去F5看看
简单逆向某平台驱动 - 4442222977_冰凌汇编

很明显可以看到
if ( !KeGetCurrentIrql() )
  {
    if ( a3 )
    {

就是这两个判断

在内核中,KeGetCurrentIrql()是返回当前的IRQL

然后进行取反操作,也就是KeGetCurrentIrql()返回值为0时才往下执行。

再下来就是a3,a3为一个参数,这里应该是一个开关,而负责a3的应该是对战平台或者游戏,与驱动进行通讯修改a3的值

逆向思路:直接jmp,找到游戏或者平台通讯处,使其一直写入a3=0等等

一张一张图截图有点累,直接cv大法吧
if ( !KeGetCurrentIrql() )
  {
    if ( a3 )
    {
      Process = 0i64;
      ProcessHandle = 0i64;
      v11 = 0;
      P = 0i64;
      if ( PsLookupProcessByProcessId(a2, &Process) >= 0 )// 返回指向进程的EPROCESS结构的引用指针
      {
        if ( ObOpenObjectByPointer(Process, 0x200u, 0i64, 0, 0i64, 0, &ProcessHandle) >= 0 )// 返回对象的句柄
        {
          if ( ZwQueryInformationProcess(       // 收集进程信息
                 ProcessHandle,
                 ProcessBasicInformation,
                 ProcessInformation,
                 0x30u,
                 &ReturnLength) >= 0 )
          {
            _mm_lfence();

一直到这里下面
if ( (int)sub_FFFFF8028D19D748(a2, &P) >= 0 )

我们进入这个函数看看
__int64 __fastcall sub_FFFFF8028D19D748(void *a1, PVOID *a2)
{
  NTSTATUS v3; // ebx
  PVOID PoolWithTag; // rax
  PEPROCESS Process; // [rsp+40h] [rbp-18h] BYREF
  ULONG ProcessInformationLength; // [rsp+70h] [rbp+18h] BYREF
  HANDLE ProcessHandle; // [rsp+78h] [rbp+20h] BYREF

  v3 = PsLookupProcessByProcessId(a1, &Process);
  if ( v3 >= 0 )
  {
    v3 = ObOpenObjectByPointer(Process, 0, 0i64, 0, 0i64, 0, &ProcessHandle);
    if ( v3 >= 0 )
    {
      v3 = ZwQueryInformationProcess(ProcessHandle, ProcessImageFileName, 0i64, 0, &ProcessInformationLength);
      if ( v3 == -1073741820 )
      {
        PoolWithTag = ExAllocatePoolWithTag(NonPagedPool, ProcessInformationLength, 0x66667061u);
        *a2 = PoolWithTag;
        if ( PoolWithTag )
        {
          v3 = ZwQueryInformationProcess(
                 ProcessHandle,
                 ProcessImageFileName,
                 PoolWithTag,
                 ProcessInformationLength,
                 &ProcessInformationLength);
          if ( v3 < 0 )
          {
            ExFreePoolWithTag(*a2, 0x66667061u);
            *a2 = 0i64;
          }
        }
      }
      ZwClose(ProcessHandle);
    }
    ObfDereferenceObject(Process);
  }
  return (unsigned int)v3;
}

没什么好看的,主要是看一下
if ( v3 == -1073741820 )

v3是ZwQueryInformationProcess的返回值

在ZwQueryInformationProcess的返回值中-1073741820也就是  STATUS_INFO_LENGTH_MISMATCH  输入参数长度不匹配

也就是说这个函数是检测ZwQueryInformationProcess状态的

下边到分析完感觉也没有什么地方要单独拎出来将  直接cv分析结果把
void __fastcall sub_FFFFF8028D5266CE(HANDLE ProcessId, HANDLE a2, char a3)
{
  const char *ProcessImageFileName; // rsi
  const char *v6; // rax
  __int64 v7; // r15
  __int64 v8; // r12
  __int64 v9; // r9
  __int64 v10; // rcx
  char v11; // [rsp+20h] [rbp-F8h]
  PEPROCESS Process; // [rsp+28h] [rbp-F0h] BYREF
  PVOID P; // [rsp+30h] [rbp-E8h] BYREF
  HANDLE ProcessHandle; // [rsp+38h] [rbp-E0h] BYREF
  PEPROCESS v15; // [rsp+40h] [rbp-D8h] BYREF
  ULONG ReturnLength; // [rsp+48h] [rbp-D0h] BYREF
  char v17[16]; // [rsp+50h] [rbp-C8h] BYREF
  char ProcessInformation[8]; // [rsp+60h] [rbp-B8h] BYREF
  __int64 v19; // [rsp+68h] [rbp-B0h]
  struct _KAPC_STATE ApcState; // [rsp+90h] [rbp-88h] BYREF
  char Str2[16]; // [rsp+C0h] [rbp-58h] BYREF
  char v22[16]; // [rsp+D0h] [rbp-48h] BYREF

  if ( !KeGetCurrentIrql() )
  {
    if ( a3 )
    {
      Process = 0i64;
      ProcessHandle = 0i64;
      v11 = 0;
      P = 0i64;
      if ( PsLookupProcessByProcessId(a2, &Process) >= 0 )// 返回指向进程的EPROCESS结构的引用指针
      {
        if ( ObOpenObjectByPointer(Process, 0x200u, 0i64, 0, 0i64, 0, &ProcessHandle) >= 0 )// 返回对象的句柄
        {
          if ( ZwQueryInformationProcess(       // 收集进程信息
                 ProcessHandle,
                 ProcessBasicInformation,
                 ProcessInformation,
                 0x30u,
                 &ReturnLength) >= 0 )
          {
            _mm_lfence();
            if ( (int)sub_FFFFF8028D19D748(a2, &P) >= 0 )
            {
              ProcessImageFileName = 0i64;      // 定义文件名  路径  越看越有扫盘那味了  用const修饰 常量
              v15 = 0i64;
              if ( PsLookupProcessByProcessId(ProcessId, &v15) >= 0 )// 这里和上面都调用了PsLookupProcessByProcessId,但是参数不同,这里的processid为参数,上边为定制的变量
              {
                ProcessImageFileName = (const char *)PsGetProcessImageFileName(v15);// 获取路径
                ObfDereferenceObject(v15);      // 减少计数
              }
              if ( ProcessImageFileName )       // 如果获取的路径不为空
              {
                v6 = (const char *)PsGetProcessImageFileName(Process);// 再获取process变量的路径
                strcpy(Str2, "csgo.exe");       // 复制 为空则str2==csgo
                if ( !stricmp(v6, Str2) )       // v6为上边PsGetProcessImageFileName返回值,与csgo.exe做对比 取反,如果不是csgo继续往下执行
                {
                  Val = a2;
                  qword_FFFFF8028D3B1E38 = (__int64)Process;// 存放process
                  if ( (unsigned __int8)sub_FFFFF8028D19DC44((char *)Process + 40) )// MmIsAddressValid函数,确定地址是否可用
                    qword_FFFFF8028D3B1E40 = *((_QWORD *)Process + 5);// 存放process+5
                }
                KeStackAttachProcess(Process, &ApcState);// 附加到process读内存
                v11 = 1;
                v7 = v19;
                v8 = *(_QWORD *)(v19 + 16);
                strcpy(v22, "explorer.exe");    // 与上同理,v22=explorer.exe
                if ( !stricmp(ProcessImageFileName, v22) )// ProcessImageFileName不等于explorer.exe往下执行
                {
                  _mm_lfence();
                  LOBYTE(v9) = 1;
                  if ( (int)sub_FFFFF8028D196A64(a2, P, v8, v9, v17) >= 0 )// 这一段if就不分析了,东西太多了,简单看了下就是检测东西是不是非法,有问题,ZwOpenFile,ZwReadFile都有
                  {
                    v10 = *(_QWORD *)(v7 + 32) + 112i64;
                    qword_FFFFF8028D3B1E58 = (__int64)ProcessId;// 存放processid
                    if ( **(_WORD **)(v10 + 8) == 34 )
                    {
                      _mm_lfence();
                      sub_FFFFF8028D5224D5(a2, P, 4i64, v17, v10, 0i64);
                    }
                  }
                }
              }
            }
          }
        }
        else
        {
          ProcessHandle = 0i64;
        }
      }
      if ( P )
        ExFreePoolWithTag(P, 0x66667061u);      // 释放内存
      if ( v11 )
        KeUnstackDetachProcess(&ApcState);      // 解除附加
      if ( ProcessHandle )
        ZwClose(ProcessHandle);                 // 关闭句柄
      if ( Process )
        ObfDereferenceObject(Process);
    }
    else
    {
      if ( (HANDLE)qword_FFFFF8028D3B1E48 == a2 )// 用CE读了一下 FFFFF8028D3B1E48==4960
      {
        qword_FFFFF8028D3B1E48 = 0i64;
        qword_FFFFF8028D3B1E50 = 0i64;
        byte_FFFFF8028D3B1E84 = 0;
      }
      if ( Val == a2 )                          // if ( !stricmp(v6, Str2) )   
                                                //    {
                                                //      Val = a2;
      {
        Val = 0i64;
        qword_FFFFF8028D3B1E38 = 0i64;
        qword_FFFFF8028D3B1E40 = 0i64;
        byte_FFFFF8028D3B1BF2 = 0;
      }
    }
  }
}

其他几个地方就不分析了,知道了他的执行过程,逆向就很简单了,有很多可以过掉这个的方法

具体也不多说了,想分析的朋友可以自己去分析一下,这只是驱动层,他平台或者游戏本身也有检测的

那个比较简单,挂钩一些重要的函数即可。

最后祝大家身体健康,疫情期间勤带口罩少外出,也希望疫情早点结束
冰凌汇编免责声明
以上内容均来自网友转发或原创,如存在侵权请发送到站方邮件9003554@qq.com处理。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|站点统计|Archiver|小黑屋|RSS|冰凌汇编 ( 滇ICP备2022002049号 滇公网安备 53032102000029号)|网站地图

GMT+8, 2022-10-6 21:01 , Processed in 0.140894 second(s), 8 queries , Redis On.

冰凌汇编 - 建立于2021年12月20日

Powered by Discuz! © 2001-2022 Comsenz Inc.

快速回复 返回顶部 返回列表