显示标签为“脫殼”的博文。显示所有博文
显示标签为“脫殼”的博文。显示所有博文

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月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年9月22日星期四

[轉載]如何中斷Themida的MessageBox對話框


作 者: 鄧韜
時 間: 2011-09-19,14:46:07
鏈接: http://bbs.pediy.com/showthread.php?t=140298

原理:讀取系統DLL到分配​​的內存裡面去,然後定位到相關的API的代碼。
FARPROC GetApiAddr(BYTE *Base,DWORD Api)
參數一:我們分配的內存基址
參數二:函數地址減去模塊基址的差值
返回值:返回內存中得API的函數的地址
作用:對原來的系統API函數下斷點無效,因為我們已經吧全部系統DLL讀取到內存中定位到相關的代碼了,調用的是內存中得API函數


代碼:
#include <windows.h>
char Caption[]="Test";
char Text[]="MessageBoxA";
FARPROC  GetApiAddr(BYTE *Base,DWORD Api)
{
  DWORD VirtualSize;
  DWORD PhysicalAddr;
  DWORD RawSize;  
  FARPROC RET=NULL;
  PIMAGE_DOS_HEADER DosHeader=(PIMAGE_DOS_HEADER)Base;
  PIMAGE_NT_HEADERS NtHeader=(PIMAGE_NT_HEADERS)((DWORD)DosHeader+(DWORD)DosHeader->e_lfanew);
  PIMAGE_SECTION_HEADER SecHeader=(PIMAGE_SECTION_HEADER)((DWORD)NtHeader+sizeof(IMAGE_FILE_HEADER)+  
    NtHeader->FileHeader.SizeOfOptionalHeader+4);
  BYTE SecSum=NtHeader->FileHeader.NumberOfSections;
  while(SecSum)
  {
    PhysicalAddr=(DWORD)SecHeader->Misc.PhysicalAddress;
    VirtualSize=(DWORD)SecHeader->VirtualAddress;//>Misc.VirtualSize;
    PhysicalAddr+=VirtualSize;
    if(Api>=VirtualSize&&Api<=PhysicalAddr)
    {
      RawSize=SecHeader->PointerToRawData;
      RawSize-=VirtualSize;
      Base+=RawSize;
      Base+=Api;
    }
    SecHeader++;
    SecSum--;
  }
  RET=(FARPROC)Base;
  return RET;
}
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
  DWORD dwRead;
  char SystemPath[MAX_PATH]={0};
  HMODULE hMod=GetModuleHandleA("USER32.DLL");
  if(!hMod)
    hMod=LoadLibraryA("USER32.DLL");
  GetSystemDirectoryA(SystemPath,MAX_PATH);
  lstrcat(SystemPath,"\\user32.dll");
  HANDLE Handle=CreateFileA(SystemPath,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
  DWORD Size=GetFileSize(Handle,NULL);
  BYTE *Virtual=VirtualAlloc(NULL,Size,MEM_COMMIT,PAGE_READWRITE);
  VirtualLock(Virtual,Size);
  ReadFile(Handle,Virtual,Size,&dwRead,NULL);
  DWORD MeAddr=(DWORD)GetProcAddress(hMod,"MessageBoxA");
  MeAddr=MeAddr-(DWORD)hMod;
  DWORD Api=(DWORD)GetApiAddr(Virtual,MeAddr);
_asm{
    push 0
    lea eax,Caption
    push eax
    lea eax,Text
    push eax
    push 0
    call Api
  }
  return FALSE;
}

2011年9月14日星期三

[轉載]ACProtect1.41保護技術分析


作 者: 鄧韜
時 間: 2011-09-11,20:48:02
鏈接: http://bbs.pediy.com/showthread.php?t=139995

這個殼很老了,但是還是來分析下它所使用的保護技術,這個殼是使用加密解密三的SMC那章的保護技術,依次循環解碼,分幾個段,由當前段解碼加密後的字節碼,放到下一個執行段,當前段解碼執行完成後,執行下一段解出來的代碼,每段解碼代碼中包含了花指令,代碼變形,迷惑等等,當解壓到最後一段代碼的時候,這個殼就直接一個JMP跳到Oep了,我們脫這個殼分為兩個步驟,
第一步:清除花指令
第二部:查找迷惑變形代碼
我們首先清除花指令,花指令的清除比較簡單,比如下面這段代碼

代碼:
00405000 > 60 PUSHAD
00405001 EB 01 JMP SHORT 00405004 ; JMP SHORT 00405004
00405003 E8 0F810100 CALL 0041D117
00405008 0000 ADD BYTE PTR DS:[EAX],AL
那個JMP那裡跳到405004,這裡沒的405004,有405003,所以405003就是一個花指令了我們直接二進制編輯NOP掉,這樣依次循環下去,我們就能清除花指令了,實際上那個JMP指令也是無用的,我們第一段解碼代碼清除後總結下花指令的組合方式
花指令清除後,變形代碼的清除。
下面這段代碼模擬JMP指令
代碼:
00405016 /7E 03 JLE SHORT 0040501B
00405018 |7F 01 JG SHORT 0040501B
下面這段代碼清除花指令後可以直接NOP掉,因為他什麼作用都沒的,用來迷惑我們的
代碼:
0040502A 50 PUSH EAX
0040502B E8 01000000 CALL 00405031 ; 花指令,下指令的開始字節NOP掉
00405030 - 76 83 JBE SHORT 00404FB5
花指令後的代碼
0040502A 50 PUSH EAX ; 保存EAX的值
0040502B E8 01000000 CALL 00405031 ; Call會壓入下個地址到堆棧,esp-4
00405030 90 NOP
00405031 83C4 04 ADD ESP,4 ; 恢復堆棧,ESP+4
00405034 58 POP EAX ; 恢復EAX的值
下面這段代碼也是什麼作用都沒的,也是用來迷惑我們的

代碼:
00405036 E8 01000000 CALL 0040503C
0040503B - 7C 83 JL SHORT 00404FC0 ; 花指令
0040503D 04 24 ADD AL,24
清除後的代碼
00405036 E8 01000000 CALL 0040503C ; Call會壓入下個地址到堆棧,esp-4
0040503B 90 NOP ; 花指令
0040503C 830424 06 ADD DWORD PTR SS:[ESP],6 ; 下條指令的地址加上6,CALL的下一條指令到RETN剛好6字節,所以目標地址就是RETN後一條指令
00405040 C3 RETN
這種情況下的JMP指令也是可以NOP掉的

