显示标签为“破解”的博文。显示所有博文
显示标签为“破解”的博文。显示所有博文

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年9月26日星期一

【轉載】簡單分析mplayer播放器讀取.m3u格式文件漏洞


作 者: kindsjay
時 間: 2011-09-01,11:04:39
鏈接: http://bbs.pediy.com/showthread.php?t=139600

前言:這段時間一直在努力學習漏洞分析,並參考了failwest大牛製作<0day第二版>, 暴風m3u文件讀取漏洞(看雪網->鮮果也曾對其進行分析),小弟不才,只能跟著大牛後面跑.
附:暴風m3u分析,是在同事dge的幫助下完成的調試,乘熱打鐵,自己分析了同一類型(mplayer播放器)漏洞.也算是對自己知識的總結,第一次寫文章,還請大家指責批評,不清楚的地方可以發郵件問我

測試環境:
  windows xp sp3_cn (實體機測試,沒有使用虛擬機)
   ollyice看雪版
  poc和測試軟件版本見:http://www.exploit-db.com/exploits/17565/
  

漏洞觸發過程分析:
      ollyice 附加mplayer.exe進程,設置OD調試斷點(工具欄-->選項-->調試選項--->異常標籤)
------------------------------
   忽略在Kernel32中的內存訪問異常,並勾選以下選項
       int3 中斷
       單步中斷
       整數除以0
           無效或特權指令
       所有FPU異常
 -----------------------------------------

    將生成的m3u格式文件拖拽到mplayer播放器窗口中,F9運行,OD會自動下斷在程序異常信息信息處.
      觀察od堆棧信息窗口,查看棧信息

0022E948 00000000
0022E94C 00561760 返回到mplayer.00561760 來自<jmp.&msvcrt.strcpy>(可疑)
0022E950 0022EBB8 ASCII "http:// AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"...
0022E954 003FD5C8 ASCII "http:// AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"...
0022E958 00000000
0022E95C 00000000
0022E960 00000000
0022E964 0022E974
0022E968 00000000
0022E96C 7C930098 返回到ntdll.7C930098 來自ntdll.7C922AB0


可以發現jmp.&msvcrt.strcpy(可疑,再後面進行證實),鼠標右鍵點擊該行,選擇"反彙編窗口中跟隨",來到彙編主窗體,對該函數下斷點,重新載入m3u至mplayer,
F9來到斷點處,可以查看下堆棧信息和彙編信息

------------------------------------------
堆棧信息:
0022E950 0022EBB8 |dest = 0022EBB8(目標地址)
0022E954 003FD5C8 \src = "http:// AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"...(源地址)
0022E958 00000000
0022E95C 00000000
0022E960 00000000
0022E964 0022E974
0022E968 00000000
0022E96C 7C930098 返回到ntdll.7C930098 來自ntdll.7C922AB0

彙編主窗體信息:
00561754 |. 895C24 04 mov dword ptr [esp+4], ebx ; ||
00561758 |. 890424 mov dword ptr [esp], eax ; ||
0056175B |. E8 A8032900 call <jmp.&msvcrt.strcpy> ; |\strcpy
00561760 |. 8D9424 680200>lea edx, dword ptr [esp+268] ; |
00561767 |. C74424 04 2F0>mov dword ptr [esp+4], 2F ; |
0056176F |. 891424 mov dword ptr [esp], edx ; |
00561772 |. E8 C1032900 call <jmp.&msvcrt.strrchr> ; \strrchr
-----------------------------------------------

F7進入call:
   77C16030 > 57 push edi
77C16031 8B7C24 08 mov edi, dword ptr [esp+8]
77C16035 EB 6A jmp short 77C160A1
77C16037 8DA424 00000000 lea esp, dword ptr [esp]
77C1603E 8BFF mov edi, edi
77C16040 > 8B4C24 04 mov ecx, dword ptr [esp+4]
77C16044 57 push edi
77C16045 F7C1 03000000 test ecx, 3
77C1604B 74 0F je short 77C1605C
77C1604D 8A01 mov al, byte ptr [ecx]
77C1604F 41 inc ecx
77C16050 84C0 test al, al
77C16052 74 3B je short 77C1608F
77C16054 F7C1 03000000 test ecx, 3
77C1605A ^ 75 F1 jnz short 77C1604D
77C1605C 8B01 mov eax, dword ptr [ecx]
77C1605E BA FFFEFE7E mov edx, 7EFEFEFF
77C16063 03D0 add edx, eax
77C16065 83F0 FF xor eax, FFFFFFFF
77C16068 33C2 xor eax, edx
77C1606A 83C1 04 add ecx, 4
77C1606D A9 00010181 test eax, 81010100
77C16072 ^ 74 E8 je short 77C1605C
77C16074 8B41 FC mov eax, dword ptr [ecx-4]
77C16077 84C0 test al, al
77C16079 74 23 je short 77C1609E
77C1607B 84E4 test ah, ah
77C1607D 74 1A je short 77C16099
77C1607F A9 0000FF00 test eax, 0FF0000
77C16084 74 0E je short 77C16094
77C16086 A9 000000FF test eax, FF000000
77C1608B 74 02 je short 77C1608F
77C1608D ^ EB CD jmp short 77C1605C
77C1608F 8D79 FF lea edi, dword ptr [ecx-1]
77C16092 EB 0D jmp short 77C160A1
77C16094 8D79 FE lea edi, dword ptr [ecx-2]
77C16097 EB 08 jmp short 77C160A1
77C16099 8D79 FD lea edi, dword ptr [ecx-3]
77C1609C EB 03 jmp short 77C160A1
77C1609E 8D79 FC lea edi, dword ptr [ecx-4]
77C160A1 8B4C24 0C mov ecx, dword ptr [esp+C]
77C160A5 F7C1 03000000 test ecx, 3
77C160AB 74 19 je short 77C160C6
77C160AD 8A11 mov dl, byte ptr [ecx]
77C160AF 41 inc ecx
77C160B0 84D2 test dl, dl
77C160B2 74 64 je short 77C16118
77C160B4 8817 mov byte ptr [edi], dl
77C160B6 47 inc edi
77C160B7 F7C1 03000000 test ecx, 3
77C160BD ^ 75 EE jnz short 77C160AD
77C160BF EB 05 jmp short 77C160C6
77C160C1 8917 mov dword ptr [edi], edx(這裡出現異常)
77C160C3 83C7 04 add edi, 4
77C160C6 BA FFFEFE7E mov edx, 7EFEFEFF
77C160CB 8B01 mov eax, dword ptr [ecx]
77C160CD 03D0 add edx, eax
77C160CF 83F0 FF xor eax, FFFFFFFF
77C160D2 33C2 xor eax, edx
77C160D4 8B11 mov edx, dword ptr [ecx]
77C160D6 83C1 04 add ecx, 4
77C160D9 A9 00010181 test eax, 81010100
77C160DE ^ 74 E1 je short 77C160C1
77C160E0 84D2 test dl, dl
77C160E2 74 34 je short 77C16118
77C160E4 84F6 test dh, dh
77C160E6 74 27 je short 77C1610F
77C160E8 F7C2 0000FF00 test edx, 0FF0000
77C160EE 74 12 je short 77C16102
77C160F0 F7C2 000000FF test edx, FF000000
77C160F6 74 02 je short 77C160FA
77C160F8 ^ EB C7 jmp short 77C160C1
77C160FA 8917 mov dword ptr [edi], edx
77C160FC 8B4424 08 mov eax, dword ptr [esp+8]
77C16100 5F pop edi
77C16101 C3 retn
77C16102 66:8917 mov word ptr [edi], dx
77C16105 8B4424 08 mov eax, dword ptr [esp+8]
77C16109 C647 02 00 mov byte ptr [edi+2], 0
77C1610D 5F pop edi
77C1610E C3 retn
77C1610F 66:8917 mov word ptr [edi], dx
77C16112 8B4424 08 mov eax, dword ptr [esp+8]
77C16116 5F pop edi
77C16117 C3 retn
77C16118 8817 mov byte ptr [edi], dl
77C1611A 8B4424 08 mov eax, dword ptr [esp+8]
77C1611E 5F pop edi
77C1611F C3 retn

