2011年10月31日星期一

[轉載]ZwLoadDriver用其他註冊表路徑加載驅動的辦法~


作 者: cvcvxk
時 間: 2011-10-28,13:44:38
鏈接: http://bbs.pediy.com/showthread.php?t=142021

先上代碼~

代碼:
#include <stdio.h>
#include <tchar.h>
#include <locale.h>
#include <windows.h>
#include <stdlib.h>
#include <psapi.h>
#include <Tlhelp32.h>
#pragma comment (lib, "psapi.lib")
typedef struct _UNICODE_STRING {
  USHORT Length;
  USHORT MaximumLength;
  PWSTR Buffer;
} UNICODE_STRING;
typedef UNICODE_STRING *PUNICODE_STRING;
typedef struct _STRING {
  USHORT Length;
  USHORT MaximumLength;
  PCHAR Buffer;
} ANSI_STRING;
typedef ANSI_STRING *PANSI_STRING;
DWORD drvldr_load(char* drvname)
{
  char regkey[512];
  int buflen;
  UNICODE_STRING us;
  ANSI_STRING as;
  HMODULE hNtdll = LoadLibraryA( ("ntdll.dll") );
  int (__stdcall *RtlAnsiStringToUnicodeString)(void*,void*,int)=
    (int (__stdcall *)(void *,void *,int))GetProcAddress(hNtdll,"RtlAnsiStringToUnicodeString");
  int (__stdcall *ZwLoadDriver)(void*)=
    (int (__stdcall *)(void *))GetProcAddress(hNtdll,"ZwLoadDriver");
  ///
  buflen=sprintf(regkey,"\\Registry\\Machine\\SOFTWARE\\%s",drvname);
  as.Buffer = (PCHAR)regkey;
  as.Length = (USHORT)buflen;
  as.MaximumLength=(USHORT)buflen;
  RtlAnsiStringToUnicodeString(&us, &as,TRUE);

  return ZwLoadDriver(&us);
}
DWORD drvldr_reg(char* drvname,DWORD start_type,char* path)
  //path should be kernel type like \??\xxx
{
  char regkey[512];
  DWORD regdata;
  int buflen;
  HKEY hkey;
  ///
  sprintf(regkey,"SOFTWARE\\%s",drvname);
  if(RegCreateKeyA(HKEY_LOCAL_MACHINE, regkey, &hkey)!=ERROR_SUCCESS)
  {return -1;}
  regdata=SERVICE_KERNEL_DRIVER;
  RegSetValueExA(hkey, ("Type"), 0, REG_DWORD, (const unsigned char *)&regdata, 4);
  regdata=SERVICE_ERROR_NORMAL;
  RegSetValueExA(hkey, ("ErrorControl"), 0, REG_DWORD, (const unsigned char *)&regdata, 4);
  regdata = start_type;
  RegSetValueExA(hkey, ("Start"), 0, REG_DWORD, (const unsigned char *)&regdata, 4);
  buflen = strlen(drvname);
  RegSetValueExA(hkey, ("DisplayName"), 0, REG_EXPAND_SZ, (const unsigned char *)drvname, buflen);
  buflen = strlen(path);
  RegSetValueExA(hkey, ("ImagePath"), 0, REG_EXPAND_SZ, (const unsigned char *)path, buflen);
  RegSetValueExA(hkey,("Group"),0,REG_SZ,(const BYTE *)"Base",strlen("Base"));
  RegCloseKey(hkey);
  //sprintf(regkey,"System\\CurrentControlSet\\Services\\%s",drvname);
  //RegCreateKeyA(HKEY_LOCAL_MACHINE, regkey, &hkey);
  //regdata=SERVICE_KERNEL_DRIVER;
  //RegCloseKey(hkey);
  sprintf(regkey,"System\\Setup\\AllowStart\\%s",drvname);
  RegCreateKeyA(HKEY_LOCAL_MACHINE, regkey, &hkey);
  regdata=SERVICE_KERNEL_DRIVER;
  RegCloseKey(hkey);
  return 0;
}
//Jan 4 2005
//Enable specific privilege
BOOL EnableSpecificPrivilege(BOOL bEnable,LPCTSTR Name)
{
  BOOL bResult = FALSE;
  HANDLE hToken;
  TOKEN_PRIVILEGES TokenPrivileges;

  if(OpenProcessToken(GetCurrentProcess(),TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,&hToken) == 0)
  {
    return FALSE;
  }

  TokenPrivileges.PrivilegeCount = 1;
  TokenPrivileges.Privileges[0].Attributes = bEnable ? SE_PRIVILEGE_ENABLED : 0;
  bResult = LookupPrivilegeValue(NULL,Name,&TokenPrivileges.Privileges[0].Luid);
  if(!bResult)
  {
    CloseHandle(hToken);
    return FALSE;
  }

  bResult = AdjustTokenPrivileges(hToken,FALSE,&TokenPrivileges,sizeof(TOKEN_PRIVILEGES),NULL,NULL);
  if(GetLastError() != ERROR_SUCCESS || !bResult)
  {
    CloseHandle(hToken);
    return FALSE;
  }

  CloseHandle(hToken);
  return TRUE;
}

//Jan 4 2005
//Enable all privilege, return num of privileges successfully enabled
DWORD EnableAllPrivilege(BOOL bEnable)
{
  DWORD count=0;
  ///
  count+=EnableSpecificPrivilege(bEnable,SE_ASSIGNPRIMARYTOKEN_NAME);
  count+=EnableSpecificPrivilege(bEnable,SE_AUDIT_NAME);
  count+=EnableSpecificPrivilege(bEnable,SE_BACKUP_NAME);
  count+=EnableSpecificPrivilege(bEnable,SE_CHANGE_NOTIFY_NAME);
  count+=EnableSpecificPrivilege(bEnable,SE_CREATE_PAGEFILE_NAME);
  count+=EnableSpecificPrivilege(bEnable,SE_CREATE_PERMANENT_NAME);
  count+=EnableSpecificPrivilege(bEnable,SE_CREATE_TOKEN_NAME);
  count+=EnableSpecificPrivilege(bEnable,SE_DEBUG_NAME);
  count+=EnableSpecificPrivilege(bEnable,SE_INC_BASE_PRIORITY_NAME);
  count+=EnableSpecificPrivilege(bEnable,SE_INCREASE_QUOTA_NAME);
  count+=EnableSpecificPrivilege(bEnable,SE_LOAD_DRIVER_NAME);
  count+=EnableSpecificPrivilege(bEnable,SE_LOCK_MEMORY_NAME);
  count+=EnableSpecificPrivilege(bEnable,SE_PROF_SINGLE_PROCESS_NAME);
  count+=EnableSpecificPrivilege(bEnable,SE_REMOTE_SHUTDOWN_NAME);
  count+=EnableSpecificPrivilege(bEnable,SE_RESTORE_NAME);
  count+=EnableSpecificPrivilege(bEnable,SE_SECURITY_NAME);
  count+=EnableSpecificPrivilege(bEnable,SE_SHUTDOWN_NAME);
  count+=EnableSpecificPrivilege(bEnable,SE_SYSTEM_ENVIRONMENT_NAME);
  count+=EnableSpecificPrivilege(bEnable,SE_SYSTEM_PROFILE_NAME);
  count+=EnableSpecificPrivilege(bEnable,SE_SYSTEMTIME_NAME);
  count+=EnableSpecificPrivilege(bEnable,SE_TAKE_OWNERSHIP_NAME);
  count+=EnableSpecificPrivilege(bEnable,SE_TCB_NAME);
  count+=EnableSpecificPrivilege(bEnable,SE_UNSOLICITED_INPUT_NAME);
  count+=EnableSpecificPrivilege(bEnable,SE_MACHINE_ACCOUNT_NAME);

  return count;
}
int _tmain(int argc, _TCHAR* argv[])
{
  EnableAllPrivilege(TRUE);
  drvldr_reg("caonima",3,"\\??\\D:\\1.sys");
  drvldr_load("caonima");
  return 0;
}
對於1.sys的有些特殊的要求,首先1.sys裡的代碼不會被真的執行,但是1.sys這個驅動導入的動態庫驅動可以被執行
舉例下圖:
1.sys的導入表樣子:
x1.jpg下載此附件需要消耗2Kx,下載中會自動扣除。
導入的dllsys的導出表樣子:
x.jpg下載此附件需要消耗2Kx,下載中會自動扣除。
真正被執行的代碼要寫在動態庫驅動的NTSTATUS DllInitialize(IN PUNICODE_STRING RegistryPath)裡~~
具體怎麼寫內核版動態庫,請自行baidu研究。
好了爆完~這個方法的好處就是寫註冊表的過程基本不觸發任何報警——至於加載部分,仁者見仁吧,這玩意2005年的東西了~

2011年10月18日星期二

[轉載]MS11-081中IE9導致的一個CHM漏洞簡單分析