代碼:
00405043 /EB 01 JMP SHORT 00405046 ; 直接跳到XXXX地址,其下的代碼到目標地址之間的代碼是無效的
00405045 |90 NOP ; 花指令
無用代碼
代碼:
0040508B E8 01000000 CALL 00405091
00405090 90 NOP
00405091 83C4 04 ADD ESP,4
不可以NOP的代碼
代碼:
004050BD /0F83 05000000 JNB 004050C8 ; 不可NOP
004050C3 |66:81E2 73A2 AND DX,0A273
004050C8 \EB 01 JMP SHORT 004050CB ; 可以NOP
另一種情況
代碼:
00405101 /7C 03 JL SHORT 00405106
00405103 |7D 01 JGE SHORT 00405106 ; 模擬JMP,跳到下面的jmp
00405105 |90 NOP
00405106 \E9 06000000 JMP 00405111 ; 跳到另一個地址,其模擬JMP指令開始的地方到目標地址的上個指令都是無效
0040510B 46 INC ESI ; 無效
0040510C B8 6D9231A7 MOV EAX,A731926D ; 無效
這是我第一段的解碼指令清除後的代碼,
代碼:
00405000 > 60 PUSHAD
00405001 90 NOP ; JMP SHORT 00405004
00405002 90 NOP
00405003 90 NOP
00405004 0F81 01000000 JNO 0040500B
0040500A 41 INC ECX
0040500B 4A DEC EDX
0040500C F9 STC
0040500D F8 CLC
0040500E FC CLD
0040500F 66:8BF7 MOV SI,DI
00405012 46 INC ESI
00405013 66:8BF0 MOV SI,AX
00405016 7E 03 JLE SHORT 0040501B
00405018 7F 01 JG SHORT 0040501B
0040501A 90 NOP
0040501B 4A DEC EDX
0040501C 90 NOP
0040501D 90 NOP
0040501E 90 NOP
0040501F 0F8A 05000000 JPE 0040502A
00405025 BE 004176CF MOV ESI,CF764100
0040502A 90 NOP ; 保存EAX的值
0040502B 90 NOP ; Call會壓入下個地址到堆棧,esp-4
0040502C 90 NOP
0040502D 90 NOP
0040502E 90 NOP
0040502F 90 NOP
00405030 90 NOP
00405031 90 NOP ; 恢復堆棧,ESP+4
00405032 90 NOP
00405033 90 NOP
00405034 90 NOP ; 恢復EAX的值
00405035 46 INC ESI
00405036 90 NOP ; Call會壓入下個地址到堆棧,esp-4
00405037 90 NOP
00405038 90 NOP
00405039 90 NOP
0040503A 90 NOP
0040503B 90 NOP ; 花指令
0040503C 90 NOP ; 下條指令的地址加上6,CALL的下一條指令到RETN剛好6字節,所以目標地址就是RETN後一條指令
0040503D 90 NOP
0040503E 90 NOP
0040503F 90 NOP
00405040 90 NOP
00405041 8BF0 MOV ESI,EAX
00405043 90 NOP ; 直接跳到XXXX地址,其下的代碼到目標地址之間的代碼是無效的
00405044 90 NOP
00405045 90 NOP ; 花指令
00405046 87D6 XCHG ESI,EDX
00405048 90 NOP
00405049 90 NOP
0040504A 90 NOP
0040504B 90 NOP
0040504C 90 NOP
0040504D 90 NOP
0040504E 90 NOP
0040504F 90 NOP
00405050 90 NOP
00405051 90 NOP
00405052 90 NOP
00405053 33D7 XOR EDX,EDI
00405055 90 NOP
00405056 90 NOP
00405057 90 NOP
00405058 90 NOP
00405059 90 NOP
0040505A 90 NOP
0040505B 90 NOP
0040505C 90 NOP
0040505D 90 NOP
0040505E 90 NOP
0040505F 90 NOP
00405060 FC CLD
00405061 90 NOP
00405062 90 NOP
00405063 90 NOP
00405064 85EA TEST EDX,EBP
00405066 90 NOP ; JMP,直接跳到目標地址,其現在的代碼到目標之間的代碼直接NOP
00405067 90 NOP
00405068 90 NOP
00405069 90 NOP
0040506A 90 NOP ; 花指令
0040506B 77 02 JA SHORT 0040506F
0040506D 1BEB SBB EBP,EBX
0040506F 90 NOP
00405070 90 NOP
00405071 90 NOP
00405072 90 NOP
00405073 90 NOP
00405074 46 INC ESI
00405075 90 NOP
00405076 90 NOP
00405077 90 NOP
00405078 D3D6 RCL ESI,CL
0040507A 90 NOP
0040507B 90 NOP
0040507C 90 NOP
0040507D 90 NOP
0040507E 90 NOP
0040507F 90 NOP
00405080 90 NOP
00405081 90 NOP
00405082 90 NOP
00405083 90 NOP
00405084 90 NOP
00405085 81F5 E0CDFA12 XOR EBP,12FACDE0
0040508B 90 NOP
0040508C 90 NOP
0040508D 90 NOP
0040508E 90 NOP
0040508F 90 NOP
00405090 90 NOP
00405091 90 NOP
00405092 90 NOP
00405093 90 NOP
00405094 46 INC ESI
00405095 90 NOP
00405096 90 NOP
00405097 90 NOP
00405098 90 NOP
00405099 90 NOP
0040509A 90 NOP
0040509B 90 NOP
0040509C 90 NOP
0040509D 90 NOP
0040509E 90 NOP
0040509F 90 NOP
004050A0 87EA XCHG EDX,EBP
004050A2 90 NOP
004050A3 90 NOP
004050A4 90 NOP
004050A5 90 NOP
004050A6 90 NOP
004050A7 F9 STC
004050A8 90 NOP
004050A9 90 NOP
004050AA 90 NOP
004050AB 46 INC ESI
004050AC 90 NOP
004050AD 90 NOP
004050AE 90 NOP
004050AF 90 NOP
004050B0 90 NOP
004050B1 C1FA 75 SAR EDX,75
004050B4 90 NOP
004050B5 ​​90 NOP
004050B6 90 NOP
004050B7 90 NOP
004050B8 90 NOP
004050B9 90 NOP
004050BA 90 NOP
004050BB 90 NOP
004050BC 90 NOP
004050BD 0F83 05000000 JNB 004050C8 ; 不可NOP
004050C3 66:81E2 73A2 AND DX,0A273
004050C8 90 NOP ; 可以NOP
004050C9 90 NOP
004050CA 90 NOP
004050CB 66:8BD7 MOV DX,DI
004050CE 90 NOP
004050CF 90 NOP
004050D0 90 NOP
004050D1 90 NOP
004050D2 90 NOP
004050D3 90 NOP
004050D4 90 NOP
004050D5 90 NOP
004050D6 90 NOP
004050D7 66:8BD3 MOV DX,BX
004050DA 90 NOP
004050DB 90 NOP
004050DC 90 NOP
004050DD 66:D3CE ROR SI,CL
004050E0 90 NOP
004050E1 90 NOP
004050E2 90 NOP
004050E3 F9 STC
004050E4 68 C1514000 PUSH 004051C1
004050E9 7A 01 JPE SHORT 004050EC
004050EB 40 INC EAX
004050EC 5A POP EDX
004050ED 78 03 JS SHORT 004050F2
004050EF 79 01 JNS SHORT 004050F2
004050F1 90 NOP
004050F2 40 INC EAX
004050F3 BF 4047AA83 MOV EDI,83AA4740
004050F8 66:13F3 ADC SI,BX
004050FB 81EF 4AB300E0 SUB EDI,E000B34A
00405101 90 NOP
00405102 90 NOP
00405103 90 NOP ; 模擬JMP,跳到下面的jmp
00405104 90 NOP
00405105 90 NOP
00405106 90 NOP ; 跳到另一個地址,其模擬JMP指令開始的地方到目標地址的上個指令都是無效
00405107 90 NOP
00405108 90 NOP
00405109 90 NOP
0040510A 90 NOP
0040510B 90 NOP ; 無效
0040510C 90 NOP ; 無效
0040510D 90 NOP
0040510E 90 NOP
0040510F 90 NOP
00405110 90 NOP
00405111 BD F1933A56 MOV EBP,563A93F1
00405116 8BDF MOV EBX,EDI
00405118 81C5 576CC5A9 ADD EBP,A9C56C57
0040511E 90 NOP
0040511F 90 NOP
00405120 90 NOP
00405121 90 NOP
00405122 90 NOP
00405123 90 NOP
00405124 90 NOP
00405125 90 NOP
00405126 90 NOP
00405127 90 NOP
00405128 90 NOP
00405129 85F3 TEST EBX,ESI
0040512B 87C3 XCHG EBX,EAX
0040512D 8B0A MOV ECX,DWORD PTR DS:[EDX]
0040512F 90 NOP
00405130 90 NOP
00405131 90 NOP
00405132 90 NOP
00405133 90 NOP
00405134 66:8BDA MOV BX,DX
00405137 66:D3C6 ROL SI,CL
0040513A 03CF ADD ECX,EDI
0040513C 90 NOP
0040513D 90 NOP
0040513E 90 NOP
0040513F 90 NOP
00405140 90 NOP
00405141 90 NOP
00405142 90 NOP
00405143 90 NOP
00405144 90 NOP
00405145 90 NOP
00405146 90 NOP
00405147 43 INC EBX
00405148 66:D3CE ROR SI,CL
0040514B C1C1 08 ROL ECX,8
0040514E 90 NOP
0040514F 90 NOP
00405150 90 NOP
00405151 90 NOP
00405152 90 NOP
00405153 90 NOP
00405154 90 NOP
00405155 90 NOP
00405156 90 NOP
00405157 90 NOP
00405158 90 NOP
00405159 90 NOP
0040515A 90 NOP
0040515B 90 NOP
0040515C 90 NOP
0040515D 90 NOP
0040515E 90 NOP
0040515F 90 NOP
00405160 90 NOP
00405161 90 NOP
00405162 90 NOP
00405163 90 NOP
00405164 90 NOP
00405165 83EA FC SUB EDX,-4
00405168 030A ADD ECX,DWORD PTR DS:[EDX]
0040516A 83EA 04 SUB EDX,4
0040516D 90 NOP
0040516E 90 NOP
0040516F 90 NOP
00405170 90 NOP
00405171 90 NOP
00405172 90 NOP
00405173 90 NOP
00405174 90 NOP
00405175 90 NOP
00405176 46 INC ESI
00405177 890A MOV DWORD PTR DS:[EDX],ECX
00405179 90 NOP
0040517A 90 NOP
0040517B 90 NOP
0040517C 0F8A 04000000 JPE 00405186
00405182 66:B8 8BA0 MOV AX,0A08B
00405186 81EF 737F09E2 SUB EDI,E2097F73
0040518C 90 NOP
0040518D 90 NOP
0040518E 90 NOP
0040518F 90 NOP
00405190 90 NOP
00405191 66:BE 3699 MOV SI,9936
00405195 83C2 04 ADD EDX,4
00405198 90 NOP
00405199 90 NOP
0040519A 90 NOP
0040519B 90 NOP
0040519C 90 NOP
0040519D 90 NOP
0040519E 90 NOP
0040519F 90 NOP
004051A0 90 NOP
004051A1 90 NOP
004051A2 83C5 FF ADD EBP,-1
004051A5 ^ 0F85 82FFFFFF JNZ 0040512D ; 向上跳轉
下面總結花指令的方式,當有一個JMP指令跳到一個XXXX地址的時候,這個地址不管是不是花指令,其JMP指令到目標地址的上一條指令都是無效的,有模擬JMP指令的時候也是如此,比如Jg Jle指令這樣的代碼到目標地址的上一條指令之間的代碼都是無效的,我們這樣依次清除下去就能看到全部外殼段的代碼了,這段代碼比較長,是依次循環解碼的,就不依依給出了,下面說下脫殼的方法,當裝載進程序後,下一個硬件訪問(hr)斷點,其地址指向ESP​​,這裡是hr 0012FFA4,當我們斷點下好以後,按13次F9就間接跳到OEP了,
跳向OEP的代碼
代碼:
0041C105 61 POPAD
0041C106 EB 01 JMP SHORT 0041C109
0041C108 90 NOP
0041C109 - FF25 4BC14100 JMP DWORD PTR DS:[41C14B] ; GUI1.00401000
來到Oep後我們發現輸入表被加密了,看下代碼:

