本帖最后由 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
觉得还可以麻烦给兄弟点个好评,写这么多文字截这么多图真的很累~~~
|