作 者: promsied
時 間: 2011-10-12,21:35:15
鏈接: http://bbs.pediy.com/showthread.php?t=141323

這個漏洞只存在於IE9,會導致無法下載CHM中內嵌的文件,在沒有安裝KB2586448更新的機器上測試
首先新建一個1.html文件,內容如下

代碼:
<a href = "1.rar">1.rar</a>
然後和一個1.rar文件用HTML Help Workshop一起編譯成CHM
打開CHM點擊1.rar鏈接,也可以在IE地址欄輸入mk:@MSITStore:XXX(具體路徑)\1.chm::/1.rar,報錯調試
停在CDownloadUtilities::MarshalBindContextToStream函數內,這個函數為下載1.rar文件做一些準備工作
聲明如下
HRESULT CDownloadUtilities::MarshalBindContextToStream(CInterThreadMarshal **ppITM<eax>, CDownloadThreadParam *pDTP, wchar_t *szURL, IBindCtx *pBC)
簡單分析下其代碼

代碼:
CDownloadUtilities::MarshalBindContextToStream(CInterThreadMarshal **ppITM<eax>, CDownloadThreadParam *pDTP, wchar_t *szURL, IBindCtx *pBC):
7003D23A mov edi,edi
7003D23C push ebp
7003D23D mov ebp,esp
7003D23F sub esp,0Ch ;HRESULT hr<[ebp-4h]>, IUnknown *pUnk<[ebp-8h]>, IEUserBroker *pIEUB<[ebp-0Ch]>
7003D242 push ebx
7003D243 push esi
7003D244 push edi
7003D245 lea edx,[ebp-8] ;edx = &pUnk
7003D248 push edx ;&pUnk
7003D249 mov edi,eax ;edi = ppITM
7003D24B mov eax,dword ptr [ebp+10h] ;eax = pBC
7003D24E mov ecx,dword ptr [eax] ;ecx = pBC->lpVtbl
7003D250 push offset __GUID_0000000e_0000_0000_c000_000000000046 (6FF65DA8h) ;&IID_IUnknown
7003D255 push eax ;pBC
7003D256 call dword ptr [ecx] ;eax = pBC->lpVtbl->QueryInterface(pBC, &IID_IUnknown, &pUnk)
7003D258 xor ebx,ebx
7003D25A mov dword ptr [ebp-4],eax ;hr = eax
7003D25D cmp eax,ebx
7003D25F jl CDownloadUtilities::MarshalBindContextToStream+0C6h (7003D300h) ;if (FAILED(hr)) goto 7003D300h
7003D265 push 3
7003D267 push offset string L"mk:" (70019BB4h)
7003D26C push dword ptr [ebp+0Ch]
7003D26F call dword ptr [__imp___wcsnicmp (6FE91364h)]
7003D275 add esp,0Ch
7003D278 test eax,eax
7003D27A jne CDownloadUtilities::MarshalBindContextToStream+74h (7003D2AEh) ;if (_wcsnicmp(szURL, L"mk:", 3)) goto 7003D2AEh
7003D27C mov eax,dword ptr [ebp-8] ;eax = pUnk
7003D27F mov ecx,dword ptr [eax] ;ecx = pUnk->lpVtbl
7003D281 push eax ;pUnk
7003D282 call dword ptr [ecx+8] ;pUnk->lpVtbl->Release(pUnk)
7003D285 push ebx ;0
7003D286 lea eax,[ebp+10h] ;eax = &pBC
7003D289 push eax ;&pBC
7003D28A push ebx ;0
7003D28B push ebx ;0
7003D28C push ebx ;0
7003D28D push ebx ;0
7003D28E mov dword ptr [ebp-8],ebx ;pUnk = 0
7003D291 call dword ptr [__imp__CreateAsyncBindCtxEx@24 (7023B14Ch)] ;eax = CreateAsyncBindCtxEx(0, 0, 0, 0, &pBC, 0)
7003D297 mov esi,dword ptr [edi] ;esi = *ppITM
7003D299 mov dword ptr [ebp-4],eax ;hr = eax
7003D29C cmp esi,ebx
7003D29E je CDownloadUtilities::MarshalBindContextToStream+74h (7003D2AEh) ;if (*ppITM == 0) goto 7003D2AEh
7003D2A0 call CInterThreadMarshal::~CInterThreadMarshal (701D7894h) ;(*ppITM)->~CInterThreadMarshal()(this<esi>)
7003D2A5 push esi
7003D2A6 call operator delete (6FEA35D9h) ;delete *ppITM
7003D2AB pop ecx
7003D2AC mov dword ptr [edi],ebx ;*ppITM = 0
7003D2AE cmp dword ptr [ebp-4],ebx
7003D2B1 jl CDownloadUtilities::MarshalBindContextToStream+0C6h (7003D300h) ;if (FAILED(hr)) goto 7003D300h
7003D2B3 mov eax,dword ptr [edi] ;eax = *ppITM
7003D2B5 cmp eax,ebx
7003D2B7 je CDownloadUtilities::MarshalBindContextToStream+89h (7003D2C3h) ;goto 7003D2C3h
7003D2B9 mov ecx,dword ptr [ebp+8] ;ecx = pDTP
7003D2BC mov dword ptr [ecx+18h],eax ;*(pDTP + 18h) = eax
7003D2BF mov dword ptr [edi],ebx ;*ppITM = 0
7003D2C1 jmp CDownloadUtilities::MarshalBindContextToStream+0BDh (7003D2F7h)
7003D2C3 push 4
7003D2C5 call operator new (6FE9E771h) ;eax = new LPSTREAM
7003D2CA pop ecx
7003D2CB cmp eax,ebx
7003D2CD je CDownloadUtilities::MarshalBindContextToStream+99h (7003D2D3h) ;if (eax == 0) goto 7003D2D3h
7003D2CF mov dword ptr [eax],ebx ;[eax] = 0
7003D2D1 jmp CDownloadUtilities::MarshalBindContextToStream+9Bh (7003D2D5h)
7003D2D3 xor eax,eax
7003D2D5 mov ecx,dword ptr [ebp+8] ;ecx = pDTP
7003D2D8 mov dword ptr [ecx+18h],eax ;*(pDTP + 18h) = eax
7003D2DB mov dword ptr [ebp-4],8007000Eh ;hr = 8007000Eh
7003D2E2 cmp eax,ebx
7003D2E4 je CDownloadUtilities::MarshalBindContextToStream+0BDh (7003D2F7h) ;if (eax == 0) goto 7003D2F7h
7003D2E6 push eax
7003D2E7 push dword ptr [ebp-8] ;pUnk
7003D2EA push offset _IID_IBindCtx (6FF2AB8Ch)
7003D2EF call _CoMarshalInterThreadInterfaceInStream@12 (701FF7F2h) ;eax = CoMarshalInterThreadInterfaceInStream(&IID_IBindCtx, pUnk, eax)
7003D2F4 mov dword ptr [ebp-4],eax ;hr = eax
7003D2F7 mov eax,dword ptr [ebp-8] ;eax = pUnk
7003D2FA mov ecx,dword ptr [eax] ;ecx = pUnk->lpVtbl
7003D2FC push eax ;pUnk
7003D2FD call dword ptr [ecx+8] ;pUnk->lpVtbl->Release(lpVtbl)
7003D300 call LCIEUnifiedFrame (6FF9CD1Fh)
7003D305 test al,al
7003D307 je CDownloadUtilities::MarshalBindContextToStream+128h (7003D362h)
7003D309 lea eax,[ebp-0Ch]
7003D30C push eax
7003D30D call dword ptr [__imp_CoCreateUserBroker (6FE925A4h)]
7003D313 test eax,eax
7003D315 js CDownloadUtilities::MarshalBindContextToStream+128h (7003D362h)
7003D317 lea eax,[ebp+0Ch]
7003D31A push eax
7003D31B push dword ptr [ebp-0Ch]
7003D31E push offset _IID_IEUserBroker (6FF37F6Ch)
7003D323 call dword ptr [__imp__CoMarshalInterThreadInterfaceInStream@12 (6FE91FECh)]
7003D329 mov dword ptr [ebp-4],eax
7003D32C cmp eax,ebx
7003D32E jne CDownloadUtilities::MarshalBindContextToStream+11Fh (7003D359h)
7003D330 mov edi,dword ptr [ebp+8]
7003D333 mov eax,dword ptr [edi+14h]
7003D336 mov esi,dword ptr [ebp+0Ch]
7003D339 cmp eax,ebx
7003D33B je CDownloadUtilities::MarshalBindContextToStream+109h (7003D343h)
7003D33D mov ecx,dword ptr [eax]
7003D33F push eax
7003D340 call dword ptr [ecx+8]
7003D343 mov dword ptr [edi+14h],esi
7003D346 cmp esi,ebx
7003D348 je CDownloadUtilities::MarshalBindContextToStream+116h (7003D350h)
7003D34A mov eax,dword ptr [esi]
7003D34C push esi
7003D34D call dword ptr [eax+4]
7003D350 mov eax,dword ptr [ebp+0Ch]
7003D353 mov ecx,dword ptr [eax]
7003D355 push eax
7003D356 call dword ptr [ecx+8]
7003D359 mov eax,dword ptr [ebp-0Ch]
7003D35C mov ecx,dword ptr [eax]
7003D35E push eax
7003D35F call dword ptr [ecx+8]
7003D362 mov eax,dword ptr [ebp-4]
7003D365 pop edi
7003D366 pop esi
7003D367 pop ebx
7003D368 leave
7003D369 ret 0Ch
7003D36C nop
7003D36D nop
7003D36E nop
7003D36F nop
7003D370 nop
簡單改寫成C++代碼