代碼:
00401000 6A 00 PUSH 0
00401002 E8 27000000 CALL 0040102E ; GetModuleHandleA
00401007 6A 00 PUSH 0
00401009 68 09304000 PUSH 00403009 ; ASCII "Test"
0040100E 68 00304000 PUSH 00403000 ; ASCII "UnpackMe"
00401013 6A 00 PUSH 0
00401015 E8 08000000 CALL 00401022 ; MessageBoxA
0040101A 6A 00 PUSH 0
0040101C E8 07000000 CALL 00401028 ; ExitProcess
00401021 CC INT3
00401022 - FF25 0C204000 JMP DWORD PTR DS:[40200C] ; JMP函數地址,這裡是外殼添加的代碼的地址
00401028 - FF25 04204000 JMP DWORD PTR DS:[402004] ; JMP函數地址,這裡是外殼添加的代碼的地址
0040102E - FF25 00204000 JMP DWORD PTR DS:[402000] ; JMP函數地址,這裡是外殼添加的代碼的地址
代碼:
0040501D 68 09C50F67 PUSH 670FC509
00405022 813424 48728F1B XOR DWORD PTR SS:[ESP],1B8F7248
00405029 C3 RETN ; 返回到7C80B741 (kernel32.GetModuleHandleA)
現在修復輸入表,我們直接把JMP那個地址的內容改為函數的地址,示例
代碼:
00401000 . 6A 00 PUSH 0 ; /pModule = NULL
00401002 . E8 27000000 CALL 0040102E ; \GetModuleHandleA
00401007 . 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
00401009 . 68 09304000 PUSH 00403009 ; |Title = "Test"
0040100E . 68 00304000 PUSH 00403000 ; |Text = "UnpackMe"
00401013 . 6A 00 PUSH 0 ; |hOwner = NULL
00401015 . E8 08000000 CALL 00401022 ; \MessageBoxA
0040101A . 6A 00 PUSH 0 ; /ExitCode = 0
0040101C . E8 07000000 CALL 00401028 ; \ExitProcess
00401021 CC INT3
00401022 $- FF25 0C204000 JMP DWORD PTR DS:[40200C] ; DS:[0040200C]=77D507EA (user32.MessageBoxA)
00401028 .- FF25 04204000 JMP DWORD PTR DS:[402004] ; DS:[00402004]=7C81CB12 (kernel32.ExitProcess)
0040102E $- FF25 00204000 JMP DWORD PTR DS:[402000] ; DS:[00402004]=7C81CB12 (kernel32.ExitProcess)
,然後使用LoadPE重建PE,修復成功,運行程序OK,分析完畢

2011年8月21日星期日

VProtect 調試記錄

近段時間調試了VProtect 2.06,基本上該找的都找到了,就是修復難,正好看見Unpack論壇有人發出一些訊息,如此我也把我的調試記錄發出來了.

VProtect 2.06的OEP查找不用那麼困難,只要一個函數GetSystemTimeAsFileTime,就行了
Found intermodular calls, item 510
Address=010070F1
Disassembly=CALL DWORD PTR DS:[<&KERNEL32.GetSystemTimeAsFileTime>]
Destination=kernel32.GetSystemTimeAsFileTime
010070F1 E8 B9570500 CALL NOTEPAD_.0105C8AF

之後再在code段下內存訪問斷點,執行幾下就到OEP了,

這個殼難度不是在找OEP,而是修復輸出表,他是把代碼放在一個動態分配的內存段中,其中有一個地方

010296F0 FF15 5C800101 CALL DWORD PTR DS:[<&KERNEL32.GetProcAddress>] ; kernel32.GetProcAddress

這個殼找OEP是不難滴,硬件HOOK是浮雲,修復IAT和暗樁才是重點

2011年8月5日星期五

PESpin 1.33全保護脫殼筆記

【文章作者】: Nerin
【加殼方式】: PESpin
【使用工具】: OllyIce PEID LordPE ImportRec
【操作平台】: Windows XP SP2
【作者聲明】: 只是感興趣,沒有其他目的。失誤之處敬請諸位大俠賜教!
-------------------------------------------------- ------------------------------
【詳細過程】
  PEID查殼,顯示為“PESpin 0.3x - 1.xx -> cyberbob”。雙擊文件運行,打開任務管理器發現有兩個進程,我們脫殼的第一步就是要去掉雙進程保護。用OllyIce載入文件,開始我們的脫殼之旅。
  單步F7幾次之後,發現了我們很熟悉的幾條指令:
  
代碼:
  0041C0D7 60 PUSHAD
  0041C0D8 E8 00000000 CALL UnPackMe.0041C0DD
  0041C0DD 8B1C24 MOV EBX,DWORD PTR SS:[ESP]
  0041C0E0 83C3 12 ADD EBX,12
  
  這裡我們使用ESP定律下一個硬件斷點,然後Ctrl+G輸入CreateMutexA,解除雙進程守護。我們在函數末尾的Retn 0C處下斷點,因為殼會判斷這個函數的頭部是否有斷點。運行之後我們Alt+F9返回,停在這裡:
  
  
代碼:
  0041FE1E 8985 6E6E4000 MOV DWORD PTR SS:[EBP+406E6E],EAX
  0041FE24 8D85 E2281F03 LEA EAX,DWORD PTR SS:[EBP+31F28E2]
  0041FE2A 2D FCCEDE02 SUB EAX,2DECEFC
  0041FE2F FF10 CALL DWORD PTR DS:[EAX]
  0041FE31 BB CA7DB9FE MOV EBX,FEB97DCA
  0041FE36 81EB 137DB9FE SUB EBX,FEB97D13
  0041FE3C 3BC3 CMP EAX,EBX
  0041FE3E 9C PUSHFD ★走到這裡將ZF標誌位改成1
  0041FE3F C12C24 06 SHR DWORD PTR SS:[ESP],6
  0041FE43 F71424 NOT DWORD PTR SS:[ESP]
  0041FE46 832424 01 AND DWORD PTR SS:[ESP],1
  0041FE4A 58 POP EAX
  0041FE4B 2BD2 SUB EDX,EDX
  0041FE4D BB BAE74D02 MOV EBX,24DE7BA
  0041FE52 81EB 86E74D02 SUB EBX,24DE786
  0041FE58 F7E3 MUL EBX
  0041FE5A 81CB FE12F40E OR EBX,0EF412FE
  0041FE60 8​​D8428 4E0E91ED LEA EAX,DWORD PTR DS:[EAX+EBP+ED910E4E]
  0041FE67 2D 179B50ED SUB EAX,ED509B17
  0041FE6C FFE0 JMP EAX ★父子進程的分水嶺
  
  經過0041FE6C處的跳轉指令之後,我們來到這裡:
  
