逆向TesSafe.sys

来源:互联网 发布:今古传奇故事月末 知乎 编辑:程序博客网 时间:2024/06/06 08:59
//------------------------------[IDA] => TesSafe.sys --------------------------------------//
// File MD5: E780032179272AFD93BCF4A9A67C7706
// Version: 0.0.6.5
//-----------------------------------------------------------------------------------------//
// 定义

extern "C"
{
   #include <ntddk.h>
}

DWORD dword_14288 = 0xBB40E64E;    // 这个变量有初值
DWORD dword_142C4 = 0;
DWORD dword_14050 = 5;
DWORD dword_1431C = 0; // 这个四字节变量保存一个指向PsGetProcessImageFileName的指针
DWORD dword_14338 = 0;
PKEVENT unk_143C0 = NULL;
KSPIN_LOCK SpinLock = 0; // 实际上是DWORD
PVOID unk_14328 = NULL;
PVOID unk_142B4 = NULL;
PVOID unk_143E0 = NULL;
PVOID BaseAddress = NULL;

// 其实内核函数默认调用方法就是stdcall,下面的_stdcall都是不用加的。
// 不过为了说明,还是都加上了。

VOID _stdcall proc_1121F();    // 防止动态调试
VOID _stdcall sub_11000(PVOID, DWORD, DWORD);
VOID _stdcall sub_118D0();
VOID _stdcall sub_1230C();    // 实现挂钩

// 下面的函数就是int __stdcall sub_112DA(HANDLE ProcessHandle, int, int, int, int)
NTSTATUS _stdcall Hook_ObOpenObjectByPointer(PVOID Object,ULONG HandleAttributes, PACCESS_STATE PassedAccessState, ACCESS_MASK DesiredAccess,    
  POBJECT_TYPE ObjectType, KPROCESSOR_MODE AccessMode, PHANDLE Handle);
VOID __stdcall NotifyRoutine(HANDLE ParentId, HANDLE ProcessId, BOOLEAN Create);

NTSTATUS _stdcall sub_11E48(DWORD, PVOID, PVOID);
NTSTATUS _stdcall DispatchIoctl(PDEVICE_OBJECT pDevObj, PIRP pIrp);
VOID _stdcall DriverUnload(PDRIVER_OBJECT pDrvObj);

// 实现

extern "C" NTSTATUS _stdcall DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING puRegistryString)
{
   // 实际上这个为入口点,不过为了方便就写在了一起
   if(dword_14288 != 0 && dword_14288 == 0xBB40E64E) // 十进制3141592654,晕倒。如果下面的异或操作已经执行过奇数次,那么这两个值不等
   {
       // 不过实际上由于 dword_14288 != 0的判断,不可以执行奇数次
       dword_14288 = *(DWORD*)KeTickCount ^ dword_14288; // 与那个大PI异或
   }
   // 这里有一个长跳转,可能做过手脚
// jmp to text.12996,开始进入正事
   // 定义变量
   // sub esp,14H
   // 由后面的反汇编我们可以知道定义了些什么

   UNICODE_STRING SymbolicLinkName;
   UNICODE_STRING DestinationString;
   PDEVICE_OBJECT DeviceObject = NULL;
   // 这个是我自己定义的,实际上在堆栈中没有分配
   NTSTATUS st = STATUS_SUCCESS;
   NTSTATUS stPre = STATUS_SUCCESS;
   // 初始化
   DestinationString.Length = 0;
   DestinationString.MaximunLength = 0;
   SymbolicLinkName.Length = 0;
   SymbolicLinkName.MaximnumLength = 0;

   _asm call loc_129C5;    // 这个花指令思路比较新
   _asm emit 0fH;
   _asm emit 88H;
   _asm jmp loc_129D3; // IDA认错指令了,它认为是:db eb; pop cs
loc_129C5:
   _asm pop ecx;    // 第一次执行,ecx == loc_129C5 - 3,就是那个0fH 第二次执行为cs
   _asm jmp loc_129CA;        // 这个作者用了这么多嵌入汇编?要知道jmp和call是不能过IDA的

   // 花指令
   _asm emit 50H;
   _asm emit 33H;
loc_129CA:
   _asm add ecx,2; // 第一次执行 ecx == loc_129C5 - 1,就是loc_129C5上面那条指令,IDA将它认出来了。
                   // 第二次执行时 ecx = loc_129D3
   _asm jmp loc_129D0;
   _asm emit 76H;
loc_129D0:
   goto ecx;    // 原来这里的指令是 push ecx, retn,相当于jmp
               // 第二次执行时跳到了loc_129D3处
loc_129D3:
   // 花指令段结束。开始!
   // 这里最好把前面那些花指令Undefine掉,使得IDA认为这是一个函数。
   // 这样就可以显示函数的参数名了。
   proc_11218(); // 里面有jmp 1121F,进去
   RtlInitUnicodeString(&DestinationString, L"//Device//TesSafe");
   st = IoCreateDevice(DriverObject, 0, &DestinationString, 0x22,
       0, FALSE, &DeviceObject);
   // 反汇编里有一句 mov [ebp+DriverObject],eax 可能是因为参数DriverObject已经不再使用
   // 可以用来放st ,这是编译器优化的结果
   if(!NT_SUCCESS(st))
   {
       return st;
   }
   RtlInitUnicodeString(&SourceString, L"//DosDevices//TesSafe");
   st = IoCreateSymbolicLink(&SymbolicLinkName, &DestinationString);
   stPre = st;
   if(!NT_SUCCESS(st))
   {
       return st;
   }
   // 这里有个小花
   _asm call loc_12A34;
   _asm emit 83H;
   _asm emit 0F8H;
   _asm emit 2H;
   _asm emit 74H;
loc_12A34:
   _asm add esp,4; // 保持堆栈平衡

   DriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchIoctl;
   DriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchIoctl;
   DriverObject->MajorFunction[IRP_MJ_CLEANUP] = DispatchIoctl;
   DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DispatchIoctl;
   st = sub_11E48(&dword_142B0, &unk_142B4, &unk_143E0);
   if(!NT_SUCCESS(st))
   {
       IoDeleteSymolicLink(&SymbolicLinkName);
       // 这里还有一个判断,用于判断上次的st是否为TRUE
       if(!NT_SUCCESS(stPre))
       {
           IoDeleteDevice(DeviceObject);
       }

       return st;
   }
   // 这里来了个小花(call loc_12A8A),将它去掉
   _asm call loc_12A8A;
   _asm emit 74H;
loc_12A8A:
   _asm add esp,4; // 平衡堆栈

   BaseAddess = MmAllocateNonCachedMemory(0x2000);
   return st;
}

