首页
社区
课程
招聘
逆向某一款上市读写驱动,什么居然不加壳?
发表于: 2024-1-14 07:26 12297

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

2024-1-14 07:26
12297

听说群里的兄弟租了个云端驱动,我来看看肿么肥事(帮群友鉴毒,作为群主义不容辞,绝不是因为听说该驱动吴迪稳定)
1
看样子很专业,打开例子康康
2
WTF???你不是AnyEveryDrv吗?怎么代码里面还有GSDrv,看样子是没删完 3,直接掏出神器IDA超度它(AnyEvery_x64.dll)
4
DllMain函数里面拿了很多函数地址,继续往下看
4
函数最后创建了一个线程,看看里面是个啥
4
呦西,这就是传说中的云端驱动,下载这个文件看看是个什么玩意
4
有签名,还是过期的证书,看样子是驱动文件了,102KB?看样子还是没壳,祭出IDA,先看驱动入口函数
4
填充了个结构(PoolWithTag),看看sub_1400021C0函数是什么
4
看样子是根据系统版本号拿一些系统结构偏移,盲猜是进程保护什么用的,继续看驱动入口函数,看看这个sub_140001C80函数
4
4
可以看到拿了两个ssdt函数的地址(NtCreateThreadEx、NtProtectVirtualMemory)
继续看驱动入口函数
4
这段代码大伙应该都知道是啥吧,从win10某个版本开始(哪个版本我也忘了),系统启动后ptebase不再固定而是一个随机值,继续往下看
4
什么,还有高手,进sub_140001920看看
4
豁然开朗,这个驱动其实就是一个映射器,unk_140004010才是真正的功能驱动文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
*(_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一探究竟,这个驱动肯定就没壳了,因为能映射带壳驱动的映射器基本上没有吧。
4
功能驱动入口函数很简洁,作者怎么还限制驱动使用时间呢?往下看可以直接看到创建了一个注册表回调用来通讯,进sub_14000BC40函数看一下
4
可以看到读取了之前写的AnyEvery.bin文件,拿到了它的数据,然后删除文件,我愿称之为好家伙。
伪代码可以看到回调函数入口是v4,v4是sub_140002900函数返回的,跟进去看看
4
一开始我也没看懂这是干嘛的,看了好久直到我看到了这句代码

1
if ( *(v10 - 3) == 2019914798 && *((_BYTE *)v10 - 8) == 116 )

经常写shellcode和pe有关代码的朋友应该可以看出来2019914798和116其实是字符串".text"在内存中的字节数组,大概就是这样

1
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偏移写入的是什么数据
4
调用两次ZwQuerySystemInformation并且调用11号功能,大伙应该都知道这个是干嘛的了吧,获取系统模块的起始地址,看看是获取什么模块的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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的值了,由于代码没截全直接放代码

1
2
3
4
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),跟过去

1
2
3
4
__int64 __fastcall sub_14000D810(__int64 (*a1)(void))
{
  return a1();
}

函数汇编:

1
2
.text:000000014000D810     sub_14000D810 proc near                
.text:000000014000D810     FF E1    jmp     rcx

到这里是不是已经明白了大概,函数sub_140002900其实是一个内存搜索函数,第一个参数是起始地址,第二个参数是特征码(0xFF,0xE1),第三个参数是掩码,第四个参数是函数的大小,正好对应上传入的参数,函数sub_14000D810其实是个跳板,如果把这个跳板当回调函数注册的话,函数内会跳转到函数的第一个参数,也就是rcx(学到老活到老),看回调函数原型:

1
2
3
4
5
6
NTSTATUS
EX_CALLBACK_FUNCTION (
    _In_ PVOID CallbackContext,
    _In_opt_ PVOID Argument1,
    _In_opt_ PVOID Argument2
    );

第一个参数又是Context,注册回调函数又把sub_140009220作为Context传入,也就是函数sub_140009220才是真正的回调函数

1
CmRegisterCallback(v4, sub_140009220, v3 + 7)

正片来了,直接看回调函数
4
开幕雷击,GET请求协议和ip已经出来了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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个字节看样子就是验证的开关了

1
2
3
4
5
6
if ( *(_DWORD *)qword_1400116A0 != 1 )
  {
    if ( v21 > 0x30303031 )
      return (unsigned int)-1073741823;
    return (unsigned int)VirtualMemory;
  }