代碼:
HRESULT CDownloadUtilities::MarshalBindContextToStream(CInterThreadMarshal **ppITM<eax>, CDownloadThreadParam *pDTP, wchar_t *szURL, IBindCtx *pBC)
{
  IEUserBroker *pIEUB;
  IUnknown *pUnk;
  HRESULT hr;
  if (SUCCEEDED(hr = pBC->QueryInterface(&IID_IUnknown, &pUnk)))
  {
    if (!_wcsnicmp(szURL, L"mk:", 3))
    {
      pUnk->Release();
      pUnk = NULL;
      hr = CreateAsyncBindCtxEx(0, 0, 0, 0, &pBC, 0);
      if (*ppITM)
      {
        (*ppITM)->~CInterThreadMarshal();
        delete *ppITM;
        *ppITM = NULL;
      }
    }
    if (SUCCEEDED(hr))
    {
      if (*ppITM)
      {
        *(pDTP + 18h) = *ppITM;
        *ppITM = NULL;
      }
      else
      {
        IStream **ppStm = new LPSTREAM;
        if (ppStm)
        {
          *ppStm = 0;
        }
        else
        {
          ppStm = NULL;
        }
        *(pDTP + 18h) = ppStm;
        hr = 8007000Eh;
        if (ppStm)
        {
          hr = CoMarshalInterThreadInterfaceInStream(&IID_IBindCtx, pUnk, ppStm);
        }
      }
      pUnk->Release();
    }
  }
  //omit
  return hr;
}
首先函數調用pBC->QueryInterface獲得pBC對象的IUnknown接口pUnk,由於IBindCtx只繼承自IUnknown,所以IUnknown接口和IBindCtx接口地址實際相同,當下載CHM內嵌的文件時,CDownloadUtilities::MarshalBindContextToStream函數的szURL參數為形如mk:XXX的字符串,所以函數調用pUnk->Release又設pUnk=NULL,而pUnk原本的計數應為1,pUnk->Release徹底釋放了pBC對象,於是函數調用CreateAsyncBindCtxEx重新獲得pBC對象, pBC是更新了但pUnk仍然為NULL,此外函數又徹底釋放了*ppITM對象並設*ppITM=NULL,於是之後函數會調用CoMarshalInterThreadInterfaceInStream列集接口指針,但調用時第二個參數為pUnk,但pUnk= NULL,實際應為pBC,這還不是大問題,緊跟著的pUnk->Release才是禍根,由於pUnk=NULL,取pUnk的虛函數表指針造成訪問違例,程序報錯,不過幸好之前pUnk設為了NULL,不然後果就更糟糕了,解決方法應該是換用pBC。當szURL參數不是形如mk:XXX的字符串時就不會產生問題了。
附件是編譯好的一個CHM,其中的1.rar是本人寫的一個小工具,說明見本人博客http://hi.baidu.com/promised_lu

2011年10月17日星期一

[轉載]EditPlus另類破解


作 者: wangweilll
時 間: 2011-10-12,14:07:09
鏈接: http://bbs.pediy.com/showthread.php?t=141294

由於需要EditPlus來編寫一些東東,就下載了這個軟件。雖然網上有好的註冊碼和破解版的軟件。
呵呵~~好久沒有玩破解軟件了。就想自已來破解看看~~~~~~~,找註冊碼的算法要的時間太長了~
因為軟件只有30天的試用我們把時間調一下在運行下軟件看看~一運行就直接出現了


沒有哪麼多的時間!於是就想到了爆破~~~~~~。爆破點選擇是註冊碼還時間呢,我暈打開OD看看吧~
看有什麼好的信息~~

SetTimer 這個API有什麼用呢百度一下就可以知道了我們下個斷點吧! ! ! ! ! !按F9就來到了

看看

代碼:
mov eax,121E
哪AL == 1E
打開計算器看看1E 是10進制的多少

喔 是30啊! ! ! ! ! ! ! ! ! ! ! !呵呵在想想我們我軟件試用多少天啊,好像是3​​0天喔
哪我們改改看改成試試

F9運行看看發現可以用了

呵呵有的朋友就會想哪保存一下吧~~
可是這個動態鏈接庫中不修改數據

我們反彙編跟隨一下看看來到

ESI中保存的就SetTimer這個API
我們在來看看

我就想可不可以跳過
mov eax,0X121E 不執行它呢
我發現如果ESI 5.我們CALL ESI時它就跳過了
mov eax,0X121E
直接從 mov edx,7FFE0300
F9一下哈哈競然OK了
哪我們就來PATH一下吧

F9運行一下OK沒有問題就保存一份吧~~~~~~~~~~~~
打玩收功吧~~~~~~~~~~~~~~~~

2011年10月13日星期四

[轉載]【修改】打造自己的反彙編引擎



作 者: DoItFreely
時 間: 2011-10-12,12:32:02
鏈接: http://bbs.pediy.com/showthread.php?t=141285

原始信息來自http://bbs.pediy.com/showthread.php?t=75094
本人水平有限,沒有太多理論,只是增強了PE的處理,並稍微hack了一下代碼。
為什麼要增強?處理特定文件格式(比如PE、ELF)並不是反彙編器的功能,原作者僅僅演示了一下如何編寫和使用反彙編器,沒有深入處理特定文件格式也不奇怪
主要增強如下:
1) load整個PE,而不是只處理.text節
         這樣就可以處理.sys等entry point不在.text段的程序
         跨段的調用、引用也能正常處理

2) 從entry point開始反彙編,只有引用到的代碼才反彙編
         反彙編整個.text很可能會得到不准確的結果。對於用C編寫的程序,導出函數和entry point引用到的函數覆蓋了所有的代碼。
         數據段、資源段等“非代碼”段在開始反彙編之前就被排除掉了
         當前的輸出沒有按照地址排序,比較凌亂

3) 檢測對字符常量和導入函數的引用
          如果找到了引用的目標,就輸出在後面

4) 檢測代碼塊的結束
         無條件跳轉指令(JMP)和返回指令(ret,retn)

5) 增加了對實模式代碼的處理(不完善)
         檢測返回DOS的調用

主要修改:
1) 一處原作者忘記轉換偏移量的類型(應該是signed displacement)而導致算錯偏移量的問題
     與IDA對比時發現的

2) 增加了浮點指令的反彙編(有問題)
      沒有讀intel的指令手冊,僅僅是“依葫蘆畫瓢”,結果可想而知。 (原來遇到浮點指令顯示為???,現在是亂抓了一條來充數)


待增強功能:
1)反彙編浮點(d8~df)和f0 xx序列


文件說明:
    dasm.exe 修改後的執行文件
    dasm.txt 用dasm.exe反彙編它自己的輸出
    filedisk.sys v1.7版(?)的filedisk驅動(該軟件開源,地球人應該都知道吧?)
    filedisk.asm IDA 反彙編filedisk.sys的輸出
    filedisk.idb IDA 5.2生成的數據庫,6.1版的IDA應該也能打開吧(?)
    filedisk.txt 用dasm.exe反彙編filedisk.sys的輸出
    dasmMain.exe 原作者編譯的執行文件
    *.h *.c *.dsw *.dsp 工程文件和源碼

(原作者的代碼可以在前面的鏈接頁面裡面找到,dasm.rar。)



實例效果:(來自filedisk.txt)
===============================
11550: Proc_11550 proc


11550: Loc_11550:
11550: mov edi, edi ;8B FF
11552: push ebp ;55
11553: mov ebp, esp ;8B EC
11555: mov eax, dword ptr [ebp + 8h] ;8B 45 08
11558: push esi ;56
11559: mov esi, dword ptr [eax + 28h] ;8B 70 28
1155C: push 0h ;6A 00
1155E: push dword ptr [esi + Ch] ;FF 76 0C
11561: call dword ptr [10C04h] ;FF 15 04 0C 01 00 ExFreePoolWithTag
11567: push dword ptr [esi + 4h] ;FF 76 04
1156A: call dword ptr [10C38h] ;FF 15 38 0C 01 00 ZwClose
11570: mov eax, dword ptr [ebp + Ch] ;8B 45 0C
11573: mov byte ptr [esi], 0h ;C6 06 00
11576: and dword ptr [eax + 18h], 0h ;83 60 18 00
1157A: and dword ptr [eax + 1Ch], 0h ;83 60 1C 00
1157E: xor eax, eax ;33 C0
11580: pop esi ;5E
11581: pop ebp ;5D
11582: retn 8h ;C2 08 00
11550: Proc_12FD9C endp
===============================


