close

【文章標題】關於時間限制的拆除
【文章作者】: kanghtta
--------------------------------------------------------------------------------
【詳細過程】
  
廢話   
  
大家好,好久不見了! 前段時間朋友讓我CRACK一個國產軟體,和軟體的作者交流了一下,由於征得他的同意,就開始破了
  VB
寫的,運行5分鐘就自動退出,由於對反編譯的p-code不太熟悉,就把它爆破了! 
    
可這兩天又從網上下了一個,一開始就遇上用易語言寫的殼,好不容易把這層烏龜蓋那了,又遇上異常處理,附加資料,
  
反跟蹤,這些內容都不會阿,怎麼辦啊,學阿!  所以crackme就做的少了點。不過也打算潛水段時間來把作業系統的,記憶體管理,進程管理
  
還有檔,和I/o等知識好好深入一下,自我感覺破解的過程中如果你對這些知識有一個深度的掌握,更能讓你得心應手。
  
正文:
  
今天寫的是關於時間限制的拆除,我用的是段大哥出的 《加密與解密》第二版,首先聲明,本文大部分內容來源於這本書中,
  
只不過是當作小弟的學習筆記寫出來;也算是對所學知識的一種總結和鞏固;
  
理論
  1
:)時間限制
     
當我們運行某個沒註冊的軟體時,可能會發現它會給你一個提示,意味著你使用的是未註冊版本;大一點的軟體一般讓你用
  30
天,卡巴 的試用 也算是一種時間限制吧! 它在你使用了30天后,會提示你重新註冊,不過有個辦法可以繞過這個限制,
  
就是找到相關的註冊表鍵值,刪了就可以! 其它小一點的軟體,可能會讓你運行幾分鐘後,就將程式殺了! 
    
總的來說,時間限制有兩類: 
  
第一種: 限制你每次運行的時間,比如:每次你只能使用目的程式幾分鐘,或幾個小時;
  
第二種:不限制你每次運行的時間,但是有個時間段的限制,  如上面說的卡巴;
  2:)
關於時間限制的保護方式
    
程式在第一次運行的時候,或在安裝的時候,取得當前系統日期,並且將它記錄在系統中的某個地方;程式在每次運行時
  
都要取系統時間,並將其和記錄下的時間進行比較,當其差值大於某個約定的時間後就停止運行,並提示用戶註冊;
  3
:)計時器 
     
有時間限制的程式,自然有一個計時器統計程式運行的時間。
     
那麼什麼是計時器以及如何使用它?
  
   
一個內在的例行程式,只要指定的時間間隔過去,系統就發送一個WM_TIMER消息到對應的訊息佇列中;它在程式運行時
  
不可見的;
   
下面是羅雲兵的 win32 程式設計書中有關計時器的一部分:
    Windows 
的計時器是一種輸入裝置,它週期性地在指定的間隔時間通知應用程式。它可以用向指定視窗發送 WM_TIMER 消息或者調用指定的過程來執行使用者的程式。計時器的應用主要包括下面一些地方:
  
  
時鐘程式 - 顯然,這是計時器最直接的應用。 
  
多工 - 如果程式有大量的資料處理,除了用多執行緒的辦法,還可以用計時器,在每一個計時器消息中處理一小塊內容。 
  
定時顯示程式的狀況 - 計時器就相當於 Dos 程式設計中的自己掛接在 int 1ch 上面的要定時處理的程式,它可以定時顯示程式運行的情況,如發送了多少內容,接收了多到內容等等。 
  
在遊戲程式中使用計時器可以消除在不同處理器下用延時來保持速度一致所造成的誤差。 
  
用於資料流程處理 - 在音訊、視頻的播放中,需要隔一段時間處理一段資料。 
  