代碼:
  0041FE85 F1 INT1
  0041FE86 E8 1C030000 CALL UnPackMe.004201A7
  0041FE8B 85C0 TEST EAX,EAX
  0041FE8D 75 23 JNZ SHORT UnPackMe.0041FEB2
  0041FE8F 8BC3 MOV EAX,EBX
  0041FE91 35 08001F0E XOR EAX,0E1F0008
  0041FE96 C3 RETN
  
  看堆棧顯示:
  
引用:
  0012FFA0 0041C51B RETURN to UnPackMe.0041C51B
  0012FFA4 7C930738 ntdll.7C930738
  
  我們直接到0041C51B新建EIP,同時注意修改ESP寄存器的值。
  
代碼:
  0041C51B B8 4AAC1C95 MOV EAX,951CAC4A ★此處新建EIP
  0041C520 2BC9 SUB ECX,ECX
  0041C522 83C9 15 OR ECX,15
  0041C525 0FA3C8 BT EAX,ECX
  0041C528 0F83 81000000 JNB UnPackMe.0041C5AF
  
  在這裡搜索二進製字符串“F1 87 DF”,找到這裡:
  
代碼:
  004207AD F1 INT1
  004207AE 87DF XCHG EDI,EBX
  004207B0 57 PUSH EDI
  004207B1 C3 RETN
  
  在004207AD這裡下一個斷點,shift+F9停下來,同樣看堆棧新建EIP,調整堆棧。
  
代碼:
  0041D6D4 /EB 04 JMP SHORT UnPackMe.0041D6DA ★新建EIP
  0041D6D6 |7A EB JPE SHORT UnPackMe.0041D6C3
  0041D6D8 |04 9A ADD AL,9A
  0041D6DA ^\EB FB JMP SHORT UnPackMe.0041D6D7
  0041D6DC FFF6 PUSH ESI
  
  到這裡我們就已經處理好了雙進程保護,下面開始處理API Redirection。 Ctrl+G輸入GetVersion,在函數頭部下一個硬件
  訪問斷點,然後F9找到對API進行重定向的地方。
  
代碼:
  0041CE25 F3:A4 REP MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
  0041CE27 8BC6 MOV EAX,ESI
  0041CE29 8BF7 MOV ESI,EDI
  0041CE2B 5F POP EDI
  
  走到這裡要注意了,快到關鍵的地方了,刪除硬件斷點,然後單步跟踪。
  
代碼:
  0041CD64 83FA 12 CMP EDX,12
  0041CD67 73 7B JNB SHORT UnPackMe.0041CDE4 ★直接跳過這裡的循環
  0041CD69 8B18 MOV EBX,DWORD PTR DS:[EAX]
  0041CD6B EB 07 JMP SHORT UnPackMe.0041CD74
  
  繼續F7往下走:
  
代碼:
  0041D08D /0F84 92000000 JE UnPackMe.0041D125 ★這裡的循環也直接跳
  0041D093 |47 INC EDI
  0041D094 |EB 01 JMP SHORT UnPackMe.0041D097
  
  來到這裡:
  
代碼:
  0041D1A0 8907 MOV DWORD PTR DS:[EDI],EAX ★這裡就是關鍵了,在這裡下一個硬件執行斷點
  0041D1A2 EB 02 JMP SHORT UnPackMe.0041D1A6
  0041D1A4 02F5 ADD DH,CH
  0041D1A6 F9 STC
  0041D1A7 72 08 JB SHORT UnPackMe.0041D1B1
  0041D1A9 73 0E JNB SHORT UnPackMe.0041D1B9
  0041D1AB - E9 83042417 JMP 1765D633
  0041D1B0 C3 RETN
  
  走到這裡我們看一下堆棧:
  
引用:
  $-34 > 7C8114BA kernel32.7C8114BA ★函數的地址
  $-30 > 0000000F ★被偷的opcode數量
  $-2C > 00000000
  $-28 > 00000007
  $-24 > 0041CDDC UnPackMe.0041CDDC
  $-20 > 0041E4AD UnPackMe.0041E4AD
  $-1C > 00407A81 UnPackMe.00407A81
  $-18 > 004076D8 UnPackMe.004076D8
  $-14 > 0012FF94
  $-10 > 0012FF94
  $-C > 00007060
  $-8 > 00D50180
  $-4 > 0041D07F UnPackMe.0041D07F
  $ ==> > 0041D8A8 UnPackMe.0041D8A8
  
  我們找一塊空的地方,打上我們的補丁代碼,這樣就繞過了殼的API重定向:
  
代碼:
  mov eax,dword ptr [esp-34]
  sub eax,dword ptr [esp-30]
  mov dword ptr [edi],eax
  jmp 0041D1A6
  
  API處理完之後,我們刪除那個硬件執行斷點,F9快速到達偽OEP處,單步跟踪來補上被偷的OEP代碼。
  
代碼:
  0041DCFA F7D2 NOT EDX ★來到這裡
  0041DCFC 39C2 CMP EDX,EAX
  0041DCFE F7C0 74E7F921 TEST EAX,21F9E774
  0041DD04 0FACC2 48 SHRD EDX,EAX,48 ; Shift constant out of range 1..31
  0041DD08 0FBDC8 BSR ECX,EAX
  0041DD0B C7C2 2431C7CD MOV EDX,CDC73124
  0041DD11 85C0 TEST EAX,EAX
  0041DD13 0FBAEA 31 BTS EDX,31
  0041DD17 F7D2 NOT EDX
  0041DD19 F7C1 25C4A65C TEST ECX,5CA6C425
  0041DD1F 3BD0 CMP EDX,EAX
  0041DD21 0FABC2 BTS EDX,EAX
  0041DD24 EB 01 JMP SHORT UnPackMe.0041DD27
  
  0041DD3C 55 PUSH EBP ★第一句被偷的代碼
  0041DD3D EB 01 JMP SHORT UnPackMe.0041DD40
  
  下面就單步跟踪,就不一一列舉過程了,大家可以自己動下手,這裡我就直接貼出來被偷的OEP代碼了:
  
引用:
  0041DD3C 55 PUSH EBP ; 1
  
  0041DD40 8BEC MOV EBP,ESP ; 2
  
  0041DD45 6A FF PUSH -1 ; 3
  
  push 00407190
  push 00403368
  
  0041DD62 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]
  
  0041DD6B 50 PUSH EAX
  
  0041DD6F 64:8925 00000000 MOV DWORD PTR FS:[0],ESP
  
  0041DD79 83EC 58 SUB ESP,58
  
  0041DD7F 53 PUSH EBX
  
  0041DD83 56 PUSH ESI
  
  0041DD87 57 PUSH EDI ; ntdll.7C930738
  
  0041DD8B 8965 E8 MOV DWORD PTR SS:[EBP-18],ESP
  
  0041DD91 FF15 D20E4200 CALL DWORD PTR DS:[420ED2] ;GetVersion
  
  0041DD9A 33D2 XOR EDX,EDX
  
  0041DD9F 8AD4 MOV DL,AH
  
  0041DDA4 8915 34AD4000 MOV DWORD PTR DS:[40AD34],EDX
  
  0041DDAD 8BC8 MOV ECX,EAX
  
  0041DDB2 81E1 FF000000 AND ECX,0FF
  
  0041DDBB 890D 30AD4000 MOV DWORD PTR DS:[40AD30],ECX
  
  0041DDC4 C1E1 08 SHL ECX,8
  
  二進制opcode如下:
  
  55 8B EC 6A FF 68 90 71 40 0​​0 68 68 33 40 0​​0 64 A1 00 00 00 00 50 64 89 25 00 00 00 00 83 EC 58
  53 56 57 89 65 E8 FF 15 D2 0E 42 00 33 D2 8A D4 89 15 34 AD 40 0​​0 8B C8 81 E1 FF 00 00 00 89 0D
  30 AD 40 0​​0 C1 E1 08
  
  補上之後直接在00402598處新建EIP即可。下面我們開始處理Code Redirection,往下隨便找一句看看:
  