(來自dasm.txt, main()片段)

===============================
402180: Proc_402180 proc


402180: Loc_402180:
402180: mov eax, dword ptr [esp + 4h] ;8B 44 24 04
402184: sub esp, 164h ;81 EC 64 01 00 00
40218A: cmp eax, 2h ;83 F8 02
40218D: push ebx ;53
40218E: push ebp ;55
40218F: push esi ;56
402190: push edi ;57
402191: jz short Loc_4021A9 ;74 16 ->
402193: push 408418h ;68 18 84 40 0​​0 string 'Usage: dasm.exe PEFile
'
402198: call dword ptr [40702Ch] ;FF 15 2C 70 40 0​​0 printf
40219E: add esp, 4h ;​​83 C4 04
4021A1: push 0h ;6A 00
4021A3: call dword ptr [40703Ch] ;FF 15 3C 70 40 0​​0 exit
4021A9: mov esi, dword ptr [esp + 17Ch + 0h];8B B4 24 7C 01 00 00
4021B0: mov eax, dword ptr [esi + 4h] ;8B 46 04
4021B3: push eax ;50
4021B4: call Proc_405430 ;E8 77 32 00 00 =>
4021B9: mov ecx, dword ptr [esi + 4h] ;8B 4E 04
4021BC: mov ebx, dword ptr [40702Ch] ;8B 1D 2C 70 40 0​​0 printf
4021C2: push ecx ;51
4021C3: push 408414h ;68 14 84 40 0​​0 string '%s
'
4021C8: mov ebp, eax ;8B E8
4021CA: call ebx ;FF D3
4021CC: push 408404h ;68 04 84 40 0​​0 string 'DOS Header:
'
4021D1: call ebx ;FF D3
4021D3: push 4083B8h ;68 B8 83 40 0​​0 string '====================================== ===================================
'
4021D8: call ebx ;FF D3
4021DA: lea edx, dword ptr [esp + 28h] ;8D 54 24 28
4021DE: push edx ;52
4021DF: push ebp ;55
4021E0: call Proc_405470 ;E8 8B 32 00 00 =>
4021E5: sub esp, 24h ;83 EC 24
4021E8: mov ecx, Fh ;B9 0F 00 00 00
4021ED: lea esi, dword ptr [esp + 54h] ;8D 74 24 54
4021F1: mov edi, esp ;8B FC
4021F3: rep movsd ;F3 A5
4021F5: movsw ;66 A5
4021F7: call Proc_4054C0 ;E8 C4 32 00 00 =>
4021FC: mov eax, dword ptr [esp + 90h + 0h] ;8B 84 24 90 00 00 00
402203: add esp, 40h ;83 C4 40
402206: cmp ax, 1Eh ;66 3D 1E 00
40220A: jb Loc_402325 ;0F 82 15 01 00 00 ->
402210: mov ecx, dword ptr [esp + 2Ch] ;8B 4C 24 2C
402214: test cx, cx ;66 85 C9
402217: jz short Loc_40222D ;74 14 ->
402219: cmp cx, ax ;66 3B C8
40221C: jnb short Loc_40222D ;73 0F ->
40221E: and ecx, FFFFh ;81 E1 FF FF 00 00
402224: and eax, FFFFh ;25 FF FF 00 00
402229: sub eax, ecx ;2B C1
40222B: jmp short Loc_402235 ;EB 08 ->


402235: Loc_402235:
402235: mov esi, eax ;8B F0
402237: push esi ;56
402238: push esi ;56
402239: push 408388h ;68 88 83 40 0​​0 string '
disassemly dos stub code, %d(0x%x) bytes
'
40223E: call ebx ;FF D3
402240: push esi ;56
402241: call dword ptr [407094h] ;FF 15 94 70 40 0​​0 malloc
402247: mov edi, eax ;8B F8
402249: add esp, 10h ;83 C4 10
40224C: test edi, edi ;85 FF
40224E: jnz short Loc_40226F ;75 1F ->
402250: mov eax, dword ptr [407020] ;A1 20 70 40 0​​0 _iob
402255: push 40836Ch ;68 6C 83 40 0​​0 string 'Memory allocation error !
'
40225A: add eax, 40h ;83 C0 40
40225D: push eax ;50
40225E: call dword ptr [40701Ch] ;FF 15 1C 70 40 0​​0 fprintf
402264: add esp, 8h ;83 C4 08
402267: push FFh ;6A FF
402269: call dword ptr [40703Ch] ;FF 15 3C 70 40 0​​0 exit
40226F: mov eax, dword ptr [esp + 2Ch] ;8B 44 24 2C
402273: cmp ax, word ptr [esp + 50h] ;66 3B 44 24 50
402278: jnb short Loc_40228C ;73 12 ->
40227A: and eax, FFFFh ;25 FF FF 00 00
40227F: push 0h ;6A 00
402281: push eax ;50
402282: push ebp ;55
402283: call dword ptr [407038h] ;FF 15 38 70 40 0​​0 fseek
402289: add esp, Ch ;83 C4 0C
40228C: push ebp ;55
40228D: push esi ;56
40228E: push 1h ;6A 01
402290: push edi ;57
402291: call dword ptr [407034h] ;FF 15 34 70 40 0​​0 fread
402297: mov ecx, dword ptr [esp + 3Ah] ;8B 4C 24 3A
40229B: push esi ;56
40229C: and ecx, FFFFh ;81 E1 FF FF 00 00
4022A2: shl ecx, 10h ;C1 E1 10
4022A5: push ecx ;51
4022A6: call Proc_401000 ;E8 55 ED FF FF <=
4022AB: mov edx, dword ptr [esp + 42h] ;8B 54 24 42
4022AF: mov eax, dword ptr [esp + 40h] ;8B 44 24 40
4022B3: and edx, FFFFh ;81 E2 FF FF 00 00
4022B9: and eax, FFFFh ;25 FF FF 00 00
4022BE: shl edx, 10h ;C1 E2 10
4022C1: or edx, eax ;0B D0
4022C3: push edx ;52
4022C4: call Proc_401270 ;E8 A7 EF FF FF <=
4022C9: mov ecx, dword ptr [esp + 46h] ;8B 4C 24 46
4022CD: mov edx, dword ptr [esp + 44h] ;8B 54 24 44
4022D1: and ecx, FFFFh ;81 E1 FF FF 00 00
4022D7: and edx, FFFFh ;81 E2 FF FF 00 00
4022DD: shl ecx, 10h ;C1 E1 10
4022E0: or ecx, edx ;0B CA
4022E2: mov dword ptr [40AB34h], 1h ;C7 05 34 AB 40 0​​0 01 00 00 00
4022EC: push ecx ;51
4022ED: push 408350h ;68 50 83 40 0​​0 string '%X: realmode_entry_point:
下載請到:http://bbs.pediy.com/showthread.php?t=141285

2011年10月12日星期三

[轉載]計算機病毒對消息鉤子的利用與對抗


作 者: dncwbc
時 間: 2011-10-07,23:57:15
鏈接: http://bbs.pediy.com/showthread.php?t=141059

一、消息鉤子的概念
    1、基本概念
   Windows應用程序是基於消息驅動的,任何線程只要註冊窗口類都會有一個消息隊列用於接收用戶輸入的消息和系統消息。為了攔截消息,Windows提出了鉤子的概念。鉤子(Hook)是Windows消息處理機制中的一個監視點,鉤子提供一個回調函數。當在某個程序中安裝鉤子後,它將監視該程序的消息,在指定消息還沒到達窗口之前鉤子程序先捕獲這個消息。這樣就有機會對此消息進行過濾,或者對Windows消息實現監控。
    2、分類
    消息鉤子分為局部鉤子和全局鉤子。局部鉤子是指僅攔截指定一個進程的指定消息,全局鉤子將攔截系統中所有進程的指定消息。
    3、實現步驟
    使用鉤子技術攔截消息通常分為如下幾個步驟:
    設置鉤子回調函數;(攔截到消息後所調用的函數)
    安裝鉤子;(使用SetWindowsHookEx函數)
    卸載鉤子。 (使用UnhookWindowsHookEx函數)
    4、功能
    利用消息鉤子可以實現特效界面、同步消息、監控消息、自啟動等功效。