總的來說,在 Dos 下實現精確定時的唯一方法是在 int 1ch 時鐘中斷中處理常式,但你使用起來必須遵守很多的規範,而在 Windows 的計時器中,你可以用 SetTimer 函數分配不止一個的計時器,比如說,在你的文本編輯程式中,你可以使用一個間隔1秒的計時器來在狀態列中顯示時鐘,同時分配一個10分鐘的計時器來實現定時存檔的功能。計時器實際上是 Windows 對時鐘中斷的一種擴展,它的本質還是基於時鐘中斷的,所以你實際上無法把計時器的間隔設置到55毫秒以下,另外,計時器的精度也是以55毫秒為倍數的,比如說,你設置了一個1秒的計時器,它實際上是在每989毫秒的時候發生的。和在 Dos 下使用時鐘中斷,windows 的計時器還有下面一些要點:
  
  
 Dos 中,你的程式隨時可能被 int 1ch 打斷,而在Windows 中,Windows 通過 WM_TIMER 消息把計時器消息放入正常的訊息佇列中,所以你不必擔心你的程式在別的處理中被計時器打斷。 
  
不可能有同時兩條以上的 WM_TIMER 消息,如果在一個還在訊息佇列中,視窗再得到一條 WM_TIMER 消息,兩條消息會被合併為一條,所以在程式比較忙的時候可能會丟失 WM_TIMER 消息。 
  WM_TIMER 
消息的級別是很低的,程式只有在訊息佇列中沒有其他消息的情況下,才會接收 WM_TIMER 消息,你可以通過下馬方法驗證:在一個設置了計時器的視窗上按住標題列移動視窗,你會發現計時器停止了工作,當你鬆開滑鼠後,在這個過程中丟失的 WM_TIMER 消息並沒有被補上,所以如果你設計一個時鐘程式,你不能使用計時器消息來計數,而必須在消息中每次獲取正確的系統時間。 
  
講了這麼多計時器的特點,下面是計時器相關的API,你會發現除了在使用中要注意的這些特性,計時器的API真是又少又簡單:
  
  
建立計時器
  SetTimer( 
  HWND hWnd, // handle of window for timer messages 
  UINT nIDEvent, // timer identifier 
  UINT uElapse, // time-out value 
  TIMERPROC lpTimerFunc // address of timer procedure 
  );
  hWnd 
 windows 發送 WM_TIMER 的視窗,nIDEvent 是計時器的編號,在 WM_TIMER 中出現在 wParam 參數中,用來區分在多個計時器的情況下,這條消息是由哪個計時器產生的。uElapse 是計時器間隔的毫秒數,如果你要設置一個1秒的計時器,這個值就是1000lpTimerFunc 是處理計時器消息的過程,如果這個參數不是 NULLwindows 在到時間後會調用lpTimerFunc 指定的過程,調用的參數是 CALLBACK TimerProc(hwnd,WM_TIMER,iTimerID,dwTime)iTimerID 是計時器 IDdwTime 是系統時間;如果 lpTimerFunc 參數是 NULLWindows 會把 WM_TIMER 消息放入消息迴圈中,消息的 hWnd 是第一個參數中指定的 hWnd,也就是說向這個視窗發送了 WM_TIMER 消息。
  
另外,如果你的程式沒有視窗,你也可以用這種辦法建立計時器:invoke SetTimer,NULL,NULL,uElapse,TimerProc,函數會返回一個系統定義的 TimerID供你在 KillTimer 中使用。
  
  
取消計時器
  KillTimer( 
  HWND hWnd, // handle of window that installed timer 
  UINT uIDEvent // timer identifier 
  );
  
取消計時器只需對應 SetTimer 時的 hWnd  uIDEvent 調用 KillTimer 函數就行了。 
  
  4: ) 
獲取時間的API函數
     
取得時間的API函數一般有:  
  
一:GetSystemTime
  The GetSystemTime function retrieves the current system date and time. The system time is expressed in Coordinated Universal Time (UTC). 
  GetSysTemTime
函數獲取當前系統日期和時間.系統時間基於格林威治時間表示.
  VOID GetSystemTime(
    LPSYSTEMTIME lpSystemTime   // address of system time structure
  );
   
  Parameters
  