代碼:
  004026A0 - E9 9FDBFFFF JMP UnPackMe.00400244
  004026A5 90 NOP
  004026A6 90 NOP
  004026A7 8DC0 LEA EAX,EAX ; Illegal use of register
  
  00400244 833D 20AD4000 01 CMP DWORD PTR DS:[40AD20],1
  0040024B - E9 57240000 JMP UnPackMe.004026A7
  
  我們記下00400244處的代碼,然後再補回去就好了,但是這裡的工作量比較大,所以我就用網上的一個腳本來修復了。修
  复完畢之後,我們用LordPE抓取鏡像(★記得糾正鏡像大小),打開ImportRec,填入OEP:2598,在空白處右鍵,選擇高
  級命令->獲取API調用,然後剪切掉無效的指針就可以得到一份完整的API了,直接修復dump。下面就要修復nanomites了,
  這個我們可以直接用工具進行修復。修復完畢之後雙擊脫殼文件,沒有反應,⊙﹏⊙b 我們重新載入脫殼文件,單步跟踪
  發現問題出現在這裡:
  
代碼:
  0040264F |. E8 2D030000 CALL tuowen_.00402981
  
  00402981 $- E9 AAD9FFFF JMP tuowen_.00400330
  00402986 90 NOP
  00402987 90 NOP
  
  00400330 0000 ADD BYTE PTR DS:[EAX],AL ★這裡看來是腳本沒有修復好,我們手工修復一下好了
  
這裡是我修復的方案,大家可以參考一下:
  
代碼:
  00400330 833D C8B34000 0>CMP DWORD PTR DS:[40B3C8],0
  00400337 - E9 18230000 JMP dumped_1.00402654
  
  這裡修復完畢之後保存,雙擊已經可以看到界面了,但是點擊OK按鈕之後會崩潰,下面我們就來修復SDK保護吧。 O(∩_∩)O~
  用OD載入,F9跑起來,然後點擊OK按鈕之後,發現造成崩潰的地方在這裡。
  
代碼:
  00401160 $​​ 55 PUSH EBP ★我們要修復這裡的代碼
  00401161 . 8BEC MOV EBP,ESP
  00401163 . 81EC 60020000 SUB ESP,260
  00401169 . 53 PUSH EBX
  0040116A . 56 PUSH ESI
  0040116B . 57 PUSH EDI
  0040116C . FF15 8F124200 CALL DWORD PTR DS:[42128F]
  00401172 . B2 1E MOV DL,1E
  00401174 . 57 PUSH EDI
  00401175 . 6F OUTS DX,DWORD PTR ES:[EDI] ; I/O command
  00401176 . DBBF 81D54987 FSTP TBYTE PTR DS:[EDI+8749D581]
  0040117C C5 DB C5
  0040117D . C8 08118B ENTER 1108,8B
  00401181 . C3 RETN
  
  我們帶殼調試源文件,然後看看對應的代碼應該是什麼樣子,然後再補上去就可以恢復那個按鈕的功能了。由於這裡只有
  一處,所以比較簡單:
  
代碼:
  00401160 55 PUSH EBP
  00401161 8BEC MOV EBP,ESP
  00401163 81EC 60020000 SUB ESP,260
  00401169 53 PUSH EBX
  0040116A 56 PUSH ESI
  0040116B 57 PUSH EDI
  0040116C FF15 8F124200 CALL DWORD PTR DS:[42128F]
  
  此時DS:[0042128F]=00D30000,看來源文件是跑到殼臨時申請的空間裡面去直接代碼解碼了,我們跟進去之後,把代碼記
  下來,然後找個空白的地方把代碼補上即可,下面是我自己補的代碼,貼出來僅供大家參考下O(∩_∩)O~:
  
代碼:
  00401160 $​​ 55 PUSH EBP
  00401161 . 8BEC MOV EBP,ESP
  00401163 . 81EC 60020000 SUB ESP,260
  00401169 . 53 PUSH EBX
  0040116A . 56 PUSH ESI
  0040116B . 57 PUSH EDI
  0040116C . 90 NOP
  0040116D . E8 CC650000 CALL dumped_2.0040773E ★到空白的地方去解碼
  
  
  0040773E 9C PUSHFD
  0040773F 60 PUSHAD
  00407740 8B4424 24 MOV EAX,DWORD PTR SS:[ESP+24]
  00407744 8B08 MOV ECX,DWORD PTR DS:[EAX]
  00407746 8D78 04 LEA EDI,DWORD PTR DS:[EAX+4]
  00407749 897C24 24 MOV DWORD PTR SS:[ESP+24],EDI
  0040774D 81E9 BC1D576F SUB ECX,6F571DBC
  00407753 FC CLD
  00407754 8A07 MOV AL,BYTE PTR DS:[EDI]
  00407756 2C B2 SUB AL,0B2
  00407758 F9 STC
  00407759 32C1 XOR AL,CL
  0040775B F8 CLC
  0040775C 34 98 XOR AL,98
  0040775E C0C0 CE ROL AL,0CE ; Shift constant out of range 1..31
  00407761 34 41 XOR AL,41
  00407763 C0C8 06 ROR AL,6
  00407766 2AC1 SUB AL,CL
  00407768 34 EF XOR AL,0EF
  0040776A C0C8 9E ROR AL,9E ; Shift constant out of range 1..31
  0040776D 32C1 XOR AL,CL
  0040776F C0C8 8B ROR AL,8B ; Shift constant out of range 1..31
  00407772 02C1 ADD AL,CL
  00407774 04 F6 ADD AL,0F6
  00407776 04 6B ADD AL,6B
  00407778 AA STOS BYTE PTR ES:[EDI]
  00407779 49 DEC ECX
  0040777A ^ 75 D8 JNZ SHORT dumped_2.00407754
  0040777C 61 POPAD
  0040777D 9D POPFD
  0040777E C3 RETN
  
  補上這裡的代碼之後保存,再次打開發現OK按鈕的功能也正常了,PEID再掃描下,顯示“Microsoft Visual C++ 6.0”看
  來我們已經成功脫殼了( ⊙o⊙ )
  
-------------------------------------------------- ------------------------------
【經驗總結】
  脫這個殼的方法總得來說還是比較傳統的,如果能跟踪一下殼的流程,相信能學到不少東西。這篇文章是匆忙寫成的,有錯誤的地方希望大家能指正。另外我這裡修復SDK的方法還不能夠跨平台,如果有哪位大俠有更好的方法的話,希望能夠給出給我們學習學習。

2011年8月3日星期三

IDA + Bochs 調試器插件進行PE+ 格式DLL脫殼

By :obaby 

在IDA Pro6.1中我們擴展了Bochs調試器插件,現在已經可以進行64位代碼段的調試。在IDA Pro 6.2版本中將有可能實現PE+ 可執行程序的動態調試。由於程序將會在Bochs系統中執行,因而在調試的過程中我們並不需要實際的64位操作系統,因而在實際的調試過程中可以從任何的32位或者64位的Linux,Mac OS 或者Windows操作系統中使用IDA Pro進行64位可執行文件的調試。
為了確認這一項新的功能,我們將進行PE+格式的一個木馬程序進行脫殼並且進行一個大體的分析,這個文件是由MATCODE Software公司的mpress進行壓縮的。我們將會對講解DLL文件脫殼,修復輸入表並且最終修復數據庫來進行分析。

Unpacking the DLL
我們的目標文件是一個木馬的DLL文件,該文件被殺軟識別為“Win32/Giku”。我們從使用idaq64載入DLL文件開始進行分析,載入之後按Ctrl+S鍵打開區段窗口:

打開區段窗口之後注意觀察區段的名稱和mpress壓縮殼設置的區段的屬性。
為了進行DLL文件調試需要確保在啟動之前已經設置調試器的選項設置(“Bochs debugger plugin”)為PE 和64bit emulation 模式。

在啟動調試器之後,注意觀察下面的代碼段,在這段代碼中調用了unpack()函數:

如果我們繼續單步執行到更遠的地方我們將會到達修復輸入表的代碼處,為了實現輸入表的修復程序將會循環調用LoadLibrary()/GetModuleHandle()函數並且在這個循環中會包含另外的一個子循環調用GetProcAddress()。 Mpress外殼通過這兩層循環來實現IAT修復:

在stosq執行之後我們將可以從rdi寄存器中得到IAT結構的起始地址,同樣在兩層循環全部結束之後我們可以從rdi寄存器中得到IAT結構的結束地址。
在IAT修復之後不遠的地方我們可以找到一個跳轉到原始入口點的jmp代碼:

程序的入口點代碼如下所示:

