开启辅助访问 切换到宽版

精易论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

用微信号发送消息登录论坛

新人指南 邀请好友注册 - 我关注人的新帖 教你赚取精币 - 每日签到


求职/招聘- 论坛接单- 开发者大厅

论坛版规 总版规 - 建议/投诉 - 应聘版主 - 精华帖总集 积分说明 - 禁言标准 - 有奖举报

查看: 10530|回复: 45
收起左侧

[Windows逆向] [驱动逆向] 逆向某一款上市读写驱动,什么居然不加壳?

[复制链接]
发表于 2024-1-14 11:50:43 | 显示全部楼层 |阅读模式   江苏省南京市
本帖最后由 Superqaq 于 2024-1-14 11:49 编辑

听说群里的兄弟租了个云端驱动,我来看看肿么肥事(帮群友鉴毒,作为群主义不容辞,绝不是因为听说该驱动吴迪稳定)

看样子很专业,打开例子康康

WTF???你不是AnyEveryDrv吗?怎么代码里面还有GSDrv,看样子是没删完,直接掏出神器IDA超度它(AnyEvery_x64.dll)

DllMain函数里面拿了很多函数地址,继续往下看

函数最后创建了一个线程,看看里面是个啥

呦西,这就是传说中的云端驱动,下载这个文件看看是个什么玩意

有签名,还是过期的证书,看样子是驱动文件了,102KB?看样子还是没壳,祭出IDA,先看驱动入口函数

填充了个结构(PoolWithTag),看看sub_1400021C0函数是什么

看样子是根据系统版本号拿一些系统结构偏移,盲猜是进程保护什么用的,继续看驱动入口函数,看看这个sub_140001C80函数


可以看到拿了两个ssdt函数的地址(NtCreateThreadEx、NtProtectVirtualMemory)
继续看驱动入口函数

这段代码大伙应该都知道是啥吧,从win10某个版本开始(哪个版本我也忘了),系统启动后ptebase不再固定而是一个随机值,继续往下看

什么,还有高手,进sub_140001920看看

豁然开朗,这个驱动其实就是一个映射器,unk_140004010才是真正的功能驱动文件
[C++] 纯文本查看 复制代码
*(_QWORD *)(qword_140017950 + 48) = ExAllocatePoolWithTag(NonPagedPool, *(unsigned int *)(v3 + 80), 0x5347u);
  v5 = *(_QWORD *)(qword_140017950 + 48);
  if ( !v5 )
    return -1073741823;
  v6 = *(unsigned int *)(v4 + 80);
  if ( *(_DWORD *)(v4 + 80) )
    sub_140002C00(v5, 0i64, v6);
  v7 = *(unsigned int *)(v4 + 84);
  v8 = *(_BYTE **)(qword_140017950 + 48);
  if ( *(_DWORD *)(v4 + 84) )
  {
    v6 = &unk_140004010 - (_UNKNOWN *)v8;
    do
    {
      *v8 = v8[v6];
      ++v8;
      --v7;
    }
    while ( v7 );
  }
  v9 = (char *)&unk_140004010 + dword_14000404C;
  for ( i = 0i64; (unsigned int)i < *(unsigned __int16 *)(v4 + 6); i = (unsigned int)(i + 1) )
  {
    v7 = *(unsigned int *)&v9[40 * (unsigned int)i + 280];
    v6 = (signed __int64)&unk_140004010 + *(unsigned int *)&v9[40 * (unsigned int)i + 284];
    v11 = *(_QWORD *)(qword_140017950 + 48) + *(unsigned int *)&v9[40 * (unsigned int)i + 276];
    if ( *(_DWORD *)&v9[40 * (unsigned int)i + 280] )
    {
      v6 -= v11;
      do
      {
        *(_BYTE *)v11 = *(_BYTE *)(v6 + v11);
        ++v11;
        --v7;
      }
      while ( v7 );
    }
  }
  if ( (int)sub_140001300(*(_QWORD *)(qword_140017950 + 48), v7, v6, i) < 0
    || (int)sub_1400015A0(*(_QWORD *)(qword_140017950 + 48)) < 0 )
  {
    return -1073741823;
  }
  FileHandle = 0i64;
  RtlInitUnicodeString(&v16, L"\\SystemRoot\\System32\\AnyEvery.bin");
  ObjectAttributes.Length = 48;
  ObjectAttributes.RootDirectory = 0i64;
  ObjectAttributes.Attributes = 576;
  ObjectAttributes.ObjectName = &v16;
  *(_OWORD *)&ObjectAttributes.SecurityDescriptor = 0i64;
  v12 = ZwCreateFile(&FileHandle, 0x10000000u, &ObjectAttributes, &IoStatusBlock, 0i64, 0x80u, 3u, 3u, 0x20u, 0i64, 0);
  if ( v12 >= 0 )
  {
    ByteOffset.QuadPart = 0i64;
    v12 = ZwWriteFile(FileHandle, 0i64, 0i64, 0i64, &IoStatusBlock, &qword_140017950, 8u, &ByteOffset, 0i64);
    ZwClose(FileHandle);
  }
  if ( v12 < 0 )
    return v12;
  v13 = *(unsigned int *)(v4 + 40);
  ByteOffset.QuadPart = 0i64;
  result = PsCreateSystemThread(
             (PHANDLE)&ByteOffset,
             0x1FFFFFu,
             0i64,
             (HANDLE)0xFFFFFFFFFFFFFFFFi64,
             0i64,
             (PKSTART_ROUTINE)(*(_QWORD *)(qword_140017950 + 48) + v13),
             0i64);