參數: lpSystemTime 
  Pointer to a SYSTEMTIME structure to receive the current system date and time. 
  
指向一個返回當前系統日期和時間的SYSTEMTIME結構指標;
  Return Values: 
函數無返回值;
  
  
二:GetLocalTime
  The GetLocalTime function retrieves the current local date and time. 
  GetLocalTime
函數返回本地當前的日期和時間;
  
  VOID GetLocalTime(
    LPSYSTEMTIME lpSystemTime   //  address of system time structure
  參數同上
  ); 
   
  
  
三:GetFileTime
  GetFileTime
  
函數返回檔被創建或是最後一次被存取和修改的日期和時間;
  BOOL GetFileTime(
    HANDLE hFile,                 // handle to the file  
檔案控制代碼
    LPFILETIME lpCreationTime,    // address of creation time  
創建檔的FILETIME結構體位址
    LPFILETIME lpLastAccessTime,  // address of last access time  
最後存取[訪問]檔的FILETIME結構體位址
    LPFILETIME lpLastWriteTime    // address of last write time  
最近寫入時間的FILETIME結構體位址
  );
  
  
軟體作者也可能用高階語言封裝的類來作業系統時間,但封裝的類同樣要調用這幾個函數;
      
還有一種方法就是讀取需要頻繁訪問的系統檔的最後修改日期,在利用FileTimeToSystemTime()將其轉換為系統時間格式;
  
從而取得當前系統時間;
    
注意:如果你打算在你的軟體中打算用時間保護,那麼你的軟體必須要能防RegMon FIleMOn之類的監視軟體,
  
否則,拆除這種時間保護將會變得相當容易;
  
  
實踐練手:
  
  
   首先照本宣科,由於時間保護的軟體我機子裡庫存不多,就用下看雪老大光碟裡的:timer.exe
  1: 
運行下程式看看,有兩個文字方塊:
  
第一個: 展示版本本將運行20秒,20秒後它將自己關閉,你需要重新運行它;
  
第二個:  這是一個演示時間限制的crackme版本
  
  2: 
PEid查看  
  
 什麼都沒找到  * 嘿嘿,,,
  3:OD
載入看看,ctrl+N 看看有沒有我們上面所說的那些函數;
  
  
名稱位於 Timer
  