這裡就是脫殼之後的程序的真實的DllEntryPoint()函數了。現在有了程序的OEP和IAT結構的起始/結束地址,我們就可以清空數據庫並且重現脫殼之後的程序了。
Reconstructing and cleaning the database
到這裡有許多的辦法在程序脫殼之後進行清理數據庫清理.通常會包含如下幾步:
1. 定位IAT並且創建一個額外的區段來重現程序的輸入表;
2. 刪除外殼代碼的入口點,並且添加脫殼之後程序的原始入口點OEP;
3. 重新分析代碼;
4. 重新加載FLIRT特徵庫
5. 刪除無用的外殼區段(可選)
其中第一步到第三步可以通過IDA的uunp插件來自動完成,執行菜單中的“Edit/Plugins/Universal unpacker manual reconstruct”即運行該插件:

在插件中填入通過上面的操作得到的數據即可:

點擊確定之後一個新的區段將會被創建,並且代碼段將會被重新分析,在分析完成之後脫殼之後的程序的一個內存快照將會被呈現出來。
現在我們就可以重新引用FLIRT特徵庫了:

在選擇“vc64rtf”簽名(shi​​ft +F5)之後我們可以看到IDA已經成功的識別出了庫函數並且對這些庫函數使用淺藍色進行了標記,這樣可以使得後續的分析工作變得更加簡單。
Analyzing the unpacked code
在代碼解壓之後我們可以通過String Window窗口進行一個快速分析,String Window窗口呈現了一些加密的字符串:

通過交叉引用,我們可以定位到解密函數。在給函數適當的參數之後我們可以直接通過Appcall來解密這些字符串:

我們得到了一個指向加密的文本文件的URL。在深入挖掘之後我們定位到了解密文件的函數:

下面是Appcall版本的decrypt_file()函數:

我們使用這個函數就可以解密spm.txt文件了,解密之後的內容如下所示:

X32.jpg是一個upx壓縮的DLL文件,x64.jpg是一個mpress壓縮的PE+格式的Dll文件。

2011年7月30日星期六

新手補給之脫殼+破解—— HwndSpy v1.9(Armadillo)


作 者: rocktx
時 間: 2010-06-03,22:06:42 
鏈接: http://bbs.pediy.com/showthread.php?t=114458 
【文章標題】: 新手補給之脫殼+破解—— HwndSpy v1.9(Armadillo) 
【文章作者】: rocktx 
【軟件名稱】: HwndSpy 
【軟件大小】: 920KB 
【下載地址】: http://www.highplains.net/public/HPSHwndSpy.exe 
【加殼方式】: Armadillo 
【保護方式】: Armadillo 
【編寫語言】: VC6 
【操作平台】: WinXP SP3 
【軟件介紹】: 簡直就是 SpyXX 加強版 
【作者聲明】: 拙作一篇,獻給像我一樣的精品菜鳥,好好學習,天天向上! -------------------------------------------------- ------------------------------
【詳細過程】 
  
  
  
  
一、脫殼
  
  
保護方式:
  
!- Protected Armadillo
  
Protection system (Professional)
  
!- <Protection Options>
  
Debug-Blocker
  
CopyMem-II
  
Enable Import Table Elimination
  
Enable Memory-Patching Protections
  
!- <Backup Key Options>
  
Variable Backup Keys
  
!- <Compression Options>
  
Better/Slower Compression
  
!- <Other Options>
  
!- 版本號: 4.10 08Apr2005
  
!- Elapsed Time 00h 00m 12s 734ms
  
  
重點是前三項保護方式:
  
Debug-Blocker
  
CopyMem-II
  
Enable Import Table Elimination
  
  
準備工具:
  
OD (with HideOD 插件) ——— 插件用於AntiAntiDebug
  
ArmaDetach 1.3 —————— 主要功臣,大大加快脫殼進度,有時候在處理Debug-Blocker 時,1.1 版會比較穩定
  
LordPE —————————— 處理PE 模塊
  
ImportRec 1.6 or 1.7 —— 處理IAT
  
ArmInline 0.96 —————— 處理IAT 亂序
  
FixRes —————————— 處理資源
  
ODbgScript plugin v1.47 —— OD腳本插件,一般使用1.47 或以上的版本
  
CFF Explorer —————— 又一款PE 編輯器,可以不用,純粹個人喜好
  
  
準備腳本(將下面的文本保存為.osc 或者.txt 文件):
  
////////////////////////////////////////////////// //////////////////////////////////////////////////
  
//轉單進程腳本
  
msg "請忽略所有異常,並添加忽略C000001E異常,然後運行本腳本!"
  
gpa "OpenMutexA","kernel32.dll"
  
bp $RESULT
  
esto
  
exec
  
pushad
  
pushfd
  
push edx
  
xor eax,eax
  
push eax
  
push eax
  
call kernel32.CreateMutexA
  
popfd
  
popad
  
jmp kernel32.OpenMutexA
  
ende
  
bc eip
  
msg "現已轉換成單進程!"
  
ret
  
////////////////////////////////////////////////// //////////////////////////////////////////////////
  
  
首先用HideOD 隱藏好OD,然後忽略所有異常,如果實在搞不定,可以用看雪的OllyICE;
  
  
下面開始流水作業(考慮到篇幅和精力,本人未啟用完全手脫功能);
  
  
  
1、尋找OEP
  
  
打開ArmaDetach v1.3,選擇CopyMem-II 模式,拖入HwndSpy.exe 文件,顯示信息
  
-------------------------------------
  
Filename: HwndSpy.exe
  
Parent process iD: [000004BC]
  
Processing...
  
[PROTECTiON S​​YSTEM]
  
Professional Edition
  
[PROTECTiON OPTiONS]
  
Debug-Blocker protection detected
  
CopyMem-II protection detected
  
Memory-Patching Protections enabled
  
Import Table Elimination enabled
  
[CHiLD iNFO]
  
Crypto call found: [0049BA76]
  
Child process iD: [00000D9C] // 進程ID
  
Entry point: [0042A2C6] // OEP
  
Original bytes: [558BEC6A] // OEP 處4個字節值
  
Detached successfully :)
  
-------------------------------------
  
打開OD,附加進程00000D9C 後Alt+F9,會停在OEP,還原上面的4個字節值後:
  
0042A2C6 55 push ebp
  
0042A2C7 8BEC mov ebp,esp
  
0042A2C9 6A FF push -1
  
0042A2CB 68 F8424500 push HwndSpy.004542F8
  
0042A2D0 68 B8914200 push HwndSpy.004291B8
  
0042A2D5 64:A1 00000000 mov eax,dword ptr fs:[0]
  
0042A2DB 50 push eax
  
  
  
2、處理IAT
  
  
為避免功虧一簣,這裡推薦使用ArmaDetach v1.1,拖入HwndSpy.exe 文件後顯示信息
  
-------------------------------------
  
DONE!
  
Child process ID: 0000092C // 進程ID
  
Entry point: 004BD000 // 殼的入口
  
Original bytes: 60E8 // 殼的入口處頭2個字節值
  
-------------------------------------
  
再打開一個OD,附加進程00000B54,Alt+F9返回並還原上面的2個字節到入口:
  
004BD000 > 60 pushad
  
004BD001 E8 00000000 call HwndSpy.004BD006
  
004BD006 5D pop ebp
  
004BD007 50 push eax
  
004BD008 51 push ecx
  
004BD009 0FCA bswap edx
  
004BD00B F7D2 not edx
  
004BD00D 9C pushfd
  
  
用ODbgScript v1.47 載入上面保存的腳本文件,程序會自動執行,將進程由雙變單;
  
然後he GetModuleHandleA,Shift + F9一直運行,注意觀察堆棧窗口,直到依次出現:
  
00127A6C 00D752BA /CALL 到GetModuleHandleA 來自00D752B4
  
00127A70 00D88BAC \pModule = "kernel32.dll"
  
00127A74 00D89CC4 ASCII "VirtualAlloc"
  
...
  
00127A6C 00D752D7 /CALL 到GetModuleHandleA 來自00D752D1
  
00127A70 00D88BAC \pModule = "kernel32.dll"
  
00127A74 00D89CB8 ASCII "VirtualFree"
  
...
  
001277D0 00D64F0D /CALL 到GetModuleHandleA 來自00D64F07
  
001277D4 00127920 \pModule = "kernel32.dll"
  
001277D8 00000000
  
  
取消斷點,Alt+F9返回到
  
00D64F0D 8B0D AC0DD900 mov ecx,dword ptr ds:[D90DAC]
  