最简单的破解方式就是找到qword_1400116A0,然后*(int*)qword_1400116A0 = 1
或者把这句代码改成

1
if ( *(_DWORD *)qword_1400116A0 != 0 )

怎么找qword_1400116A0?直接上inlinehook呗(拿到qword_1400116A0记得恢复钩子,趁PG没反应过来),hook ZwReadFile然后根据文件句柄来判断是不是AnyEvery.bin文件,是的话参数Buffer就是就qword_1400116A0指针了,你也可以选择hook CmRegisterCallback直接拿到Context,或许你还有不用hook的方法呢?
好了,看看它的驱动回调有什么功能
4
查询内存的属性
4
传说中的读写内存,这真的能吴迪稳定吗???
4
传说中的强写内存,附加到进程然后映射MDL,好家伙
4
传说中的强删文件
......
还有很多就不截图了。
驱动代码很简单,吴不吴迪稳不稳定不知道,不加壳就上市这操作实属逆天,驱动文件我就不上传了,也不是啥好东西,上面有链接自己下载,据说就是GS驱动加了个验证就上市了,话说作者不会起诉我吧qaq
觉得还可以麻烦给兄弟点个赞,写这么多文字截这么多图真的很累~~~


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

收藏
免费 15
支持
分享
最新回复 (17)
雪    币: 1532
活跃值: (4588)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
2
文件发发?
2024-1-14 17:29
0
雪    币: 452
活跃值: (626)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
逆向爱好者 文件发发?
https://anyeverydrv.oss-cn-beijing.aliyuncs.com/2_1.bin这是他oss的文件
2024-1-14 20:50
0
雪    币: 3070
活跃值: (30876)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
感谢分享
2024-1-14 23:09
1
雪    币: 1532
活跃值: (4588)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
5
秋狝 感谢分享
两个驱动文件你还有么
2024-1-14 23:14
0
雪    币: 2141
活跃值: (5173)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
6
 经典MDL,自删除,大手子们共享一套底层代码是吧
2024-1-15 09:17
0
雪    币: 565
活跃值: (582)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
这是20年底开源的驱动源码,分析浪费时间,需要我给你源代码
2024-1-21 04:32
0
雪    币: 320
活跃值: (40)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
苏苏苏苏苏苏 这是20年底开源的驱动源码,分析浪费时间,需要我给你源代码
大哥 发一下源代码
2024-1-30 09:46
0
雪    币: 452
活跃值: (626)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
苏苏苏苏苏苏 这是20年底开源的驱动源码,分析浪费时间,需要我给你源代码
你是GS驱动作者吗大佬
2024-1-30 15:28
0
雪    币: 82
活跃值: (165)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
苏苏苏苏苏苏 这是20年底开源的驱动源码,分析浪费时间,需要我给你源代码
大哥发一份学习一下
2024-2-29 10:30
0
雪    币: 12453
活跃值: (9422)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
逆向爱好者 两个驱动文件你还有么


当个雷锋。

这个文件
https://anyeverydrv.oss-cn-beijing.aliyuncs.com/2_4.bin  

不过不能下载了,后面得需要key。

上传的附件:
2024-2-29 13:02
0
雪    币: 3204
活跃值: (5429)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
加了壳子的驱动,你能搞吗?
2024-2-29 13:58
0
雪    币: 12848
活跃值: (9147)
能力值: ( LV9,RANK:280 )
在线值:
发帖
回帖
粉丝
13
woc,是马↓西↑若↑↓!
2024-2-29 14:29
0
雪    币: 261
活跃值: (547)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
14
验证用的代码比内存读写还多 
2024-3-8 13:08
0
雪    币: 0
活跃值: (241)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
chengqiyan 验证用的代码比内存读写还多 [em_14]
毕竟验证才是赚米的关键
2024-3-25 16:39
0
雪    币: 136
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
16
还不错看着
2024-4-4 15:26
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
17
苏苏苏苏苏苏 这是20年底开源的驱动源码,分析浪费时间,需要我给你源代码
大哥发一份学习一下
2024-4-11 14:18
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
18
好家伙..直接抄袭GS+明文验证,易语言的都这个德行
2024-4-15 17:47
0
游客
登录 | 注册 方可回帖
返回
//