// 防止动态调试

VOID proc_1121F()
{
   while(::KdDebuggerEnabled())
   {
       ::KdDisableDebugger();
   }
}

// 下面的东西先反成C,等有时间看看头文件,再把结构补上
NTSTATUS _stdcall sub_11E48(DWORD* arg_0 /* ntoskrnl.exe 大小 */, PVOID arg_4 /* 标志 */, PVOID arg_8)
{
   DWORD dwCount = 0x10000;
   PVOID pMem;
   NTSTATUS st = 0xC0000004;
   for(; st == 0xC0000004; dwCount *= 2)
   {
       pMem = ExAllocatePoolWithTag(0, dwCount, 0x4E746547);
       if(pMem == NULL)
           return 0xC000009A;
       st = ZwQuerySystemInformation(SystemModuleInformation, pMem, 0x10000, 0);
       if(st == 0xC0000004) // 缓冲太小?
       {
           ExFreePoolWithTag(pMem, 0);
       }
       else if(NT_SUCCESS(st))    // 怎么看着这么别扭?
       {
           // 空间大小合适
           break;
       }
       else
       {
           ExFreePoolWithTag(pMem, 0);
           return st;
       }
   }
   PSYSTEM_MODULE_INFORMATION pInfo = (PSYSTEM_MODULE_INFORMATION)pMem;
   // pInfo是第一个成员
   *arg_0 = pInfo->Size; // 一些信息
   *arg_4 = pInfo->Flags;

   if(arg_8 != 0)
   {
       sub_11000(arg_8, 0x10, (DWORD)(pInfo->ModuleNameOffset + (DWORD)pInfo + 0x20));
       if(pMem != NULL)    // 这个判断好像没用吧,如果内存分配不成功早就return 0xC000009A了。
           ExFreePoolWithTag(pMem, 0);
   }
   return st;
}


VOID _stdcall sub_11000(PBYTE arg_0, int arg_4, PBYTE arg_8)
{
   if(arg_4 == 0)
       return 0xC000000D;
   // 字符串拷贝?
   //    直接反C:
   for(char c = *arg_8; c != 0; c = *(arg_8++))
   {
       *arg_0 = c;
       arg_0++;
       arg_4--;
   }
   if(arg_4 == 0) // 长度?
   {
       arg_0--;
   }
   *(BYTE*)arg_0 = 0;
}