二、病毒對消息鉤子技術的利用
    計算機病毒經常利用消息鉤子實現兩種功能:
    1、監控用戶按鍵,盜取用戶信息。
    這樣的病毒會啟動一個常駐內存的EXE病毒進程,然後安裝一個全局鍵盤消息鉤子,鉤子回調函數位於病毒進程中,這樣系統中任何有按鍵操作的進程,其按鍵詳細信息都會被病毒進程攔截記錄。
    2、自啟動
    這樣的病毒會將鉤子回調函數放在一個DLL文件中,然後安裝一個全局消息(容易觸發的消息,如WH_CBT、WH_GETMESSAGE等)鉤子,這樣凡響應該消息的進程都會自動加載病毒的DLL,病毒也就跟著自動運行了。

三、消息鉤子病毒的對抗技術(重點)
    1、對抗技術原理
    對付消息鉤子病毒方法很簡單,只要將病毒安裝的鉤子卸載掉即可。 (注意:對於系統中許多進程已經因為全局鉤子而加載了病毒DLL的情況,並不需要去卸載這些DLL,只要安裝的消息鉤子被卸載那麼對應的DLL也都會被在這些進程中自動卸載。 )卸載鉤子有兩種方法:
    (1)、結束掉安裝鉤子的進程
    將設置鉤子的進程結束,進程在退出之前會自行卸載掉該進程安裝的所有消息鉤子。這種方法很適合對付監控用戶按鍵的病毒。
    (2)、獲得消息鉤子句柄,然後調用UnhookWindowsHookEx函數即可將消息鉤子卸載。
    如果病毒單獨啟動了一個病毒進程安裝了一個全局消息鉤子,然後就常駐內存。這時我們將這個病毒進程結束掉即可。但是如果病毒在系統進程中註入代碼而安裝的鉤子,這樣鉤子句柄就位於系統進程中,我們不可以結束系統進程,這時就只能獲取這個消息鉤子句柄,然後調用函數卸載。
    2、對抗技術實現細節
    對於結束掉安裝鉤子進程從而卸載病毒消息鉤子的方法很容易實現,只要找到病毒進程結束即可。而對於獲取病毒消息鉤子句柄,然後調用函數卸載鉤子的方法比較複雜,也是本文重點討論的內容,將在下一個標題中詳細介紹。

四、查找病毒消息鉤子句柄然後卸載的方法實現(重點、難點)
    1、實現原理分析
    系統會將所有安裝的鉤子句柄保存在內核中,要查找病毒安裝的消息鉤子句柄,我們要枚舉所有的消息鉤子句柄。如何枚舉稍後講解,還要解決一個問題,就是在枚舉過程中,我們怎麼知道哪個句柄是病毒安裝的呢?
    通過分析病毒樣本我們通常可以得到病毒安裝鉤子就是為了令其他合法進程加載病毒DLL,所以它會將鉤子回調函數寫在該DLL中。在枚舉消息鉤子句柄時,同時也可以得到該句柄所對應的回調函數所屬的DLL模塊,根據這個DLL模塊是不是病毒的DLL模塊即可找到病毒的消息鉤子句柄,最後將其卸載即可。
    關於如何枚舉系統消息鉤子句柄,對於不同的操作系統方法大不相同,這裡介紹一種用戶層讀內存的方法,此方法僅在2000/XP系統下可用。
    在2000/XP系統下有一個Windows用戶界面相關的應用程序接口User32.dll。它用於包括Windows窗口處理,基本用戶界面等特性,如創建窗口和發送消息。當它被加載到內​​存後,它保存了所有Windows窗口、消息相關的句柄,其中就包括消息鉤子句柄。這些句柄被保存在一塊共享內存段中,通常稱為R3層的GUI TABLE。所以只要我們找到GUI TABLE,然後在其中的句柄中篩選出消息鉤子句柄。 GUI TABLE這塊內存段可以被所有進程空間訪問。 GUI TABLE被定義成如下結構:
typedef struct tagSHAREDINFO {
  struct tagSERVERINFO *pServerInfo; //指向tagSERVERINFO結構的指針
  struct _HANDLEENTRY *pHandleEntry; // 指向句柄表
  struct tagDISPLAYINFO *pDispInfo; //指向tagDISPLAYINFO結構的指針
  ULONG ulSharedDelta;
  LPWSTR pszDllList;
} SHAREDINFO, *PSHAREDINFO;
    tagSHAREDINFO結構體的第一個成員pServerInfo所指向的tagSERVERINFO結構體定義如下。
typedef struct tagSERVERINFO {
    short wRIPFlags ;
    short wSRVIFlags ;
    short wRIPPID ;
    short wRIPError ;
    ULONG cHandleEntries; //句柄表中句柄的個數
}SERVERINFO,*PSERVERINFO;
    可以看出通過tagSERVERINFO結構的cHandleEntries成員即可得到tagSHAREDINFO結構的pHandleEntry成員所指向的句柄表中的句柄數。
    tagSHAREDINFO結構體的第二個成員pHandleEntry是指向_HANDLEENTRY結構體數組起始地址的指針,該數組的一個成員對應一個句柄。句柄結構體_HANDLEENTRY定義如下。
typedef struct _HANDLEENTRY{
    PVOID pObject; //指向句柄所對應的內核對象
    ULONG pOwner;
      BYTE bType; //句柄的類型
    BYTE bFlags;
    short wUniq;
}HANDLEENTRY,*PHANDLEENTRY;
    _HANDLEENTRY結構體成員bType是句柄的類型,通過該變量的判斷可以篩選消息鉤子句柄。 User32中保存的句柄類型通常有如下種類。
typedef enum _HANDLE_TYPE
{
        TYPE_FREE = 0,
        TYPE_WINDOW = 1 ,
        TYPE_MENU = 2, //菜單句柄
     TYPE_CURSOR = 3, //光標句柄
     TYPE_SETWINDOWPOS = 4,
        TYPE_HOOK = 5, //消息鉤子句柄
     TYPE_CLIPDATA = 6 ,
        TYPE_CALLPROC = 7,
        TYPE_ACCELTABLE = 8,
        TYPE_DDEACCESS = 9,
        TYPE_DDECONV = 10,
        TYPE_DDEXACT = 11,
        TYPE_MONITOR = 12,
        TYPE_KBDLAYOUT = 13 ,
        TYPE_KBDFILE = 14 ,
        TYPE_WINEVENTHOOK = 15 ,
        TYPE_TIMER = 16,
        TYPE_INPUTCONTEXT = 17 ,
        TYPE_CTYPES = 18 ,
        TYPE_GENERIC = 255
}HANDLE_TYPE;
    _HANDLEENTRY結構體的成員pObject是指向句柄對應的內核對象的指針。
    這樣只要通過pObject就可以得到句柄的詳細信息(其中包括創建進程,線程、回調函數等信息),通過bType就可以的值句柄的類型。
_HANDLEENTRY結構體的其他成員可以忽略不看。
    (知識要點補充:如何在用戶層程序中讀取內核內存)
    需要注意的是,pObject指針指向的是內核內存,不可以在用戶層直接訪問內核內存。後面還有些地方也同樣是內核內存,需要加以注意。應該把內核內存的數據讀取到用戶層內存才可以訪問。且不可以直接訪問,畢竟不是在驅動中。
    在用戶層讀取內核內存使用ZwSystemDebugControl函數,它是一個Native API。其原型如下。