可以發現,在調試過程中,紅色地方,就是之前我們捕獲的異常地址處,說明該函數在函數cpy時,發生了溢出


命令:DD 0022EBB8,查看內存信息,內容已經被覆蓋,

0022EBB8 68 74 74 70 3A 2F 2F 20 41 41 41 41 41 41 41 41 http:// AAAAAAAA
0022EBC8 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0022EBD8 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0022EBE8 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0022EBF8 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0022EC08 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0022EC18 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0022EC28 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0022EC38 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 AAAAAAAAAAAAAAAA
0022EC48 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41..... AAAAAAAAAAAAAAA.....



原理分析:函數在拷貝的過程中,由於原地址數據超長,導致目標數據出現溢出.
總結:之前我看過failwest和仙果的文章,他們分別是按特徵字符串".m3u"和"Readfile函數"下斷點並進行跟踪,查找異常函數,而我此篇文章卻是直接分析的棧,可能會有些跳躍,大家可自行選擇

2011年8月6日星期六

Reglo v3.5a 註冊算法詳析

【軟件名稱】:
    Reglo v3.5a

【下載地址】:
    Http://www.basta.com/

【軟件簡介】:
    Reglo 是一款電腦屏幕測量工具,其實就是一把尺子,有多種單位可以選擇,用於測量電腦屏幕中圖片或其它.

【軟件限​​制】:
    如果沒有註冊,則只有 30 天的試用期.

【破解聲明】:
    我是搞軟件的,知道一款軟件的開發,需要付出很多很多,所以如果大家有點錢的話,還是多支持支持軟件的作者,這樣人家才有乾勁呀.其實,這款軟件對我沒什麼用,只是好奇它的註冊算法而已,如果大家有興趣,不妨在看完了這篇註冊算法分析後,用Reglo 的姊妹軟件試試,看它們的註冊算法是什麼.它的姊妹軟件包括:
    AppToService v4.0 一個Windows 控制台,這個使用控制台的軟體可以讓你把常規程序或服務器,運行做Windows 服務.
    Buzof v4.0 一款讓你可以消除煩人的系統消息的工具,比如,刪除文件時,系統總會提示是否真的要刪除等信息.
    Deletor v4.2 一款可以按照你的要求,進行刪除的工具.例如,刪除滿足一定大小/時間/屬性等條件的文件夾等等.
    Filo v4.2 修改文件或文件夾屬性的工具.
    Splitty v4.0 文件分割合併工具.
    ZMover v7.2 桌面佈局管理軟件,可以讓你指定程序窗口位置/大小/層次等.

【破解工具】:
    PEID v0.94,OllyDBG v1.10

【破文作者】:
    WSLVIC 電郵:Crk4u@163.com

【破解時間】:
    二〇一一年七月二日