这段代码就是根据PE头写入每个节然后修复IAT表,映射功能驱动,把之前根据系统版本拿到的一系列偏移还有ssdt函数地址什么的写到一个文件里面(AnyEvery.bin),然后启动一条线程执行驱动入口(为什么不直接call呢???还有把这些数据写到本地也是神操作~)。拷贝unk_140004010把这个功能驱动文件写出来,拖到IDA一探究竟,这个驱动肯定就没壳了,因为能映射带壳驱动的映射器基本上没有吧。

功能驱动入口函数很简洁,作者怎么还限制驱动使用时间呢?往下看可以直接看到创建了一个注册表回调用来通讯,进sub_14000BC40函数看一下

可以看到读取了之前写的AnyEvery.bin文件,拿到了它的数据,然后删除文件,我愿称之为好家伙。
伪代码可以看到回调函数入口是v4,v4是sub_140002900函数返回的,跟进去看看

一开始我也没看懂这是干嘛的,看了好久直到我看到了这句代码
[C++] 纯文本查看 复制代码
if ( *(v10 - 3) == 2019914798 && *((_BYTE *)v10 - 8) == 116 )

经常写shellcode和pe有关代码的朋友应该可以看出来2019914798和116其实是字符串".text"在内存中的字节数组,大概就是这样
[C++] 纯文本查看 复制代码
unsigned char str[] = { 0x2E, 0x74, 0x65, 0x78, 0x74, 0x00 };

前4个字节是".tex",第5个字节是"t",这样子的话就可以猜测这个函数是查找代码段的某一个地址,第一个参数*(_QWORD *)(qword_1400116A0 + 40)应该是起始地址(我猜的),验证一下猜想,看看这个qword_1400116A0 + 40里面到底是什么,还记得qword_1400116A0是保存文件AnyEvery.bin内容的指针吗,回到上个驱动文件,找到往这个文件+0x40偏移写入的是什么数据

调用两次ZwQuerySystemInformation并且调用11号功能,大伙应该都知道这个是干嘛的了吧,获取系统模块的起始地址,看看是获取什么模块的
[C++] 纯文本查看 复制代码
if ( (unsigned __int64)SystemRoutineAddress >= v16 && (unsigned __int64)SystemRoutineAddress < v16 + v12[v15 + 8] )
      {
        v17 = (_QWORD *)*a1;
        if ( (_QWORD *)*a1 != a1 )
        {
          while ( 1 )
          {
            v18 = v17[6];
            if ( v16 == v18 )
            {
              v19 = v17[1];
              if ( v19 >= v18 && v19 < v18 + *((unsigned int *)v17 + 16) )
                break;
            }
            v17 = (_QWORD *)*v17;
            if ( v17 == a1 )
              goto LABEL_24;
          }
          *(_QWORD *)(qword_140017950 + 40) = v16;
          *(_QWORD *)(qword_140017950 + 56) = v17[1];
        }
      }