地址       區段       類型    (  名稱                                    注釋
  00402024   .rdata     
輸入    (    USER32.DestroyWindow        ;銷毀窗口
  00402018   .rdata     
輸入    (    USER32.DialogBoxParamA      :對話方塊創建
  00402010   .rdata     
輸入    (    USER32.EndDialog            : 結束對話方塊
  00402004   .rdata     
輸入    (    KERNEL32.ExitProcess        
  0040201C   .rdata     
輸入    (    USER32.GetDlgItem 
  00402000   .rdata     
輸入    (    KERNEL32.GetModuleHandleA
  00402028   .rdata     
輸入    (    USER32.KillTimer            :關閉計時器
  0040202C   .rdata     
輸入    (    USER32.LoadIconA
  0040200C   .rdata     
輸入    (    USER32.PostMessageA  
  00402014   .rdata     
輸入    (    USER32.SendMessageA
  00402030   .rdata     
輸入    (    USER32.SetTimer      :創建計時器   
  00402020   .rdata     
輸入    (    USER32.wsprintfA
  00401000   .text      
輸出         <模組入口點>
  
  4: 
程式用setTimer創建了一個計時器,在命令視窗中: bpx SetTimer 對此函數下斷,
  
或者在00402030   .rdata     輸入    (    USER32.SetTimer   這行上F2 也可以;
  F9
運行程式,程式被斷下來了;
  004010BC   . /0F85 86000000 jnz     00401148
  004010C2   . |8B7424 08     mov     esi, dword ptr [esp+8]           ;  Case 110 (WM_INITDIALOG) of switch 004010A5
  004010C6   . |6A 00         push    0                                ; /Timerproc = NULL
  004010C8   . |68 E8030000   push    3E8                              ; |Timeout = 1000. ms
  004010CD   . |6A 01         push    1                                ; |TimerID = 1
  004010CF   . |56            push    esi                              ; |hWnd
  004010D0   . |FF15 30204000 call    dword ptr [<&USER32.SetTimer>]   ; \SetTimer
  004010D6   . |A1 04304000   mov     eax, dword ptr [403004]
  
  
如何拆除世間限制呢:我們在來看看SetTimer函數的定義
  SetTimer
  The SetTimer function creates a timer with the specified time-out value. 
  SetTimer
函數創建用指定的時間間隔值創建一個計時器
  
  UINT SetTimer(
    HWND hWnd,              // handle of window for timer messages ; Timer
消息發往視窗控制碼
    UINT nIDEvent,          // timer identifier   ;
計時器標識 
    UINT uElapse,           // time-out value    : 
以毫秒為單位的時間間隔值,每隔這個時間,計時器就發送計時器消息給指定的視窗
    TIMERPROC lpTimerFunc   // address of timer procedure   
如果此值為NULL,系統發送一Wm_timer消息給應有程式訊息佇列;
  );
  
  
方法一JMP 跳過
  
   1:) 
Hiew 打開Timer.exe,回車來到代碼模式
   2:) F5
鍵後輸入虛擬位址 .004010C6
   3: ) F3
進入編輯模式後,回車鍵輸入: jmp 10D6 ESC鍵退出,F9 保存;
   4: ) 
運行程式,那個計數的視窗裡再也沒有數值跳動了;  
   
  
方法二:改UINT uElapse,           // time-out value  這個參數的值
  
 注意: 參數調用約定的堆疊順序,還有就是UINT16位不帶正負號的整數,注意修改後的值不要超出表示範圍,否則出現上溢;
  
   unsigned [int] 
表示數的範圍 :  0-4294967295  應該有好幾天了吧嘿嘿...
    1:) 
Hiew 打開Timer.exe,回車來到代碼模式
   2:) F5
鍵後輸入虛擬位址 .004010C8
   3: ) F3
進入編輯模式後,回車鍵輸入: PHSH 71266300 ESC鍵退出,F9 保存;
   4: ) 
運行程式,那個計數的視窗裡再也沒有數值跳動了;
  
  
方法三:  利用WM_TIMER , 這時你需要查找 VC標頭檔WINUSER.H 的消息定義 得到: #define WM_TIMER 0x0113
  
  
W32Dasm裡查找113字串 ,find text 對話方塊中輸入 : 113 
  
  :00401175 3D13010000              cmp eax, 00000113      
每隔一秒出現一次
  :0040117A 75CC                    jne 00401148         
  :0040117C A108304000              mov eax, dword ptr [00403008]
  :00401181 83F813                  cmp eax, 00000013      
是否超過20,1316進制數
  :00401184 7FB1                    jg 00401137            
比較 ,超時跳走就完蛋,
  :00401186 40                      inc eax
  :00401187 8D4C240C                lea ecx, dword ptr [esp+0C]
  :0040118B 50                      push eax
   
  :00401184 7FB1                    jg 00401137 
  
我們將此處用NOP代替,直接爆了它;
    1:) 
Hiew 打開Timer.exe,回車來到代碼模式
   2:) F5
鍵後輸入虛擬位址 .00401184
   3: ) F3
進入編輯模式後,回車鍵輸入: 9090 ESC鍵退出,F9 保存;
   4: ) 
運行程式,那個計數的視窗裡計數數值超過20,但程式還沒有退出;
  
  
   
好了,今天就到這了,吃飯去了,今天下了很大雨哦我喜歡這天氣祝大家玩得愉快

arrow
arrow
    全站熱搜

    殘月影 發表在 痞客邦 留言(3) 人氣()