NTSYSAPI
NTSTATUS
NTAPI
ZwSystemDebugControl(
    IN DEBUG_CONTROL_CODE Con​​trolCode,//控制代碼
   IN PVOID InputBuffer OPTIONAL, //輸入內存
   IN ULONG InputBufferLength, //輸入內存長度
   OUT PVOID OutputBuffer OPTIONAL, //輸出內存
   IN ULONG OutputBufferLength, //輸出內存長度
   OUT PULONG ReturnLength OPTIONAL //實際輸出的長度);
ZwSystemDebugControl函數可以用於讀/寫內核空間、讀/寫MSR、讀/寫物理內存、讀/寫IO端口、讀/寫總線數據、KdVersionBlock等。由第一個參數ControlCode控制其功能,可以取如下枚舉值。
    typedef enum _SYSDBG_COMMAND {
    //以下5個在Windows NT各個版本上都有
    SysDbgGetTraceInformation = 1,
      SysDbgSetInternalBreakpoint = 2,
      SysDbgSetSpecialCall = 3,
      SysDbgClearSpecialCalls = 4,
      SysDbgQuerySpecialCalls = 5,
    // 以下是NT 5.1 新增的
    SysDbgDbgBreakPointWithStatus = 6,
    //獲取KdVersionBlock
      SysDbgSysGetVersion = 7,
    //從內核空間複製到用戶空間,或者從用戶空間複製到用戶空間
    //但是不能從用戶空間複製到內核空間
    SysDbgCopyMemoryChunks_0 = 8,
   //SysDbgReadVirtualMemory = 8,
    //從用戶空間複製到內核空間,或者從用戶空間複製到用戶空間
    //但是不能從內核空間複製到用戶空間
    SysDbgCopyMemoryChunks_1 = 9,
    //SysDbgWriteVirtualMemory = 9,
    //從物理地址複製到用戶空間,不能寫到內核空間
    SysDbgCopyMemoryChunks_2 = 10,
    //SysDbgReadVirtualMemory = 10,
    //從用戶空間複製到物理地址,不能讀取內核空間
    SysDbgCopyMemoryChunks_3 = 11,
    //SysDbgWriteVirtualMemory = 11,
    //讀/寫處理器相關控制塊
    SysDbgSysReadControlSpace = 12,
    SysDbgSysWriteControlSpace = 13,
    //讀/寫端口
    SysDbgSysReadIoSpace = 14,
      SysDbgSysWriteIoSpace = 15,
    //分別調用RDMSR@4和_WRMSR@12
      SysDbgSysReadMsr = 16,
      SysDbgSysWriteMsr = 17,
    //讀/寫總線數據
    SysDbgSysReadBusData = 18,
      SysDbgSysWriteBusData = 19,
      SysDbgSysCheckLowMemory = 20,
// 以下是NT 5.2 新增的
    //分別調用_KdEnableDebugger@0和_KdDisableDebugger@0
      SysDbgEnableDebugger = 21,
      SysDbgDisableDebugger = 22,
    //獲取和設置一些調試相關的變量
    SysDbgGetAutoEnableOnEvent = 23,
      SysDbgSetAutoEnableOnEvent = 24,
      SysDbgGetPitchDebugger = 25,
      SysDbgSetDbgPrintBufferSize = 26,
      SysDbgGetIgnoreUmExceptions = 27,
      SysDbgSetIgnoreUmExceptions = 28
    } SYSDBG_COMMAND, *PSYSDBG_COMMAND;
    我們這裡要讀取內核內存,所以參數ControlCode應取值為SysDbgReadVirtualMemory。
當ControlCode取值為SysDbgReadVirtualMemory時,ZwSystemDebugControl函數的第4個參數和第5個參數被忽略,使用時傳入0即可。第二個參數InputBuffer是一個指向結構體_MEMORY_CHUNKS的指針,該結構體定義如下。
typedef struct _MEMORY_CHUNKS {
    ULONG Address; //內核內存地址指針(要讀的數據)
    PVOID Data; //用戶層內存地址指針(存放讀出的數據)
    ULONG Length; //讀取的長度
}MEMORY_CHUNKS, *PMEMORY_CHUNKS;
第三個參數InputBufferLength是_MEMORY_CHUNKS結構體的大小。使用sizeof運算符得到即可。
SysDbgReadVirtualMemory函數執行成功將返回0。否則返回錯誤代碼。
為了方便使用,我們可以封裝一個讀取內核內存的函數GetKernelMemory,實現如下:
#define SysDbgReadVirtualMemory 8
//定義ZwSystemDebugControl函數指針類型
typedef DWORD (WINAPI *ZWSYSTEMDEBUGCONTROL)(DWORD,PVOID,
DWORD,PVOID,DWORD,PVOID);
BOOL GetKernelMemory(PVOID pKernelAddr, PBYTE pBuffer, ULONG uLength)
{
    MEMORY_CHUNKS mc ;
    ULONG uReaded = 0;
    mc.Address=(ULONG)pKernelAddr; //內核內存地址
    mc.pData = pBuffer;//用戶層內存地址
    mc.Length = uLength; //讀取內存的長度
    ULONG st = -1 ;
  //獲得ZwSystemDebugControl函數地址
  ZWSYSTEMDEBUGCONTROL ZwSystemDebugControl = (ZWSYSTEMDEBUGCONTROL) GetProcAddress(
    GetModuleHandle("ntdll.dll"), "ZwSystemDebugControl");
  //讀取內核內存數據到用戶層
    st = ZwSystemDebugControl(SysDbgReadVirtualMemory, &mc, sizeof(mc), 0, 0, &uReaded);
    return st == 0;
}


    對於不同類型的句柄,其內核對象所屬內存對應的結構體不同,對於消息鉤子句柄,它的內核對象所屬內存對應的結構體實際上是_HOOK_INFO類型,其定義如下。
typedef struct _HOOK_INFO
{
  HANDLE hHandle; //鉤子的句柄
  DWORD Unknown1;
  PVOID Win32Thread; //一個指向win32k!_W32THREAD 結構體的指針
  PVOID Unknown2;
  PVOID SelfHook; //指向結構體的首地址
  PVOID NextHook; //指向下一個鉤子結構體
  int iHookType; //鉤子的類型。
  DWORD OffPfn; //鉤子函數的地址偏移,相對於所在模塊的偏移
  int iHookFlags; //鉤子標誌
  int iMod; //鉤子函數做在模塊的索引號碼,利用它可以得到模塊基址
  PVOID Win32ThreadHooked; //被鉤的線程結構指針
} HOOK_INFO,*PHOOK_INFO;
由上可以看出,得到鉤子內核對像數據後,該數據對應HOOK_INFO結構體信息。其中:
hHandle是鉤子句柄,使用它就可以卸載鉤子。
iHookType是鉤子的類型,消息鉤子類型定義如下。
typedef enum _HOOK_TYPE{
        MY_WH_MSGFILTER = -1,
        MY_WH_JOURNALRECORD = 0,
        MY_WH_JOURNALPLAYBACK = 1,
        MY_WH_KEYBOARD = 2,
        MY_WH_GETMESSAGE = 3,
        MY_WH_CALLWNDPROC = 4,
        MY_WH_CBT = 5,
        MY_WH_SYSMSGFILTER = 6,
        MY_WH_MOUSE = 7,
        MY_WH_HARDWARE =​​ 8,
        MY_WH_DEBUG = 9,
        MY_WH_SHELL = 10,
        MY_WH_FOREGROUNDIDLE = 11,
        MY_WH_CALLWNDPROCRET = 12,
        MY_WH_KEYBOARD_LL = 13,
        MY_WH_MOUSE_LL = 14
}HOOK_TYPE;
    OffPfn是鉤子回調函數的偏移地址,該偏移地址是相對於鉤子函數所在模塊基址的偏移。
   Win32Thread是指向_W32THREAD結構體的指針,通過這個結構體可以獲得鉤子所在進程ID和線程ID。該結構體定義如下。
typedef struct _W32THREAD
{
    PVOID pEThread ; //該指針用以獲得進程ID和線程ID
    ULONG RefCount ;
    ULONG ptlW32 ;
    ULONG pgdiDcattr ;
    ULONG pgdiBrushAttr ;
    ULONG pUMPDObjs ;
    ULONG pUMPDHeap ;
    ULONG dwEngAcquireCount ;
    ULONG pSemTable ;
    ULONG pUMPDObj ;
    PVOID ptl;
    PVOID ppi; //該指針用以獲得模塊基址
}W32THREAD, *PW32THREAD;
    _W32THREAD結構體第一個參數pEThread指向的內存偏移0x01EC處分別保存著進程ID和線程ID。注意pEThread指針指向的內存是內核內存。
   _W32THREAD結構體最後一個參數ppi指向的內存偏移0xA8處是所有模塊基址的地址表, _HOOK_INFO結構體的iMod成員就標識了本鉤子所屬模塊基址在此地址表中的位置。 (每個地址佔4個字節)所以通常使用ppi+0xa8+iMod*4定位模塊基址的地址。注意ppi指向的內存是內核內存。
    2、實現細節
    首先編寫程序枚舉消息鉤子句柄,需要得到GUI TABLE,它的地址實際上存儲於User32.dll的一個全局變量中,該模塊導出的函數UserRegisterWowHandlers將返回該全局變量的值。所以我們只要調用這個函數就能夠得到GUI TABLE。然而UserRegisterWowHandlers是一個未公開的函數,不確定它的函數原型,需要反彙編猜出它的原型。筆者反彙編後得到的原型如下。
typedef PSHAREDINFO (__stdcall *USERREGISTERWOWHANDLERS) (PBYTE ,PBYTE );
僅知道它兩個參數是兩個指針,但是不知道它的兩個參數的含義,所以我們無法構造出合理的參數。如果隨便構造參數傳進去又會導致user32.dll模塊發生錯誤。所以通過調用這個函數接收其返回值的方法就不能用了。再次反彙編該函數的實現可以看出,在不同操作系統下該函數的最後三行代碼如下。
2K系統:(5.0.2195.7032)
:77E3565D B880D2E477 mov eax, 77E4D280
:77E35662 C20800 ret 0008
XP系統:(5.1.2600.2180)
:77D535F5 B88000D777 mov eax, 77D70080
:77D535FA 5D pop ebp
:77D535FB C20800 ret 0008
2003系統:(5.2.3790.1830)
:77E514D9 B8C024E777 mov eax, 77E724C0
:77E514DE C9 leave
:77E514DF C2080000 ret 0008
可以看到共同點,該函數的倒數第三行代碼就是將保存GUI TABLE指針的全局變量值賦值給寄存器EAX,只要我們想辦法搜索到這個值即可。能夠看出無論是哪個版本的函數實現中,都有C20800代碼,含義是ret 0008。我們可以自UserRegisterWowHandlers函數的入口地址開始一直搜索到C20800,找到它以後再向前搜索B8指令,搜到以後B8指令後面的四個字節數據就是我們需要的數據。代碼如下。
//獲得UserRegisterWowHandlers函數的入口地址
DWORD UserRegisterWowHandlers = (DWORD) GetProcAddress(LoadLibrary("user32.dll"), "UserRegisterWowHandlers");
PSHAREDINFO pGUITable; //保存GUITable地址的指針
for(DWORD i=UserRegisterWowHandlers; i<UserRegisterWowHandlers+1000; i++)
{
  if((*(USHORT*)i==0x08c2)&&*(BYTE *)(i+2)== 0x00)
  { //已找到ret 0008指令,然後往回搜索B8
    for (int j=i; j>UserRegisterWowHandlers; j--)
    { //找到B8它後面四個字節保存的數值即為GUITable地址
      if (*(BYTE *)j == 0xB8)
      {
        pGUITable = (PSHAREDINFO)*(DWORD *)(j+1);
        break;
      }
    }break;
  }
}
    得到SHAREDINFO結構指針後,它的成員pServerInfo的成員cHandleEntries就是句柄的總個數,然後循環遍歷每一個句柄,找到屬於指定模塊的消息鉤子句柄。代碼如下。
int iHandleCount = pGUITable->pServerInfo->cHandleEntries;
HOOK_INFO HookInfo;
DWORD dwModuleBase;
struct TINFO
{
  DWORD dwProcessID;
  DWORD dwThreadID;
};
char cModuleName[256] = {0};
for (i=0; i<iHandleCount; i++)
{ //判斷句柄類型是否為消息鉤子句柄
  if (pGUITable->pHandleEntry[i].bType == TYPE_HOOK)
  {
    DWORD dwValue = (DWORD)pGUITable->pHandleEntry[i].pObject;
    //獲得消息鉤子內核對像數據
    GetKernelMemory(pGUITable->pHandleEntry[i].pObject, (BYTE *)&HookInfo, sizeof(HookInfo));
    W32THREAD w32thd;
    if( GetKernelMemory(HookInfo.pWin32Thread,(BYTE *)&w32thd , sizeof(w32thd)) )
    { //獲取鉤子函數所在模塊的基址
      if (!GetKernelMemory((PVOID)((ULONG)w32thd.ppi+0xA8+4*HookInfo.iMod),
        (BYTE *)&dwModuleBase, sizeof(dwModuleBase)))
      {
        continue;
      }
      TINFO tInfo;
      //獲取鉤子所屬進程ID和線程ID
      if (!GetKernelMemory((PVOID)((ULONG)w32thd.pEThread+0x1ec),
        (BYTE *)&tInfo, sizeof(tInfo)))
      {
        continue;
      }
      HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, tInfo.dwProcessID);
      if (hProcess == INVALID_HANDLE_VALUE)
      {
        continue;
      }
      //根據模塊基址,獲取鉤子函數所屬模塊的名稱
      if (GetModuleFileNameEx(hProcess, (HMODULE)dwModuleBase, cModuleName, 256))
      {
        OutputDebugString(cModuleName);
        OutputDebugString("\r\n");
      }
    }
  }
}

    利用上面的代碼就可以找到所屬病毒DLL的消息鉤子句柄,然後調用UnhookWindowsHookEx函數卸載這個消息鉤子就OK了​​。

    文章寫完了,寫點題外話。上述文章內容大部分摘自王倍昌的《計算機病毒揭秘與對抗》一書,該書已於2011年10月1日出版。該書揭秘了Windows系統下計算機病毒常用的技術和對抗技術。如:隱藏注入技術、SPI網絡劫持技術、DLL劫持技術、瀏覽器綁架技術、服務劫持技術、感染型病毒技術、加殼實現與靜態脫殼技術、反病毒虛擬機技術、主動防禦技術等。歡迎閱讀此書,並提出寶貴意見。關於此書的更多內容請查看www.safe163.com網站。