VOID _stdcall DriverUnload(PDRIVER_OBJECT pDrvObj)
{
   // 应用了与DriverEntry几乎一样的加花手法,这里不再重复。
   // 我已经去除了花指令
   UNICODE_STRING DestinationString;
   LARGE_INTEGER TimeOut;
   NTSTATUS st = STATUS_SUCCESS;
   TimeOut.HighPart = 0xFFFFFFFF;
   TimeOut.LowPart = 0xFFE17B80;
   if(dword_142C4 == 0)
       return;
   sub_118D0();
   PsSetCreateProcessNotifyRoutine(NotifyRoutine, 1);
   do
   {
       KeWaitForSingleObject(unk_143C0, 0, 0, 0, &TimeOut); // 应该对unk_143C0检测一下合理性吧。。我没逆完,先不下结论。
       // ....
   }while(InterlockedExchange(&unk_14328, 0); // 工作还没有完成,不要执行清理工作。

   KeWaitForSingleObject(unk_143C0, 0, 0, 0, &TimeOut);
   ObDereferenceObject(unk_143C0);
   if(BaseAddress != 0)
   {
       MmFreeNonCachedMemory(BaseAddress, 0x2000);
   }
   RtlInitUnicodeString(&DestinationString, L"//DosDevices//TesSafe");
   IoDeleteSymbolicLink(&destinationString);
   IoDeleteDevice(pDrvObj->DeviceObject);
}


NTSTATUS __stdcall DispatchIoctl(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
   pIrp->IoStatus = STATUS_SUCCESS;
   pIrp->Information = 0;
   IoCompleteRequest(pIrp, IO_NO_INCREMENT);
   return STATUS_SUCCESS;
}


VOID _stdcall sub_118D0()
{
   // 待续
}
VOID _stdcall sub_1230C()
{
   // 待续
}

// 这个函数的反C可能错误多些
// 逻辑判断不少,再加上花指令,容易搞错
VOID __stdcall NotifyRoutine(HANDLE ParentId, HANDLE ProcessId, BOOLEAN Create)
{
   NTSTATUS st = STATUS_SUCCESS;
   PEPROCESS Object = NULL;
   BOOLEAN NewIrql = FALSE;
   char* pImageName = NULL;
   if(Create)
   {
       if(dword_14050 == 6)
       {
           if(dword_1431C == 0) // 这个四字节变量保存一个指向PsGetProcessImageFileName的指针
               return;
           st = PsLookupProcessByProcessId(ProcessId, &Object);
           if(!NT_SUCCESS(st))
               return;
           pImageName = PsGetProcessImageFileName(Object); // 返回名字
           // 多媒体保护进程?
           if(strnicmp(pImageName, "mfpmp.exe") == 0) // 这是一个_cdecl约定的函数,所以调用后使用add esp,0ch平衡堆栈
           {
               // 这个函数是_fastcall,需要通过EDX和ECX传递参数,这个函数仅仅使用了ECX
               KfAcquireSpinLock(&SpinLock);
               if(dword_14338 != *(DWORD*)dword_14338)
               {
                   // 下面是伪C代码,要想编译得自己定义结构
                   // 这个结构中有SINGLE_LIST_ENTRY
                   for(DWORD pEsi = dword_14338; pEsi != NULL; pEsi = *(DWORD*)pEsi /* Offset 0 , Equal to pEsi = *pEsi*/)
                   {
                       if(*(DWORD*)(pEsi - 4) == ParentId) // 应该是个链表吧。。。。
                       {
                           *(DWORD*)pEsi = Object;    // 加入?
                           break;
                       }
                   }
               }
               KfReleaseSpinLock(&SpinLock, FALSE);
           }
           ObDereferenceObject(Object);
       }
   }
   else
   {
       // 还是老的加花方法,略
       PsLookupProcessByProcessId(ProcessId, &Object);
       // 没有对st 验值。如果我HOOK掉PsLookupProcessByProcessId怎么办?
       NewIrql = KfAcquireSpinLock(&SpinLock);
       if(dword_14338 != *(DWORD*)dword_14338) // 链表
       {
           for(DWORD pEax = dword_14338; pEax != NULL; pEax = *(DWORD*)pEax /* Offset 0 , Equal to pEsi = *pEsi*/)
           {
               DWORD dwContain = pEax - 8;
               if(*(DWORD*)dwContain == (DWORD)Object || *(DWORD)(dwContain + 4) == 0) // 这个CONTAIN的偏移为8
               {
                   // 从其中删除元素?
                   DWORD temp = *(DWORD*)pEax;
                   *(DWORD*)(pEax + 4) = *(DWORD*)pEax; // 设置链表
                   *(DWORD*)(temp + 4) = pEax + 4; // A* q = p; q->pNext = p->pNext;
                   ExFreePoolWithTag((DWORD)(pEax - 8), 0);
                   break;
               }
           }
       }
       KfReleaseSpinLock(&SpinLock, NewIrql);
   }
}
原创粉丝点击