【破解過程】:
———————————————————————————————————————————
    不必多說,首先是查查殼,用PEID 一看,原來是Microsoft Visual C++ 7.0,也好,省的脫殼了.然後當然是用OD 載入,載入後停在這裡:
    00416197 > $ 6A 60 PUSH 60
    00416199 . 68 F0284300 PUSH Reglo2.004328F0
    0041619E . E8 451A0000 CALL Reglo2.00417BE8
    004161A3 . BF 94000000 MOV EDI,94
    004161A8 . 8BC7 MOV EAX,EDI
    004161AA . E8 C10E0000 CALL Reglo2.00417070
    004161AF . 8965 E8 MOV DWORD PTR SS:[EBP-18],ESP
    004161B2 . 8BF4 MOV ESI,ESP
    004161B4 . 893E MOV DWORD PTR DS:[ESI],EDI
    004161B6 . 56 PUSH ESI ; /pVersionInformation
    004161B7 . FF15 A801​​4300 CALL DWORD PTR DS:[<&KERNEL32.GetVersion>; \GetVersionExA
    ......
    典型的VC 入口呀,不急下斷點,先看看註冊窗口是什麼樣的,按F9 運行,在標尺上點擊右鍵選擇"&About Reglo...",在彈出窗口中選擇"&Purchasing Information.. .",終於出現了,原來註冊碼分兩部分,每部分都8 位,總長度是16,也就是十六進制的0x10,好了先看看能不能輸入中文(呵呵,習慣了),輸入"瑤淼"---原來可以輸入中文,當然也可以輸入英文字母和數字,胡亂輸一通,比如"11111111-22222222" 點擊"Ok",彈出對話框"The password you have entered is incorrect." ---現在當然不能註冊,不過記下了這串E 文,點擊"確定" 回到OD 中,用OD 自帶的字符串搜索功能搜索"所有參考文本字符串",很快就搜索完畢了,可惜上面那串E 文,連影子都沒有,再用OD 插件"超級字串參考" 和"中文搜索引擎",也沒找到,暈,只好下對話框斷點了,試試MessageBoxA 吧,在命令條中輸入"bpx messageboxa",然後點擊F9 運行,輸入註冊碼"11111111-22222222",點擊"確定",呵呵,運氣不錯,被OD 斷了下來,斷點停在:
    0042B8C7 |. FF15 9C034300 CALL DWORD PTR DS:[<&USER32.MessageBoxA>>; \MessageBoxA (1)
    不管它,向上看,直到該段代碼的開始處,
    0042B7FB /> /55 PUSH EBP (2)
    點擊該行,這時,OD 顯示"跳轉來自0042B924",一看,竟然是這段代碼的最後一行
    0042B924 \.^\E9 D2FEFFFF JMP Reglo2.0042B7FB (3)
    於是想到應該是從其它某處調用到(1) 之後的某個地方,再執行到(3),然後再跳轉到(2),仔細觀察代碼,發現(1) 之後的0042B904 處
    0042B904 |. C2 0C00 RETN 0C
    非常可疑,其下一行為
    0042B907 |$ 55 PUSH EBP
    在該行點擊,OD 顯示"本地調用來自00401698,00406C15,00425097,0042B96A,0042C086,0042C163",到底從這裡的哪個調用來的呢,不忙,先在該處按F2,下一個斷點,按F9 運行程序,胡亂輸入"1111111122222222" 點擊"確定",發現OD 已經斷在了該處,在OD 工具欄中點擊圖標"K" --- 調用堆棧,發現OD 顯示"函數過程Reglo.0042B907,調用來自Reglo.0042B96A",於是在OD 信息窗口的"本地調用來自00401698,00406C15,00425097,0042B96A...",上點右鍵選擇"轉到Call 來自0042B96A",於是OD 立刻來到了0042B96A所在的代碼段,在該段的段頭:
    0042B929 /$ B8 6DED4200 MOV EAX,Reglo.0042ED6D
    上點擊,發現OD 顯示"本地調用來自004018F9, 00401F57, 0040777D, 00407CB3, 0040887B, 0040A7CC, 0040B31A, 0040B464, 00412AC9, 00412AE0, 004250B4, 004286E2, 0042875A, 0042BF9A",暈,又是一堆,咋辦呢,還是老辦法,F2 斷點→F9 運行→輸入註冊碼→點擊"確定",OD 斷在了0042B929,在點擊圖標"K",OD 顯示"函數過程Reglo.0042B929,調用來自Reglo.0040B464" ,再次轉到0040B464 所在的代碼段,該段的段頭為:
    0040B350 /$ 64:A1 0000000>MOV EAX,DWORD PTR FS:[0]
    下斷點,運行到該行,F8 單步到
    0040B3AC |. E8 519A0100 CALL Reglo.00424E02
    處時,註冊窗口彈出,呵呵,該行是調用註冊窗口的.繼續F8,直到
    0040B3BA |. 8B8424 D80000>MOV EAX,DWORD PTR SS:[ESP+D8]
    這時在OD 信息窗口中看到了已經輸入的註冊碼.
    哈哈,萬里長征終於走到了第一站!
    這點很重要,這是進行下一步調試的關鍵,於是整理一下,刪除除了0040B3BA 處以外的所有其它斷點.註冊碼分析就要開始了.在0040B3BA 下方看到一個Call
    0040B3DA |. E8 A17B0000 CALL Reglo.00412F80
    F7 跟進去一看,果真是進行註冊碼測試,好,先下個斷點.然後按Ctrl+F2 重新載入,輸入註冊碼時輸入"1111111122222222",OD 斷在了0040B3DA,F7 跟進,便來到了下方,
    ┌──────────────────────────────────────────────┐
    │00412F80 PUSH EBX │
    │00412F81 PUSH EBP │
    │00412F82 MOV EBX,EAX ; EBX=輸入的註冊碼│
    │00412F84 PUSH ESI │
    │00412F85 XOR EBP,EBP │
    │00412F87 TEST EBX,EBX │
    │00412F89 PUSH EDI │
    │00412F8A JE Reglo.004130C7 ; 一跳就死│
    │00412F90 LEA EDX,DWORD PTR DS:[EAX+1] ; EDX=第二個字符的地址│
    │00412F93 /MOV CL,BYTE PTR DS:[EAX] ; CL=第一個字符│
    │00412F95 |INC EAX ; 下一個│
    │00412F96 |TEST CL,CL ; 測試CL,是否為全零│
    │00412F98 \JNZ SHORT Reglo.00412F93 ; 循環測試CL 是否為00 │
    │00412F9A SUB EAX,EDX ; EAX=尾字符的地址,EDX=第二個字符的地址│
    │00412F9C CMP EAX,10 ; 比較字符串長度是否為16 │
    │00412F9F JNZ Reglo.004130C7 ; 不等就跳,一跳就完│
    │00412FA5 MOV ESI,EAX ; ESI 指向尾字符│
    └──────────────────────────────────────────────┘
    這段代碼沒什麼難度,就是測試下輸入的註冊碼的長度,及每個字符是否為00,之後則來到,
    ┌──────────────────────────────────────────────┐
    │00412FA7 /MOVSX EAX,BYTE PTR DS:[ESI+EBX-1] │
    │00412FAC |DEC ESI │
    │00412FAD |PUSH EAX │
    ┌──────────────────────────────────────────────┐
    │00412FAE |CALL Reglo.00415ACF ; 判斷是否是數字(4)│
    └──────────────────────────────────────────────┘
    │00412FB3 |ADD ESP,4 │
    │00412FB6 |TEST EAX,EAX │
    │00412FB8 |JE Reglo.004130C7 ; 這裡一跳就完蛋│
    │00412FBE |TEST ESI,ESI │
    │00412FC0 \JNZ SHORT Reglo.00412FA7 │
    └──────────────────────────────────────────────┘
    這是一段循環,中間有個Call,其過程是逐字讀入註冊碼的每一位,然後用Call 進行測試,看來是關鍵測試了,F7 步入,OD 轉到這裡,
    ┌──────────────────────────────────────────────┐
    │00415ACF PUSH EBP │
    │00415AD0 MOV EBP,ESP │
    │00415AD2 PUSH ECX │
    │00415AD3 PUSH EBX │
    │00415AD4 MOV EBX,DWORD PTR SS:[EBP+8] ; 地址移動8位│
    │00415AD7 CMP EBX,0FF ; 判定是否是ASCII 字符│
    │00415ADD /JBE SHORT Reglo.00415B49 ; 小於等於255 時,這裡一定跳,否則必死│
    └──────────────────────────────────────────────┘
    這段的關鍵是上面的最後兩句,0FF 是255,EBX 中是當前位置註冊碼的ASCII 值(別忘了這段是在循環中),所以這兩句是測試,如果當前位置註冊碼的ASCII 值<= 255 時,發生跳轉,由255 可知,要求輸入字符是"基本拉丁字符集" 和"增補拉丁字符集1" 中的字符,由於當前輸入的測試註冊碼是"1111111122222222",故滿足要求,不過你可以試試輸入註冊碼"!111111122222222",這時Reglo 會彈出對話框"Please enter no more than 8 characters." 註冊就失敗了.然後跳轉到00415B49 處,注意,中間有段代碼00415ADF-00415B47 是處理當字符的ASCII>255 時,進行的操作,我沒跟過去,如果誰有興趣不妨看看Reglo 在這段干了什麼.跳轉到了這裡,
    ┌──────────────────────────────────────────────┐
    │00415B49 \PUSH EBX │
    │00415B4A CALL Reglo.00419636 ; 判斷是否是數字:否,則跳出. │
    │00415B4F POP ECX │
    │00415B50 POP EBX │
    │00415B51 LEAVE │
    │00415B52 RETN │
    └──────────────────────────────────────────────┘
    又是一個 Call! 煩哪,還得跟進去,來到,
    ┌──────────────────────────────────────────────┐
    │00419636 /$ CALL Reglo.00419174 ; 別管,給EAX 一個地址│
    │0041963B |. MOV EAX,DWORD PTR DS:[EAX+64] │
    │0041963E |. CMP EAX,DWORD PTR DS:[43FF84] ; Reglo.0043FF30 │
    │00419644 |. JE SHORT Reglo.0041964B ; 必跳│
    │00419646 |. CALL Reglo.0041911B │
    │0041964B |> CMP DWORD PTR DS:[EAX+28],1 │
    │0041964F |. JLE SHORT Reglo.00419661 ; 必跳│
    │00419651 |. PUSH 4 │
    │00419653 |. PUSH DWORD PTR SS:[ESP+8] │
    │00419657 |. PUSH EAX │
    │00419658 |. CALL Reglo.004198A3 │
    │0041965D |. ADD ESP,0C │
    │00419660 |. RETN │
    │00419661 |> MOV EAX,DWORD PTR DS:[EAX+48] ; UNICODE " ((((( │
    │00419664 |. MOV ECX,DWORD PTR SS:[ESP+4] │
    │00419668 |. MOVZX EAX,BYTE PTR DS:[EAX+ECX*2] ; 若ECX 為0-9 的數字,則EAX=84 │
    │0041966C |. AND EAX,4 ; EAX=84 And 4=4 │
    │0041966F \. RETN │
    └──────────────────────────────────────────────┘
    怎麼又是一堆Call!而且第一句就是一個Call,F7 進入,發現一堆系統調用,包括GetLastError/TlsGetValue/TlsSetValue/GetCurrentThreadID/SetLastError 等等,它們是乾什麼的呢?為什麼這麼做?到現在也沒搞清楚,不過至少知道這個Call 改變了EAX,也就是給EAX 了一個新地址,在這個Call 後的兩句,可以看到它是在進行地址比較,不管它了,來到00419644 處,這裡一定要跳,之後來到0041964B,與1 進行比較,之後到了0041964F 處跳轉至00419661,關鍵來了,這句給了EAX 一個UniCode 字符串,這是乾啥用的?先不急,看看它的下一句,在OD 的信息窗口看到了SS:[ESP+4] 中放置的其實就是當前位置註冊碼的ASCII 值,並賦給了ECX,ok,再下一句看到了DS:[EAX+ECX *2],這又是乾什麼呢,分析看看:當前EAX 是一個指向UniCode 的地址,ECX 是ASCII 值,所以EAX+ECX*2 必是一個地址,運行到該句OD 顯示"DS:[00432AC6 ]=84",在其上點擊右鍵,選擇"數據窗口中跟隨地址",發現數據窗口中是這樣一串數據"84 00 84 00 84 00 84 00..."---共10 組"84 00",於是終於明白了這段到底是在幹什麼,也就是說從該段的第一個Call 給了EAX 一個地址,通過這個地址加上一定的偏移量,指向一些特定地址,例如00419661 處, EAX=DS:[EAX+48],如果當前位置的字符是0-9 (ASCII 的30-39),那麼從DS:[EAX+48] 開始,偏移30*2 至39*2,都將得到84 這個值,也就是上面的10 組"84 00",那麼為什麼是84 呢,從0041966C 處,可看出84 與4 進行"與" 操作,EAX 將等於4,不等於0,換句話說EAX 就是一個flag,當EAX 非零時,說明匹配成功,當前字符是0-9 的數字,否則EAX=0 說明匹配失敗.其實在UltraEdit 中也可找到這兩個字符串,其中,
    UNICODE " ((((( H" 在實偏移31C62H 處
    "84 00 84 00 84 00 84 00 84 00 84 00 84 00..." 在實偏移31CC2H 處
    誰有興趣的話,不妨算算它們之間的偏移是否是0x30*2 - 0x39*2
    至此,萬里長征第二步算完成了,可是只是知道註冊碼由數字組成,卻不知道形成規則是什麼,怎麼辦,只好一路返回,回到00412FB3 處,此處仍在循環中,在00412FC2 處按F4,運行到該行,
    ┌──────────────────────────────────────────────┐
    │00412FC2 MOVSX EDI,BYTE PTR DS:[EBX+F] ; EDI=末字符│
    │00412FC6 SUB EDI,30 │
    │00412FC9 MOV EAX,EDI ; EAX=末字符的數值│
    │00412FCB IMUL EAX,EDI ; EAX=末位數的平方│
    │00412FCE CDQ ; CDQ 雙字擴展成四字│
    │00412FCF MOV ECX,0A │
    │00412FD4 IDIV ECX ; 末位數的平方(EAX)除10,商放EAX,余放EDX │
    │00412FD6 MOV AL,BYTE PTR DS:[EDI+EBX] ; AL=第EDI+1 個字符│
    │00412FD9 ADD DL,30 ; 加30 │
    │00412FDC CMP AL,DL ; │
    │00412FDE JNZ Reglo.004130C7 ; 一跳就完│
    └──────────────────────────────────────────────┘
    這段代碼的關鍵是:
    AL 是註冊碼的第EDI+1(EDI=末字符的數值) 個字符,而DL 是註冊碼的最後一個字符的平方,除以10,所得的餘數,由CMP Al,DL 及其下一行的JNZ Reglo.004130C7 可知,註冊碼的最後一位的數值i,決定著註冊碼第i+1 位的值必須為最後一位的數值的平方除10 所得的餘數,即:
    最後一位數i 第i+1 位數模型
    i=0 第1(i+1) 位為0(0^2 Mod 10) 0xxxxxxx xxxxxxx0
    i=1 第2(i+1) 位為1(1^2 Mod 10) x1xxxxxx xxxxxxx1
    i=2 第3(i+1) 位為4(2^2 Mod 10) xx4xxxxx xxxxxxx2
    i=3 第4(i+1) 位為9(3^2 Mod 10) xxx9xxxx xxxxxxx3
    i=4 第5(i+1) 位為6(4^2 Mod 10) xxxx6xxx xxxxxxx4
    i=5 第6(i+1) 位為5(5^2 Mod 10) xxxxx5xx xxxxxxx5
    i=6 第7(i+1) 位為6(6^2 Mod 10) xxxxxx6x xxxxxxx6
    i=7 第8(i+1) 位為9(7^2 Mod 10) xxxxxxx9 xxxxxxx7
    i=8 第9(i+1) 位為4(8^2 Mod 10) xxxxxxxx 4xxxxxx8
    i=9 第10(i+1) 位為1(9^2 Mod 10) xxxxxxxx x1xxxxx9
    由於輸入的測試註冊碼"1111111122222222" 不滿足這個要求,在00412FDE 處,就跳走了.不妨把測試註冊碼改為"1141111122222222",Ctrl+F2 重新運行,終於第一個坎00412FDE 過了,然後代碼來到,
    ┌──────────────────────────────────────────────┐
    │00412FE4 |.LEA EAX,DWORD PTR DS:[EDI+1] ; EAX=末字符數值+1 │
    │00412FE7 |.CMP EAX,0F │
    │00412FEA |.JL SHORT Reglo.00412FEF ; 必跳│
    │00412FEC |.SUB EAX,0F │
    │00412FEF |>MOVSX ECX,BYTE PTR DS:[EAX+EBX] ; ECX=第末字符數值+2 個數字│
    │00412FF3 |.LEA ESI,DWORD PTR DS:[ECX-30] ; ESI=第末字符數值+2 個數字的數值│
    │00412FF6 |.CMP ESI,1 ; 第末字符數值+2 位數字必須大於等於1│
    │00412FF9 |.JGE SHORT Reglo.00413000 ; 必跳│
    └──────────────────────────────────────────────┘
    測試註冊碼"1141111122222222" 的末位為"2",故第4(2+2) 個數字必須大於等於1,測試註冊碼已滿足要求,呵呵,繼續啊,第二個坎00412FF9 過了.繼續來到這裡,
    ┌──────────────────────────────────────────────┐
    │00413000 |>XOR ECX,ECX │
    │00413002 |.CMP ESI,1 │
    │00413005 |.SETG CL ; 根據標誌寄存器,大於1 時,置CL=1 │
    │00413008 |>MOV EDX,DWORD PTR SS:[ESP+14] │
    │0041300C |.INC EAX ; EAX=末字符數值+2 │
    │0041300D |.CMP EAX,0F │
    │00413010 |.MOV DWORD PTR DS:[EDX],ECX ; DS:[0012F3C0]=00000000 │
    │00413012 |.JL SHORT Reglo.00413017 │
    │00413014 |.SUB EAX,0F │
    │00413017 |>MOV ECX,EAX ; ECX=末字符數值+2 │
    │00413019 |.ADD EAX,3 ; EAX=末字符數值+5 │
    │0041301C |.CMP EAX,0F │
    │0041301F |.JL SHORT Reglo.00413024 │
    │00413021 |.SUB EAX,0F │
    │00413024 |>MOVSX ESI,BYTE PTR DS:[ECX+EBX] ; ESI=第末字符數值+3 個數的ASCII 值│
    │00413028 |.MOV ECX,EAX ; ECX=末字符數值+5 │
    │0041302A |.ADD EAX,3 ; EAX=末字符數值+8 │
    │0041302D |.SUB ESI,30 ; ESI=第末字符數值+3 個數的數值│
    │00413030 |.CMP EAX,0F │
    │00413033 |.JL SHORT Reglo.00413038 │
    │00413035 |.SUB EAX,0F │
    │00413038 |>MOVSX ECX,BYTE PTR DS:[ECX+EBX] ; ECX=第末字符數值+6 個數的ASCII 值│
    │0041303C |.SUB ECX,30 ; ECX=第末字符數值+6 個數的數值│
    │0041303F |.MOV EDX,EAX │
    │;EDX=末字符數值+8(末字符數值+8<15 時)或末字符數值+8-15(末字符數值+8>=15 時) │
    │00413041 |.IMUL ECX,ECX,64 ; ECX=(第末字符數值+6 個數的數值)*64 │
    │00413044 |.ADD EAX,3 ; EAX=末字符數值+11 │
    │00413047 |.CMP EAX,0F │
    │0041304A |.JL SHORT Reglo.0041304F │
    │0041304C |.SUB EAX,0F │
    │0041304F |>MOVSX EDX,BYTE PTR DS:[EDX+EBX] │
    │;EDX=第{末字符數值+9(末字符數值+8<15)或末字符數值+8-14(末字符數值+8>=15)}個數字的ASCII值│
    │00413053 |.MOVSX EAX,BYTE PTR DS:[EAX+EBX] │
    │;EAX=第{末字符數值+12(末字符數值+11<15)或末字符數值+11-14(末字符數值+11>=15)}個數字的ASCII值│
    │00413057 |.SUB EDX,30 │
    │;@EDX=第{末字符數值+9(末字符數值+8<15)或末字符數值+8-14(末字符數值+8>=15)}個數字的數值│
    │0041305A |.ADD ECX,EAX ; ECX=ECX+EAX │
    │0041305C |.LEA EDX,DWORD PTR DS:[EDX+EDX*4] ; EDX=@EDX*5 │
    │0041305F |.LEA ECX,DWORD PTR DS:[ECX+EDX*2-30]; ECX=ECX+@EDX*A-30 │
    │00413063 |.CMP ECX,7 ; ECX=ECX+@EDX*A-30=7(@EDX 是0-9 的數字) │
    │00413066 |.JNZ SHORT Reglo.004130C7 ; 這裡一跳就完蛋了│
    └──────────────────────────────────────────────┘
    這段代碼比較複雜,註釋寫的老長,目的是方便修改測試註冊碼,否則,註冊無法繼續下去.其關鍵是00413063 處,要求ECX+@EDX*A-30=7,這裡:
    ECX=(第末字符數值+6 個數的數值)*64+第{末字符數值+12(末字符數值+11<15)或末字符數值+11-14(末字符數值+11>=15 )}個數字的ASCII 值.
    @EDX 是 0-9 中的某個數字.
    對當前的測試註冊碼"1141111122222222" 而言,第8(2+6) 個數是"1",1*64=64;第14(2+12) 個數是"2",其ASCII=32 ,所以ECX=1*64+32=96.
    同時@EDX=第11(2+9) 個數是"2",故ECX+@EDX*A-30=96+2*A-30=7A 不等於7,故無法繼續,如何修改測試註冊碼呢,觀察ECX,@EDX 可知,ECX>=30,@EDX>=0,故若ECX+@EDX*A-30=7,則ECX+@EDX*A=37,就必有@EDX*A<=7 ,所以@EDX=0,ECX=37,換句話說,
    1.@EDX=0,意味著:
      第{末字符數值+9(末字符數值+8<15)或末字符數值+8-14(末字符數值+8>=15)}個數字的數值必為0.
    2.ECX=37,意味著:
      第 末字符數值+6 個數的數值必為 0,
      第{末字符數值+12(末字符數值+11<15)或末字符數值+11-14(末字符數值+11>=15)}個數字的數值必為7.
    由此可以看出,末位數對整個註冊碼具有標誌性作用,我們不妨試著修改一下,對於"1141111122222222" 而言,末位數為"2",因為2+8<15,故第11 (2+9) 位數為0.又從"第末字符數值+6 個數的數值必為0" 可知,第8 位數為0,因為2+11<15,故第14(2+12 ) 位數必為7.測試註冊碼可改為"1141111022022722"
    整理一下當末位數為0-9 時的註冊模型,如下:
    最後一位數 i 模型
    i=0 0xxxx0xx 0xx7xxx0
    i=1 x1xxxx0x x0xx7xx1
    i=2 xx4xxxx0 xx0xx7x2
    i=3 xxx9xxxx 0xx0xx73
    i=4 7xxx6xxx x0xx0xx4
    i=5 x7xxx5xx xx0xx0x5
    i=6 xx7xxx6x xxx0xx06
    i=7 0xx7xxx9 xxxx0xx7
    i=8 x0xx7xxx 4xxxx0x8
    i=9 xx0xx7xx x1xxxx09
    繼續吧,還有不少工作要做,越過第三個坎00413066,我們來到這裡,
    ┌──────────────────────────────────────────────┐
    │00413068 |.XOR EAX,EAX │
    │0041306A |.MOV ECX,10 │
    │0041306F |.NOP │
    │00413070 |>/MOVSX EDX,BYTE PTR DS:[ECX+EBX-1];EDX=第ECX 個數字│
    │00413075 |.|DEC ECX │
    │00413076 |.|IMUL EDX,ECX │
    │00413079 |.|ADD EAX,EDX ;@EAX=Sum{(數字的ACSII 值)*(其所在位置-1)} │
    │0041307B |.|TEST ECX,ECX │
    │0041307D |.\JNZ SHORT Reglo.00413070 │
    │0041307F |.LEA EDX,DWORD PTR DS:[EDI+E] ;EDI=末位數的數值│
    │00413082 |.CMP EDX,0F ;EDX=14(末位數為0 時) │
    │00413085 |.JL SHORT Reglo.0041308A ;必跳│
    │00413087 |.SUB EDX,0F │
    │0041308A |>MOVSX ECX,BYTE PTR DS:[EDX+EBX] ;EDX=末位數的數值+14-15(末位數不為0 時) │
    │;@ECX=第15 個數的ASCII(當末位數為0 時)或第末位數數值位數的ASCII 值(當末位數不為0 時) │
    │0041308E |.IMUL ECX,EDX ;ECX=@ECX*EDX │
    │00413091 |.SUB EAX,ECX ;EAX=@EAX-ECX │
    │00413093 |.DEC EDX │
    │00413094 |.JNS SHORT Reglo.00413099 ;結果為正就跳(SF=0) │
    │00413096 |.ADD EDX,0F │
    │00413099 |>MOV CL,BYTE PTR DS:[EDX+EBX] │
    │;@CL=第14 個數的ASCII(當末位數為0 時)或第末位數數值-1 位數的ASCII 值(當末位數不為0 時)│
    │0041309C |.MOVSX EDI,CL ; │
    │0041309F |.IMUL EDI,EDX ; │
    │004130A2 |.SUB EAX,EDI ;EAX=@EAX-@ECX*EDX-@CL*EDI │
    │004130A4 |.CDQ │
    │004130A5 |.MOV EDI,0A ;EDI=除數10 │
    │004130AA |.IDIV EDI │
    │004130AC |.ADD DL,30 ;DL=餘數的ASCII 值│
    │004130AF |.CMP DL,CL ;CL=第14 個數或第末位數數值-1 位數的ASCII │
    │004130B1 |.JNZ SHORT Reglo.004130C7 ;一跳就死│
    └──────────────────────────────────────────────┘
    一進來,就看見一個循環,不過這個循環倒也簡單,就是從尾到頭,將每個數字的ASCII 值與其(所在位置-1)相乘,並累加起來送給EAX,也就是說:@EAX =n1*0+n2*1+n3*2+n4*3+n5*4+...+n13*12+n14*13+n15*14+n16*15,循環完畢後,來到了0041307F,覺得有點奇怪,因為在這段代碼附近,沒看到EDI 是何時賦值的,仔細找了好一段代碼才發現,上次EDI 是在00412FC6 處賦值的,其值是末位數的數值,對當前測試註冊碼"1141111022022722" 而言,它就是"2".這段代碼的關鍵是004130AF,它事實上是要求:

    當末位數為 0 時,
    設置被除數 EAX=@EAX-n15*14-n14*13
    設置第 14 位數的數值為 EAX/10 的餘數

    當末位數為 1 時,
    設置被除數 EAX=@EAX-n1*0-n15*14=@EAX-n15*14
    設置第 15 位數的數值為 EAX/10 的餘數

    當末位數為 2 時,
    設置被除數 EAX=@EAX-n2*1-n1*0=@EAX-n2*1
    設置第 1 位數的數值為 EAX/10 的餘數

    當末位數不為 0,1,2 時,
    設置被除數 EAX=@EAX-ni*(i-1)-n(i-1)*(i-2)
    設置第i-1 位數的數值為EAX/10 的餘數,(其中,i=末位數數值)

    先在的問題是如何修改測試註冊碼,算出EAX,再求餘,顯然不是一個好辦法,解決方法是在4130AF 處下一個斷點,看OD 信息窗口中的DL,是什麼數字,再退出Reglo ,修改指定位置的數字為DL 中的值.例如,當前測試註冊碼,末位數是"2",故需要修改第1 位數為DL 中的值7,修改後,測試註冊碼變為" 7141111022022722",試試,呵呵,又過一關吶!如此以來,第四個坎也過了.之後,來到這裡,
    ┌──────────────────────────────────────────────┐
    │004130B3 PUSH ESI │
    │004130B4 PUSH 10 │
    ┌──────────────────────────────────────────────┐
    │004130B6 CALL Reglo.00412ED0 (5)│
    └──────────────────────────────────────────────┘
    │004130BB ADD ESP,8 │
    │004130BE TEST EAX,EAX │
    │004130C0 MOV EAX,1 │
    │004130C5 JNZ SHORT Reglo.004130C9 │
    │004130C7 MOV EAX,EBP │
    │004130C9 POP EDI │
    │004130CA POP ESI │
    │004130CB POP EBP │
    │004130CC POP EBX │
    │004130CD RETN │
    └──────────────────────────────────────────────┘
    這裡有個Call,應該是調用註冊成功的對話框吧,哈哈,迫不及待了,按F9 直接運行!什麼也沒彈出來,但仔細一看原來關於對話框中的兩行字符"THIS SOFTWARE IS NOT REGISTERED " 以及"Expiration date:08/01/11" 不見了,呵呵,真的註冊成功了!測試註冊碼"7141111022022722" 真的是註冊碼!
    高興歸高興,可是別得意忘形,凡事都要留個心眼.其實在寫這篇破文之前,我用的測試註冊碼不是"1111111122222222" 而是"0100000000070200",這個註冊碼和"7141111022022722" 一樣可以通過以上所有的坎,可是當你用它來註冊的時候,會讓Reglo 爆掉!不信你試試!為什麼呢?---其實原因很簡單,還有暗樁!在那裡?當然不能放過上面的那個Call (5) 了,F7 步入,到了這裡,
    ┌──────────────────────────────────────────────┐
    │00412ED0 /$SUB ESP,8 ; 指向註冊碼│
    │00412ED3 |.TEST EBX,EBX │
    │00412ED5 |.JNZ SHORT Reglo.00412EDD ; 這裡必跳│
    │00412ED7 |.XOR EAX,EAX │
    │00412ED9 |.ADD ESP,8 │
    │00412EDC |.RETN │
    │00412EDD |>CMP DWORD PTR SS:[ESP+C],-1 ; -1 是在堆棧中設定的某種標誌│
    │00412EE2 |.JNZ SHORT Reglo.00412EFD ; 必跳│
    │00412EE4 |.MOV EAX,EBX │
    │00412EE6 |.LEA EDX,DWORD PTR DS:[EAX+1] │
    │00412EE9 |.LEA ESP,DWORD PTR SS:[ESP] │
    │00412EF0 |>/MOV CL,BYTE PTR DS:[EAX] │
    │00412EF2 |.|INC EAX │
    │00412EF3 |.|TEST CL,CL │
    │00412EF5 |.\JNZ SHORT Reglo.00412EF0 │
    │00412EF7 |.SUB EAX,EDX │
    │00412EF9 |.MOV DWORD PTR SS:[ESP+C],EAX │
    │00412EFD |>MOV EAX,DWORD PTR SS:[ESP+C] │
    │00412F01 |.PUSH EBP │
    │00412F02 |.PUSH ESI │
    │00412F03 |.PUSH EDI │
    │00412F04 |.XOR EBP,EBP │
    │00412F06 |.XOR ESI,ESI │
    │00412F08 |.XOR EDI,EDI │
    │00412F0A |.TEST EAX,EAX │
    │00412F0C |.MOV DWORD PTR SS:[ESP+C],2 ; 12F38C 設置標誌2 │
    │00412F14 |.MOV DWORD PTR SS:[ESP+10],1 ; 12F390 設置標誌1 │
    │00412F1C |.JLE SHORT Reglo.00412F76 ; 一跳就死│
    │00412F1E |.MOV EDI,EDI ; EDI=0 │
    │00412F20 |>/MOVSX EAX,BYTE PTR DS:[EDI+EBX] ; 讀入註冊碼第EDI+1 位│
    │00412F24 |.|PUSH EAX │
    │00412F25 |.|CALL Reglo.00415ACF ; 判斷是否為0-9 的數字│
    │00412F2A |.|ADD ESP,4 │
    │00412F2D |.|TEST EAX,EAX ; 是,則EAX=4,否,則EAX=0 │
    │00412F2F |.|JE SHORT Reglo.00412F52 ; 若不是數字,就跳過本次循環│
    │00412F31 |.|MOVSX ECX,BYTE PTR DS:[EDI+EBX] ; 讀入註冊碼第EDI+1 位│
    │00412F35 |.|MOV EAX,DWORD PTR SS:[ESP+ESI*4+C] ; EAX=2(ESI=0 時)或EAX=1(ESI=1 時) │
    │00412F39 |.|SUB ECX,30 ; ECX=註冊碼第EDI+1 位的數值│
    │00412F3C |.|IMUL EAX,ECX │
    │; 奇數位用2 乘以當前位置數字的數值,偶數位用1 乘以當前位置數字的數值│
    │00412F3F |.|CMP EAX,0A │
    │00412F42 |.|JL SHORT Reglo.00412F47 │
    │00412F44 |.|SUB EAX,9 ; 如果EAX>=10,則EAX=EAX-9 │
    │00412F47 |>|XOR ​​EDX,EDX ; EBP=Sum(EAX) │
    │00412F49 |.|ADD EBP,EAX │
    │00412F4B |.|TEST ESI,ESI │
    │00412F4D |.|SETE DL ; 若ZF=1,則DL 置1 │
    │00412F50 |.|MOV ESI,EDX ; ESI=0(奇數位) 或1(偶數位) │
    │00412F52 |>|MOV EAX,DWORD PTR SS:[ESP+18] ; EAX=10h=16d │
    │00412F56 |.|INC EDI │
    │00412F57 |.|CMP EDI,EAX │
    │00412F59 |.\JL SHORT Reglo.00412F20 │
    │00412F5B |.TEST EBP,EBP │
    │00412F5D |.JE SHORT Reglo.00412F76 ; 一跳就死│
    │00412F5F |.MOV EAX,EBP ; 設置被除數│
    │00412F61 |.CDQ │
    │00412F62 |.IDIV DWORD PTR SS:[ESP+1C] │
    │;第末字符數值+3 個數的數值作為除數放在堆棧SS:[12F39C] 中│
    │00412F66 |.TEST EDX,EDX ; 測試餘數是否為0 │
    │00412F68 |.JNZ SHORT Reglo.00412F76 ; 一跳就死│
    │00412F6A |.POP EDI │
    │00412F6B |.POP ESI │
    │00412F6C |.MOV EAX,1 │
    │00412F71 |.POP EBP │
    │00412F72 |.ADD ESP,8 │
    │00412F75 |.RETN │
    │00412F76 |>POP EDI │
    │00412F77 |.POP ESI │
    │00412F78 |.XOR EAX,EAX │
    │00412F7A |.POP EBP │
    │00412F7B |.ADD ESP,8 │
    │00412F7E \.RETN │
    └──────────────────────────────────────────────┘
    這段代碼比較簡單,其主要過程是,在堆棧中設置兩個標誌2 和1,然後逐字讀入註冊碼,奇數位數字*2,偶數位數字*1,當奇數位數字*2 的值大於等於10 時,對其進行減9 操作,將各位結果累加到EBP 中,作為被除數.同時取出SS:[12F39C] 中的數字作為除數,只要整除成功,就可通過測試.但值得注意的是00412F62 處,關於除數的選擇,由於該處除數位於堆棧SS:[12F39C] 中,改變該堆棧值的操作是在004130B3 處的PUSH ESI 操作,而上一次改變ESI 是在0041302D 處,由此可知所謂的除數其實是:第末字符數值+3 個數的數值.所以測試註冊碼"7141111022022722",恰巧其第5(2+3) 個數是"1",所以可以整除,而最終註冊成功.但對於測試註冊碼"0100000000070200" 來說,其第3(0+3) 個數是"0",所以Reglo 發生了除0 操作,而崩潰了.

    至此,第五個坎也過了,軟件的註冊碼算法已基本清晰.可以寫總結了,但作為一個軟件從業人員,希望提醒大家,Reglo 竟然可以通過非法註冊碼導致軟件崩潰,說明其作者在驗證註冊碼的算法中存在Bug,也就是除零操作,這是一個軟件開發人員應盡量避免的低級錯誤,希望大家在自己的軟​​件中不要犯同樣的毛病.


【破解總結】:
———————————————————————————————————————————
    從Reglo 的註冊算法中,可以明確的感受到,"末位數"的軸心作用,幾乎每一步都涉及到末位數的操作,要么用作指定某位為操作位,要么用作固定某位的數值,要么用作判定某條件是否達成,這可能是Reglo 註冊算法的最大特色吧.
    對於Reglo v3.5a 的註冊碼的形制從現在的觀點來看,其模型可描述為:

     k 註冊碼模型
    k=0 0!#xx0xx 0xx7x?x0
    k=1 x1!#xx0x x0xx7x?1
    k=2 ?x4!#xx0 xx0xx7x2
    k=3 x?x9!#xx 0xx0xx73
    k=4 7x?x6!#x x0xx0xx4
    k=5 x7x?x5!# xx0xx0x5
    k=6 xx7x?x6! #xx0xx06
    k=7 0xx7x?x9 !#xx0xx7
    k=8 x0xx7x?x 4!#xx0x8
    k=9 xx0xx7x? x1!#xx09

    說明:
    k 表示末位數數值,
    x 表示 0-9 的數字,任選
    ! 位的數值必須大於等於 1
    ? 位的數值必須等於EAX 除10 所得的餘數,其中
      @EAX=n1*0+n2*1+n3*2+n4*3+n5*4+...+n13*12+n14*13+n15*14+n16*15 (ni 表示註冊碼第i 位的數值)
      當末位數為 0 時,EAX=@EAX-n15*14-n14*13
      當末位數為 1 時,EAX=@EAX-n15*14
      當末位數為 2 時,EAX=@EAX-n2*1
      當末位數為3-9 時,EAX=@EAX-ni*(i-1)-n(i-1)*(i-2)
    # 位的數值不能為0,且能被SUM 整除,其中:
      SUM=2*(n1+n3+n5+n7+n9+n11+n13+n15)-9*y+(n2+n4+n6+n8+n10+n12+n14+n16)
      (y 表示:大於等於 5 的奇數位的個數)

    那麼如何快速的得到一個註冊碼呢?
    根據上面的模型,首先選擇一個末位數,不妨選"9" 吧,其註冊碼模型為"xx0xx7x? x1!#xx09",對於x 部分可讓它們為"0",這樣註冊碼變為" 0000070? 01!#0009",對於# 位的數字來說,當# = "1" 時,一定可以被整除,故令#="1",對於! 位的數字來說,! 位恰好是第11 位,該位數字乘以10(11-1) 一定被10 整除,故對? 位沒有影響,所以可以令! 位的數字為1-9 中的任意一個,這樣註冊碼變為"0000070? 01510009",就剩最後一位了,0 這麼多,好辦,7*(6-1)+1*(10-1)+5*(11-1)+1*(12-1)+9 *(16-1)=35+9+50+11+135=240,240 Mod 10 = 0,故? 位為"0",這樣註冊碼就為"00000700 01510009".其實上面的# 位可以不參加運算,原因已經說過,這裡寫出來只是為了大家看著方便.

    當然使用註冊機KeyGen 是最方便的,不過我不想寫keygen,有興趣的朋友可以試試---我本來的目的也不是破解.又抽空看了看Reglo 的姊妹軟件Deletor,呵呵,註冊算法很相近呀,不過把註冊失敗的對話框彈出方式變為了MessageBoxW,註冊碼也變為了UniCode,新手們可以用它的姊妹軟件進行練手,是成長的不錯選擇.

    哎,終於完了!神馬都是浮雲!

2011年7月16日星期六

IDA Pro Advanced 6.1 Full + Hex-Rays 1.5

IDA是一個強大的反彙編工具,這是IDA 6.1版本,其中包括:

Hex-Rays.IDA.Pro.Advanced.v6.1.Windows.incl.Hex-Rays.x86.Decompiler.v1.5.READ.NFO-RDW
Hex-Rays.IDA.Pro.Advanced.SDK.v6.1-RDW
Hex-Rays.IDA.Pro.Advanced.FLAIR.v6.1-RDW
Hex-Rays.IDA.Pro.Advanced.IDS.Utilities.v6.1-RDW
Hex-Rays.IDA.Pro.Advanced.LOADINT.v6.1-RDW
Hex-Rays.IDA.Pro.Advanced.TILIB.v6.1-RDW
Hex-Rays.IDA.Pro.Advanced.v6.1.TVision.v2009b.Source-RDW

下载地址: http://filemarkets.com/file/newbing/0c22cc9f/
          http://www.filesonic.com/file/1455039374/i61f.rar