2011年10月11日星期二

[轉載]SEH分析筆記(X86篇)六


附錄2 《nt!__local_unwind2 的反彙編代碼》


代碼:
      kd> uf __local_unwind2
      nt!__local_unwind2 [d:\dnsrv\base\crts\crtw32\misc\i386\exsup.asm @ 205]:
        8087257b push ebx
        8087257c push esi
        8087257d push edi
        8087257e mov eax,dword ptr [esp+10h] ; eax = pExceptionRegistration
        80872582 push ebp
        80872583 push eax
        80872584 push 0FFFFFFFEh
        80872586 push offset nt!__unwind_handler (80872540)
        8087258b push dword ptr fs:[0]
        80872592 mov dword ptr fs:[0],esp ; __local_unwind2 自身也會構建_EXCEPTION_REGISTRATION

      nt!__local_unwind2+0x1e [d:\dnsrv\base\crts\crtw32\misc\i386\exsup.asm @ 222]:
> 80872599 mov eax,dword ptr [esp+24h] ; eax = pExceptionRegistration
: 8087259d mov ebx,dword ptr [eax+8] ; ebx = pExceptionRegistration->scopetable
: 808725a0 mov esi,dword ptr [eax+0Ch] ; esi = pExceptionRegistration->trylevel
: 808725a3 cmp esi,0FFFFFFFFh ; cmp pExceptionRegistration->trylevel, TRYLEVEL_NONE
:< 808725a6 je nt!_NLG_Return2+0x2 (808725dd)
::
:: nt!__local_unwind2+0x2d [d:\dnsrv\base\crts\crtw32\misc\i386\exsup.asm @ 228]:
:: 808725a8 cmp dword ptr [esp+28h],0FFFFFFFFh
::< 808725ad je nt!__local_unwind2+0x3a (808725b5)
:::
::: nt!__local_unwind2+0x34 [d:\dnsrv\base\crts\crtw32\misc\i386\exsup.asm @ 230]:
::: 808725af cmp esi,dword ptr [esp+28h]
:::< 808725b3 jbe nt!_NLG_Return2+0x2 (808725dd)
::::
:::: nt!__local_unwind2+0x3a [d:\dnsrv\base\crts\crtw32\misc\i386\exsup.asm @ 234]:
::>: 808725b5 lea esi,[esi+esi*2]
:: : 808725b8 mov ecx,dword ptr [ebx+esi*4] ; move ecx, [pExceptionRegistration->scopetable[i].previousTryLevel]
:: : 808725bb mov dword ptr [esp+8],ecx ; 這個esp+8 只寫沒讀,什麼情況?
:: : 808725bf mov dword ptr [eax+0Ch],ecx ; pExceptionRegistration->trylevel = ecx
:: : 808725c2 cmp dword ptr [ebx+esi*4+4],0 ; cmp pExceptionRegistration->scopetable->lpfnFilter, NULL
:: :< 808725c7 jne nt!_NLG_Return2 (808725db)
:: ::
:: :: nt!__local_unwind2+0x4e [d:\dnsrv\base\crts\crtw32\misc\i386\exsup.asm @ 243]:
:: :: ; RegistrationPointer->scopetable->lpfnFilter 等於NULL,即這裡是__try & __finally 的組合
:: :: 808725c9 push 101h
:: :: 808725ce mov eax,dword ptr [ebx+esi*4+8]
:: :: 808725d2 call nt!_NLG_Notify (80872617) ; 這個函數對理解SEH 不重要,可以暫時忽略
:: :: 808725d7 call dword ptr [ebx+esi*4+8] ; pExceptionRegistration->scopetable->lpfnHandler()
:: ::
:: :: nt!_NLG_Return2 [d:\dnsrv\base\crts\crtw32\misc\i386\exsup.asm @ 251]:
<: :> 808725db jmp nt!__local_unwind2+0x1e (80872599) ; 循環
 : :
 : : nt!_NLG_Return2+0x2 [d:\dnsrv\base\crts\crtw32\misc\i386\exsup.asm @ 253]:
 > > 808725dd pop dword ptr fs:[0]
        808725e4 add esp,10h
        808725e7 pop edi
        808725e8 pop esi
        808725e9 pop ebx
        808725ea ret

2011年10月10日星期一

[轉載]SEH分析筆記(X86篇)五


附錄1 《Ntfs!_except_handler3 的反彙編代碼》


代碼:
         kd> uf Ntfs!_except_handler3
         ;nt!_except_handler3 [d:\dnsrv\base\crts\crtw32\misc\i386\exsup3.asm @ 172]:
           80872c00 push ebp
           80872c01 mov ebp,esp
           80872c03 sub esp,8
           80872c06 push ebx
           80872c07 push esi
           80872c08 push edi
           80872c09 push ebp
           80872c0a cld
           80872c0b mov ebx,dword ptr [ebp+0Ch] ; pExceptionRegistration
           80872c0e mov eax,dword ptr [ebp+8] ; pExceptionRecord
           80872c11 test dword ptr [eax+4],6 ; test pExceptionRecord->ExceptionFlags, (EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND)