00D64F13 89040E mov dword ptr ds:[esi+ecx],eax
  
00D64F16 A1 AC0DD900 mov eax,dword ptr ds:[D90DAC]
  
00D64F1B 391C06 cmp dword ptr ds:[esi+eax],ebx
  
00D64F1E 75 16 jnz short 00D64F36
  
00D64F20 8D85 B4FEFFFF lea eax,dword ptr ss:[ebp-14C]
  
00D64F26 50 push eax
  
00D64F27 FF15 B432D800 call dword ptr ds:[D832B4] ; kernel32.LoadLibraryA
  
00D64F2D 8B0D AC0DD900 mov ecx,dword ptr ds:[D90DAC]
  
00D64F33 89040E mov dword ptr ds:[esi+ecx],eax
  
00D64F36 A1 AC0DD900 mov eax,dword ptr ds:[D90DAC]
  
00D64F3B 391C06 cmp dword ptr ds:[esi+eax],ebx
  
00D64F3E 0F84 2F010000 je 00D65073 ; Magic jump,改成jmp 後,Enter 跟隨
  
  
來到
  
00D65073 83C7 0C add edi,0C
  
00D65076 89BD 78FDFFFF mov dword ptr ss:[ebp-288],edi
  
00D6507C 83C6 04 add esi,4
  
00D6507F 395F FC cmp dword ptr ds:[edi-4],ebx
  
00D65082 ^ 0F85 49FEFFFF jnz 00D64ED1
  
00D65088 EB 03 jmp short 00D6508D ; 這裡F4
  
00D6508A D6 salc
  
00D6508B D6 salc
  
00D6508C 8F ??? ; 未知命令
  
  
恢復上面的修改,在內存鏡像的.text 段F2下斷,Shift+F9運行後斷下,IAT解碼完畢;
  
  
現在回到前一個OD,右鍵查找模塊調用(Search for all intermodular calls),在結果中隨便點一個已解碼函數調用,回車後跟隨IAT地址,比如到
  
00F21748 7C80E87C kernel32.FileTimeToSystemTime
  
00F2174C 7C80E8F6 kernel32.FileTimeToLocalFileTime
  
00F21750 773D7E70 COMCTL32.CreatePropertySheetPageA
  
  
隨便複製幾個字節的二進制碼,到後一個OD 的內存鏡像中查找該值,會找到
  
00F312CC 020C0119
  
00F312D0 77F18BEE GDI32.SaveDC
  
00F312D4 7E41945D USER32.GetWindowLongA
  
00F312D8 00D6623E
  
00F312DC 7E42D312 USER32.DestroyIcon
  
00F312E0 7E42D312 USER32.DestroyIcon
  
...
  
00F31BF0 77F1E9BE GDI32.Rectangle
  
00F31BF4 7E4186C7 USER32.GetDC
  
00F31BF8 77F1DCFF GDI32.GetTextExtentPointA
  
00F31BFC 00000000
  
  
將這些已完全解碼的IAT 指針值,用二進制方式複制並粘貼到前一個OD的IAT中,注意不要錯位;
  
  
然後打開ArmInline,載入前一個OD,在輸入表亂序選項卡中填寫:
  
New base va of IAT = 0046D000
  
Length of existing IAT = 1000 (AmrInline 提供的值通常會偏小)
  
  
最後點Rebase IAT,輸入表就移動到0046D000 處了;
  
當然,如果不想用ArmInline,可以試試ImportRec的重建IAT功能;
  
  
3、dump 內存鏡像,用ImportRec 恢復一下IAT,脫殼完畢;
  
  
  
  
二、破解:
  
Armadillo 殼的破解起來比較簡單,主要用到兩個函數:LoadLibrary、GetEnvironmentVariable,對於某些版本可以
  
直接修改驗證段的返回值,但是如果程序在多處有檢測,逐一修改不僅麻煩,還有可能會錯過暗樁的處理;
  
  
  
1、bp LoadLibraryA
  
  
Armadillo 在驗證時,一般會先LoadLibrary("Armaccess.dll"),憑該函數斷點,很容易找到關鍵代碼:
  
004145A6 8D85 30FFFFFF lea eax,dword ptr ss:[ebp-D0]
  
004145AC 50 push eax
  
004145AD FF15 14D24600 call dword ptr ds:[<&kernel32.LoadLibraryA>] ; kernel32.LoadLibraryA
  
004145B3 8BF8 mov edi,eax
  
004145B5 3BFB cmp edi,ebx
  
004145B7 897D F8 mov dword ptr ss:[ebp-8],edi
  
004145BA 75 09 jnz short 004145C5 ; 成功載入就會跳走
  
004145BC 5F pop edi
  
004145BD 5E pop esi
  
004145BE 33C0 xor eax,eax ; 否則返回0,所以要讓它返回1
  
004145C0 5B pop ebx
  
004145C1 8BE5 mov esp,ebp
  
004145C3 5D pop ebp
  
004145C4 C3 retn
  
  
往上到段首
  
00414550 55 push ebp
  
00414551 8BEC mov ebp,esp
  
00414553 81EC 98010000 sub esp,198
  
00414559 53 push ebx
  
0041455A 56 push esi
  
0041455B 57 push edi
  
  
改為
  
00414550 33C0 xor eax,eax
  
00414552 40 inc eax
  
00414553 C3 retn
  
  
  
2、bp GetEnvironmentVariableA
  
  
Armadillo 會使用該函數獲取一些環境變量,也就是註冊信息,對於本程序,要檢測的變量為:
  
  
HPSKEYTYPE、USESLEFT、HPSMAXDAYS、DAYSINSTALLED、USERKEY、USERNAME、HPSVERSION、HPSPRODUCTID
  
  
當然前面還有個FIRSTRUN,這些變量名是用GetEnvironmentVariableA 斷點跟踪獲得的,本程序的調用方式如下:
  
-------------------------------------------------- ---------
  
typedef DWORD (WINAPI * PGENV)(LPCTSTR, LPTSTR, DWORD);
  
HMODULE hDLL = LoadLibrary("kernel32.dll");
  
// hDLL = GetModuleHandle("kernel32.dll");
  
if (hDLL)
  
{
    
PGENV getenv = (PGENV)GetProcAddress(hDLL, "GetEnvironmentVariableA");
    
if (getenv)
    
{
      
getenv("USERKEY", szKey, sizeof(szKey) / sizeof(TCHAR));
      
// 檢查 szKey 值
    
}
  
}
  
-------------------------------------------------- ---------
  
下面是找到的驗證過程函數之一:
  
00401A50 64:A1 00000000 mov eax,dword ptr fs:[0]
  
00401A56 6A FF push -1
  
00401A58 68 2DA04400 push 0044A02D
  
00401A5D 50 push eax
  
00401A5E 64:8925 00000000 mov dword ptr fs:[0],esp
  
00401A65 81EC E0030000 sub esp,3E0
  
00401A6B 8D4424 18 lea eax,dword ptr ss:[esp+18]
  
00401A6F 50 push eax
  
00401A70 E8 4BEB0000 call 004105C0 ; 這裡開始Patch,改成jmp 0044D150
  
00401A75 83C4 04 add esp,4
  
00401A78 85C0 test eax,eax
  
00401A7A 0F84 47090000 je 004023C7
  
  
Patch代碼:
  
0044D150 60 pushad
  
0044D151 E8 00000000 call 0044D156
  
0044D156 5F pop edi
  
0044D157 83EF 06 sub edi,6
  
0044D15A 83C7 50 add edi,50
  
0044D15D 33C0 xor eax,eax
  
0044D15F 66:8B07 mov ax,word ptr ds:[edi]
  
0044D162 66:85C0 test ax,ax
  
0044D165 74 13 je short 0044D17A
  
0044D167 03C7 add eax,edi
  
0044D169 50 push eax
  
0044D16A 66:0347 02 add ax,word ptr ds:[edi+2]
  
0044D16E 50 push eax
  
0044D16F FF15 C8D34600 call dword ptr ds:[46D3C8] ; kernel32.SetEnvironmentVariableA
  
0044D175 83C7 04 add edi,4
  
0044D178 ^ EB E3 jmp short 0044D15D
  
0044D17A 61 popad
  
0044D17B E8 4034FCFF call 004105C0
  
0044D180 ^ E9 F048FBFF jmp 00401A75
  
  
二進制:
  
60 E8 00 00 00 00 5F 83 EF 06 83 C7 50 33 C0 66 8B 07 66 85 C0 74 13 03 C7 50 66 03 47 02 50 FF
  