以看到SystemRoutineAddress所在的模块就是qword_140017950 + 0x40的值了,由于代码没截全直接放代码
[C++] 纯文本查看 复制代码
RtlInitUnicodeString(&DestinationString, L"NtOpenFile");
  SystemRoutineAddress = MmGetSystemRoutineAddress(&DestinationString);
  if ( !SystemRoutineAddress )
    return 3221225473i64;

NtOpenFile函数是在ntoskrnl.exe里面的至此为止我们终于知道了模块是什么了。再回到第二个驱动中,qword_1400116A0 + 40存的是ntoskrnl.exe在内存中 的起始地址,函数sub_140002900是想在ntoskrnl.exe的代码段找什么,看第二个参数,是个函数指针(sub_14000D810),跟过去
[C++] 纯文本查看 复制代码
__int64 __fastcall sub_14000D810(__int64 (*a1)(void))
{
  return a1();
}

函数汇编:
[Asm] 纯文本查看 复制代码
.text:000000014000D810     sub_14000D810 proc near                
.text:000000014000D810     FF E1    jmp     rcx

到这里是不是已经明白了大概,函数sub_140002900其实是一个内存搜索函数,第一个参数是起始地址,第二个参数是特征码(0xFF,0xE1),第三个参数是掩码,第四个参数是函数的大小,正好对应上传入的参数,函数sub_14000D810其实是个跳板,如果把这个跳板当回调函数注册的话,函数内会跳转到函数的第一个参数,也就是rcx(学到老活到老),看回调函数原型:
[C++] 纯文本查看 复制代码
NTSTATUS
EX_CALLBACK_FUNCTION (
    _In_ PVOID CallbackContext,
    _In_opt_ PVOID Argument1,
    _In_opt_ PVOID Argument2
    );

第一个参数又是Context,注册回调函数又把sub_140009220作为Context传入,也就是函数sub_140009220才是真正的回调函数
[C++] 纯文本查看 复制代码
CmRegisterCallback(v4, sub_140009220, v3 + 7)

正片来了,直接看回调函数

开幕雷击,GET请求协议和ip已经出来了
[C++] 纯文本查看 复制代码
if ( (int)sub_140001040(v14, v13, v15, v16, ObjectType, v11, v17) >= 0 )
          {
            v18 = 0;
            v19 = -v12;
            while ( 2 )
            {
              v20 = v12;
              while ( aXxxxxxx[v19 + v20] != 120 || *(_BYTE *)v20 == aSuccess[v19 + v20] )
              {
                if ( (unsigned __int64)(++v20 - v12) >= 7 )
                {
                  if ( v12 )
                  {
                    VirtualMemory = 0;
                    *(_DWORD *)qword_1400116A0 = 1;
                  }
                  goto LABEL_25;
                }
              }
              ++v18;
              ++v12;
              --v19;
              if ( v18 < 0xFF9 )
                continue;
              break;
            }
          }

这验证也太草率了,qword_1400116A0指针前4个字节看样子就是验证的开关了
[C++] 纯文本查看 复制代码
if ( *(_DWORD *)qword_1400116A0 != 1 )
  {
    if ( v21 > 0x30303031 )
      return (unsigned int)-1073741823;
    return (unsigned int)VirtualMemory;
  }

最简单的破J方式就是找到qword_1400116A0,然后*(int*)qword_1400116A0 = 1
或者把这句代码改成
[C++] 纯文本查看 复制代码
if ( *(_DWORD *)qword_1400116A0 != 0 )

怎么找qword_1400116A0?直接上inlinehook呗(拿到qword_1400116A0记得恢复钩子,趁PG没反应过来),hook ZwReadFile然后根据文件句柄来判断是不是AnyEvery.bin文件,是的话参数Buffer就是就qword_1400116A0指针了,你也可以选择hook CmRegisterCallback直接拿到Context,或许你还有不用hook的方法呢?
好了,看看它的驱动回调有什么功能