< 80872c18 jne nt!_except_handler3+0xc9 (80872cc9)
:
: ;nt!_except_handler3+0x1e [d:\dnsrv\base\crts\crtw32\misc\i386\exsup3.asm @ 202]:
: ; ebp-8 和ebp-4 是一個類型為PEXCEPTION_POINTERS 的結構體,稱之為l_ExceptionPointers
: 80872c1e mov dword ptr [ebp-8],eax ; l_ExceptionPointers->ExceptionRecord = pExceptionRecord
: 80872c21 mov eax,dword ptr [ebp+10h] ; eax = pContext
: 80872c24 mov dword ptr [ebp-4],eax ; l_ExceptionPointers->ContextRecord = pContext
: 80872c27 lea eax,[ebp-8] ; eax = lException
: 80872c2a mov dword ptr [ebx-4],eax ; ebx-4 指向pExceptionRegistration 所在棧上類型為PEXCEPTION_POINTERS 的變量
: ; 具體棧的構造形式請參考當時建立pExceptionRegistration 的代碼
: ; 這裡是賦值給該PEXCEPTION_POINTERS 變量,以提供給GetExceptionInformation 和GetExceptionCode 使用
: 80872c2d mov esi,dword ptr [ebx+0Ch] ; esi = pExceptionRegistration->trylevel
: 80872c30 mov edi,dword ptr [ebx+8] ; edi = pExceptionRegistration->scopetable
: 80872c33 push ebx
: 80872c34 call nt!_ValidateEH3RN (8087cde8)
: 80872c39 add esp,4
: 80872c3c or eax,eax
:< 80872c3e je nt!_except_handler3+0xbb (80872cbb)
::
:: ;nt!_except_handler3+0x40 [d:\dnsrv\base\crts\crtw32\misc\i386\exsup3.asm @ 218]:
:: > 80872c40 cmp esi,0FFFFFFFFh ; cmp pExceptionRegistration->trylevel, TRYLEVEL_NONE
::< : 80872c43 je nt!_except_handler3+0xc2 (80872cc2)
::: :
::: : ;nt!_except_handler3+0x45 [d:\dnsrv\base\crts\crtw32\misc\i386\exsup3.asm @ 220]:
::: : 80872c45 lea ecx,[esi+esi*2] ; esi *= 3; 下面要將eis*4,總共esi*12,這是因為scopetable_entry 大小是12
::: : 80872c48 mov eax,dword ptr [edi+ecx*4+4] ; eax = pExceptionRegistration->scopetable[i].lpfnFilter
::: : 80872c4c or eax,eax
:::< : 80872c4e je nt!_except_handler3+0xa9 (80872ca9) ; lpfnFilter 為NULL 則跳轉
:::: :
:::: : ;nt!_except_handler3+0x50 [d:\dnsrv\base\crts\crtw32\misc\i386\exsup3.asm @ 226]:
:::: : 80872c50 push esi
:::: : 80872c51 push ebp
:::: : 80872c52 lea ebp,[ebx+10h] ; ebp = pExceptionRegistration->_ebp
:::: : 80872c55 xor ebx,ebx
:::: : 80872c57 xor ecx,ecx
:::: : 80872c59 xor edx,edx
:::: : 80872c5b xor esi,esi
:::: : 80872c5d xor edi,edi
:::: : 80872c5f call eax ; pExceptionRegistration->scopetable[i].lpfnFilter()
:::: : 80872c61 pop ebp
:::: : 80872c62 pop esi
:::: : 80872c63 mov ebx,dword ptr [ebp+0Ch] ; ebx = pExceptionRegistration
:::: : 80872c66 or eax,eax
::::< : 80872c68 je nt!_except_handler3+0xa9 (80872ca9) ; EXCEPTION_CONTINUE_SEARCH
::::: :
::::: : ;nt!_except_handler3+0x6a [d:\dnsrv\base\crts\crtw32\misc\i386\exsup3.asm @ 245]:
::::: : ; 如果lpfnFilter 返回EXCEPTION_CONTINUE_EXECUTION,跳過下面的展開操作
:::::<: 80872c6a js nt!_except_handler3+0xb4 (80872cb4) ; EXCEPTION_CONTINUE_EXECUTION
:::::::
::::::: ;nt!_except_handler3+0x6c [d:\dnsrv\base\crts\crtw32\misc\i386\exsup3.asm @ 249]:
::::::: ; lpfnFilter 返回EXCEPTION_EXECUTE_HANDLER,開始展開
::::::: 80872c6c mov edi,dword ptr [ebx+8] ; edi = pExceptionRegistration->scopetable
::::::: 80872c6f push ebx
::::::: 80872c70 call nt!__global_unwind2 (80872520)
::::::: 80872c75 add esp,4
:::::::
::::::: 80872c78 lea ebp,[ebx+10h] ; ebp = pExceptionRegistration->_ebp
::::::: 80872c7b push esi ; 展開到當前trylevel 為止(不包含本scopetable_entry)
::::::: 80872c7c push ebx
::::::: 80872c7d call nt!__local_unwind2 (8087257b)
::::::: 80872c82 add esp,8
:::::::
::::::: 80872c85 lea ecx,[esi+esi*2]
::::::: 80872c88 push 1
::::::: 80872c8a mov eax,dword ptr [edi+ecx*4+8] ; pExceptionRegistration->scopetable[i].lpfnHandler
::::::: 80872c8e call nt!_NLG_Notify (80872617)
::::::: 80872c93 mov eax,dword ptr [edi+ecx*4] ;
::::::: 80872c96 mov dword ptr [ebx+0Ch],eax ; pExceptionRegistration->trylevel = RegistrationPointer->scopetable[i].previousTryLevel
::::::: 80872c99 mov eax,dword ptr [edi+ecx*4+8] ; pExceptionRegistration->scopetable[i].lpfnHandler
::::::: 80872c9d xor ebx,ebx
::::::: 80872c9f xor ecx,ecx
::::::: 80872ca1 xor edx,edx
::::::: 80872ca3 xor esi,esi
::::::: 80872ca5 xor edi,edi
::::::: 80872ca7 call eax ; pExceptionRegistration->scopetable[i].lpfnHandler(); 這裡不會返回的! !
:::::::
::::::: ;nt!_except_handler3+0xa9 [d:\dnsrv\base\crts\crtw32\misc\i386\exsup3.asm @ 285]:
::::::: ; 找到scopetable 中的下一個scopetable_entry,繼續循環
:::>>:: 80872ca9 mov edi,dword ptr [ebx+8]
::: :: 80872cac lea ecx,[esi+esi*2]
::: :: 80872caf mov esi,dword ptr [edi+ecx*4]
::: :< 80872cb2 jmp nt!_except_handler3+0x40 (80872c40)
::: :
::: : ;nt!_except_handler3+0xb4 [d:\dnsrv\base\crts\crtw32\misc\i386\exsup3.asm @ 291]:
::: > 80872cb4 mov eax,0 ; eax = ExceptionContinueExecution (0)
::: < 80872cb9 jmp nt!_except_handler3+0xde (80872cde)
::: :
::: : ;nt!_except_handler3+0xbb [d:\dnsrv\base\crts\crtw32\misc\i386\exsup3.asm @ 295]:
:>: : 80872cbb mov eax,dword ptr [ebp+8]
: : : 80872cbe or dword ptr [eax+4],8
: : :
: : : ;nt!_except_handler3+0xc2 [d:\dnsrv\base\crts\crtw32\misc\i386\exsup3.asm @ 298]:
: > : 80872cc2 mov eax,1 ; eax = ExceptionContinueSearch (1)
: :< 80872cc7 jmp nt!_except_handler3+0xde (80872cde)
: ::
> :: ;nt!_except_handler3+0xc9 [d:\dnsrv\base\crts\crtw32\misc\i386\exsup3.asm @ 302]:
      :: ; 設置了(EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND),開始展開
      :: 80872cc9 push ebp
      :: 80872cca lea ebp,[ebx+10h] ;ebp = pExceptionRegistration->_ebp
      :: 80872ccd push 0FFFFFFFFh
      :: 80872ccf push ebx
      :: 80872cd0 call nt!__local_unwind2 (8087257b)
      :: 80872cd5 add esp,8
      :: 80872cd8 pop ebp
      :: 80872cd9 mov eax,1 eax = ExceptionContinueSearch (1)
      ::
      :: ;nt!_except_handler3+0xde [d:\dnsrv\base\crts\crtw32\misc\i386\exsup3.asm @ 313]:
      >> 80872cde pop ebp
           80872cdf pop edi
           80872ce0 pop esi
           80872ce1 pop ebx
           80872ce2 mov esp,ebp
           80872ce4 pop ebp
           80872ce5 ret