15 C8 D3 46 00 83 C7 04 EB E3 61 E8 40 34 FC FF E9 F0 48 FB FF
  
  
數據部分:
  
0044D1A0 00040060
  
0044D1A4 0004006C
  
0044D1A8 00040078
  
0044D1AC 00040084
  
0044D1B0 00380094
  
0044D1B4 000800D4
  
0044D1B8 000400E8
  
0044D1BC 000800F4
  
  
二進制:
  
60 00 04 00 6C 00 04 00 78 00 04 00 84 00 04 00 94 00 38 00 D4 00 08 00 E8 00 04 00 F4 00 08 00
  
  
...
  
0044D200 00000034 4...
  
0044D204 4B535048 HPSK
  
0044D208 59545945 EYTY
  
0044D20C 00004550 PE..
  
0044D210 00000031 1...
  
0044D214 53455355 USES
  
0044D218 5446454C LEFT
  
0044D21C 00000000 ....
  
0044D220 00000032 2...
  
0044D224 4D535048 HPSM
  
0044D228 41445841 AXDA
  
0044D22C 00005359 YS..
  
0044D230 00000030 0...
  
0044D234 53594144 DAYS
  
0044D238 54534E49 INST
  
0044D23C 454C4C41 ALLE
  
0044D240 00000044 D...
  
0044D244 31313131 1111
  
0044D248 31313131 1111
  
0044D24C 3232322D -222
  
0044D250 32323232 2222
  
0044D254 33332D32 2-33
  
0044D258 33333333 3333
  
0044D25C 342D3333 33-4
  
0044D260 34343434 4444
  
0044D264 2D343434 444-
  
0044D268 35353535 5555
  
0044D26C 35353535 5555
  
0044D270 3636362D -666
  
0044D274 36363636 6666
  
0044D278 00000036 6...
  
0044D27C 52455355 USER
  
0044D280 0059454B KEY.
  
0044D284 00000000 ....
  
0044D288 6B636F72 rock
  
0044D28C 00007874 tx..
  
0044D290 52455355 USER
  
0044D294 454D414E NAME
  
0044D298 00000000 ....
  
0044D29C 00000000 ....
  
0044D2A0 00000031 1...
  
0044D2A4 56535048 HPSV
  
0044D2A8 49535245 ERSI
  
0044D2AC 00004E4F ON..
  
0044D2B0 31303546 F501
  
0044D2B4 00000000 ....
  
0044D2B8 50535048 HPSP
  
0044D2BC 55444F52 RODU
  
0044D2C0 44495443 CTID
  
  
二進制:
  
34 00 00 00 48 50 53 4B 45 59 54 59 50 45 00 00 31 00 00 00 55 53 45 53 4C 45 46 54 00 00 00 00
  
32 00 00 00 48 50 53 4D 41 58 44 41 59 53 00 00 30 00 00 00 44 41 59 53 49 4E 53 54 41 4C 4C 45
  
44 00 00 00 31 31 31 31 31 31 31 31 2D 32 32 32 32 32 32 32 32 2D 33 33 33 33 33 33 33 33 2D 34
  
34 34 34 34 34 34 34 2D 35 35 35 35 35 35 35 35 2D 36 36 36 36 36 36 36 36 00 00 00 55 53 45 52
  
4B 45 59 00 00 00 00 00 72 6F 63 6B 74 78 00 00 55 53 45 52 4E 41 4D 45 00 00 00 00 00 00 00 00
  
31 00 00 00 48 50 53 56 45 52 53 49 4F 4E 00 00 46 35 30 31 00 00 00 00 48 50 53 50 52 4F 44 55
  
43 54 49 44
  
  
原理是調用SetEnvironmentVariableA 設置環境變量:
  
-------------------------------------------------- ------
  
HPSKEYTYPE = "4" (註冊碼類型,必須為4)
  
USESLEFT = "1"
  
HPSMAXDAYS = "2" (日期限制)
  
DAYSINSTALLED = "0"
  
USERKEY = "11111111-22222222-33333333-44444444-55555555-66666666" (註冊碼任意,長度區間[41, 100])
  
USERNAME = "rocktx" (用戶名任意,但不能是"DEFAULT",長度不大於100)
  
HPSVERSION = "1" (版本ID,即:1.9版,是程序之前用GetFileVersion 獲得的)
  
HPSPRODUCTID = "F501"
  
-------------------------------------------------- ------
  
  
最後要導入註冊表(要和環境變量相匹配):
  
-------------------------------------------------- ------
  
Windows Registry Editor Version 5.00
  
  
[HKEY_CURRENT_USER\Software\High Plains Software\HPS HwndSpy\User]
  
"UserName"="rocktx"
  
"User"="rocktx"
  
"RegistrationKey"="11111111-22222222-33333333-44444444-55555555-66666666"
  
-------------------------------------------------- ------
  
  
  
  
三、優化
  
  
1、挪動IAT (這一步可以合併到脫殼階段)
  
  
對於一般的VC 程序,IAT是在.rdata 區段,以Notepad98.exe 為例,用L​​ordPE 查看輸入表在00406000 處,在OD中:
  
00406000 000061B4
  
00406004 FFFFFFFF
  
00406008 FFFFFFFF
  
0040600C 00006592 // 這裡就是第一個模塊shell32.dll 的偏移,查看00406592 就會看到
  
00406010 000063F8
  
  
現在回到HwndSpy 的.rdata段,右鍵查找字符串:kernel32.dll,找到
  
0045D1BE 4E52454B KERN
  
0045D1C2 32334C45 EL32
  
0045D1C6 6C6C642E .dll
  
  
顯然,該模塊的偏移值為0005D1BE,繼續查找這個常量,來到
  
0045BFA8 00000000 // 這裡就是IAT的原始位置,RVA = 0005BFA8
  
0045BFAC 00000000
  
0045BFB0 00000000
  
0045BFB4 0005D1BE // 這裡指向kernel32.dll
  
0045BFB8 00000000
  
  
當然,kernel32.dll 不一定是第一個模塊,數據窗口中上下翻翻,自己確定一下。
  
  
從0045BFA8 到.rdata 段尾用00 清空(如果程序有導出表,注意不要一併清空了),將程序保存為一個副本0001.exe,以後的操作
  
都針對這個副本進行,打開ImportRec,選項中只勾選Create New IAT (重建IAT) ,獲取IAT 後還原到RVA 0005BFA8 處即可;
  
  
  
2、挪動資源
  
  
LordPE 查看.text1 區段的偏移為0006D000,下面的步驟就是將資源放到這裡:
  
a、用FixRes 打開0001.exe,NewRVA = 0006D000,FileAlignment = 1000,選擇導出路徑後,點Dump Resource;
  
b、用CFF Explorer 打開0001.exe,除了前三個區段,將後面的垃圾區段全部刪除(Delete Section (Header and Data))後保存;
  
c、用LordPE 打開0001.exe,查看區段表,右鍵Load section from disk...(從磁盤中載入區段),載入剛才Dump 的資源文件;
  
如果想要完美,可以將區段名改為.rsrc,區段屬性改為40000040;
  
d、修正資源的RVA 為0006D000,重建一下PE;
  
e、最後修正可選頭中的編譯器鏈接版本為0600,以免Peid 認不出來,將Base of Code 改成00001000,以免OD 分析不了代碼結構;
  
  
3、去掉多餘的菜單項( 這些菜單項的功能純粹是增加右鍵菜單長度):
  
00405CB4 8B55 04 mov edx,dword ptr ss:[ebp+4] ; 改成jmp 00405D05
  
00405CB7 6A 00 push 0
  
00405CB9 6A 00 push 0
  
00405CBB 68 00080000 push 800
  
00405CC0 52 push edx
  
00405CC1 FFD7 call edi
  00405CC3 8B45 04 mov eax,dword ptr ss:[ebp+4]
  
00405CC6 68 B8FF4500 push 0045FFB8 ; ASCII "Re&gister..."
  
  
至此,程序處理完畢,大小568 kb,可以用UPX壓縮;
  
  
  
  
-------------------------------------------------- ------------------------------ 【經驗總結】
  
全在上面了,好累,以後注意勞逸結合,Armageddon處理一下就可以了。
  
-------------------------------------------------- ------------------------------ 【版權聲明】: 本文原創於看雪技術論壇, 轉載請註明作者並保持文章的完整, 謝謝!

                                                       
2010年06月03日 22:05:12