cha询内存的属性

传说中的读写内存,这真的能吴迪稳定吗???

传说中的强写内存,附加到进程然后映射MDL,好家伙

传说中的强删文件
......
还有很多就不截图了。
驱动代码很简单,吴不吴迪稳不稳定不知道,不加壳就上市这操作实属逆天,驱动文件我就不上传了,也不是啥好东西,上面有链接自己下载,据说就是GS驱动加了个验证就上市了,话说作者不会起诉我吧qaq
觉得还可以麻烦给兄弟点个好评,写这么多文字截这么多图真的很累~~~

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

评分

参与人数 9好评 +7 精币 +57 收起 理由
Van42 + 40 私自转载就是不尊重原作者
胡逸之 + 1 + 2 虽然看不懂,但是很NB的样子!顶起
Me默 + 1 + 2 YYDS~!
aoty1018 + 1 感谢分享,很给力!~
a524666979 + 1 + 2 支持开源~!感谢分享
Bszk + 1 + 3 不吹nb怎么卖[狗头]
猪滴寳貝哝 + 1 + 2 此贴非常给力,技术含量高!
高先生 + 1 + 2 感谢分享,很给力!~
咸鱼666 + 1 + 3 此处应该有鼓励~

查看全部评分

本帖被以下淘专辑推荐:

签到天数: 2 天

 楼主| 发表于 2024-10-20 13:24:34 | 显示全部楼层   江苏省*
Van42 发表于 2024-10-18 22:16
多久之前的帖子了,现在还有人转载?

谁转载                                             
回复 支持 反对

使用道具 举报

签到天数: 2 天

发表于 2024-10-18 22:16:32 | 显示全部楼层   广东省深圳市
多久之前的帖子了,现在还有人转载?
回复 支持 反对

使用道具 举报

签到天数: 8 天

发表于 2024-10-1 21:15:17 | 显示全部楼层   福建省泉州市
888888888888888888888888888888888888888888888
回复 支持 反对

使用道具 举报

签到天数: 3 天

发表于 2024-9-21 01:09:25 | 显示全部楼层   江苏省徐州市
拿去用了 感谢楼主。
回复 支持 反对

使用道具 举报

结帖率:83% (5/6)

签到天数: 3 天

发表于 2024-9-7 22:32:06 | 显示全部楼层   福建省莆田市
我虽然看不懂,但是很NB的样子!顶起
回复 支持 反对

使用道具 举报

结帖率:50% (2/4)

签到天数: 11 天

发表于 2024-8-14 17:06:16 | 显示全部楼层   湖南省娄底市
我虽然看不懂,但是很NB的样子!顶起
回复 支持 反对

使用道具 举报

发表于 2024-8-14 01:46:13 | 显示全部楼层   江西省抚州市

我虽然看不懂,但是很NB的样子!
回复 支持 反对

使用道具 举报

签到天数: 9 天

发表于 2024-8-12 08:59:40 | 显示全部楼层   江西省吉安市
看看什么情况
回复 支持 反对

使用道具 举报

发表于 2024-8-5 16:10:37 | 显示全部楼层   广东省中山市
666666666666666666666666666666666
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 致发广告者

发布主题 收藏帖子 返回列表

sitemap| 易语言源码| 易语言教程| 易语言论坛| 易语言模块| 手机版| 广告投放| 精易论坛
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表精易立场!
论坛帖子内容仅用于技术交流学习和研究的目的,严禁用于非法目的,否则造成一切后果自负!如帖子内容侵害到你的权益,请联系我们!
防范网络诈骗,远离网络犯罪 违法和不良信息举报电话0663-3422125,QQ: 793400750,邮箱:wp@125.la
Powered by Discuz! X3.4 揭阳市揭东区精易科技有限公司 ( 粤ICP备12094385号-1) 粤公网安备 44522102000125 增值电信业务经营许可证 粤B2-20192173

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