羽夏逆向破解日記簿——RunAsDate的實現原理分析


前言

  RunAsDate是一個小工具,允許您在指定的日期和時間運行程序,不過有人用它來破解有時間限制了。此實用程序不會更改計算機的當前系統日期和時間,但只會將指定的日期/時間注入所需的應用程序。該軟件是個免費軟件,可以通過 官網 下載。有一天想看看它到底怎么實現的。經過分析是通過注入dllHook幾個關於時間獲取API實現的。32位的和64位的代碼實現沒啥區別,本篇以64位進行分析,32位感興趣自行分析。

主角和工具

  • Detect it easy 1.01
  • IDA 7.5
  • X64Dbg
  • RunAsDate

探測

  我們先到官網下載原汁原味的程序,如下圖所示進行下載:

  解壓后,得到了3個文件:

  運行該軟件,界面如下:

  為了檢驗工具的效果,先寫一個獲取時間的C代碼(隨便一個語言就可,不過代碼需要自己寫),如下:

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
int main()
{
    time_t t;
    time(&t);
    struct tm _t;
    errno_t err = localtime_s(&_t, &t);
    err ? puts("獲取時間失敗!!!") : printf_s("%d/%0.2d/%0.2d\n", _t.tm_year + 1900, _t.tm_mon + 1, _t.tm_mday);
    system("pause");
    return 0;
}

  編譯運行,獲取得到當前時間:

  來試試工具有沒有效果,注意把下圖所示的紅框框住的選項選中,否則沒效果,然后點擊運行,發現起作用:

初步分析

  既然工具咋用已經探測完畢了,我們來進入分析環節。先用Detect it easy 1.01探測一下:

  是C++寫的,且沒有任何加殼,為逆向分析降低了足夠的難度。直接拖到IDA進行抄底。啟動一個新進程的API函數有很多,有ShellExecuteEx系列函數、ShellExecute系列函數和CreateProcess系例函數。我們都給下上斷點,來看看它到底是用哪個函數啟動的,設置好點擊運行,斷點斷到CreateProcessW,如下圖所示。

  通過堆棧定位可以定位到調用地址,如下圖所示:

  定位到IDA中,然后大體分析一下函數,重命名一些函數方便進一步分析,並發現一些可疑函數,命名好名字,關鍵偽代碼如下面幾張圖所示:

  下圖是執行上面子函數的主過程。有經驗的一看就知道,這就是典型的遠程線程注入過程。

  什么是遠程線程注入呢?首先你准備一個待注入的Dll,假設名字為A.DLL。如果加載一個Dll,常規的辦法就是用LoadLibrary函數加載。然而我是A進程,想讓B進程調用函數,就必須啟用一個線程,需要調用的API就是CreateRemoteThread函數,只能通過lpParameter傳遞一個參數。它們的函數原型如下:

HANDLE WINAPI CreateRemoteThread(
__in HANDLE hProcess,
__in LPSECURITY_ATTRIBUTES lpThreadAttributes,
__in SIZE_T dwStackSize,
__in LPTHREAD_START_ROUTINE lpStartAddress,
__in LPVOID lpParameter,
__in DWORD dwCreationFlags,
__out LPDWORD lpThreadId
);


HMODULE WINAPI LoadLibrary(
 _In_ LPCTSTR lpFileName
);

  而每一個進程中,LoadLibrary函數地址都是一致的。故我需要通過一段ShellCode,將一些參數聚合起來直接傳給它,讓它負責分析傳來的參數並實現注入功能。而ShellCode怎樣寫到B進程呢?通過VirtualAllocate(Ex)函數和WriteProcessMemory函數配合將ShellCode和待解析的參數一塊寫進去。從IDA分析結果可知,ShellCode就是sub_140006B3C,我們來看一下:

  為了方便閱讀,偽C代碼如下:

  從ShellCode很明顯看出。a1就是通過CreateRemoteThread傳來的參數。a1的地址加40偏移的值就是一個函數地址,在把a1的地址加上64的偏移作為參數傳入得到返回值v2v3應該是一個函數地址,通過a1加上48的偏移,把v2和a1加上586的偏移作為參數傳入得到返回值v3,如果返回值不為空調用v3,把a1加上620的偏移作為參數進行傳入。只從ShellCode看不太出來是啥,如果有經驗就應該大體能猜出來。具體分析一下它傳過來的參數是如何包裝的。

  從上圖發現*(lpFileName + 66)就是所謂待注入進程調用ShellCode的參數。而從Buffer存儲寫進去,但遺憾的是,我壓根不知道Buffer里面的內容到底是啥,看前面的偽代碼也看不出來,只看到它的首地址被清零。這個就是F5插件的缺陷。它反編譯成偽C代碼不是准確的,甚至是錯誤的,比如函數調用的參數有時會有錯誤,或者直接JMPOUT,還是看匯編准確。不過我們可以看一下BufferIDA識別為char,但明顯看到使用該變量的函數告訴咱們這個是大小至少為0x288char數組。故我們可以看看Buffer的堆棧來進行修正。

  從堆棧可以明顯看出所謂的fucs就是屬於Buffer的,所以上面的部分就是給參數打包的過程。我們直接改Buffer,將它改為char Buffer[0x288]。然后重新F5,看一下代碼的變化:

  反編譯出來的偽C代碼就看起來正常了,雖然看起來還是比較別扭,不過包裝參數的過程比較明晰了。然后我們把它拖到X64Dbg,來進行驗證一下過程,我們先通過下VirtualAllocEx斷點獲取它們返回的申請到的物理頁虛擬首地址,把它記錄下來。

  然后在CreateRemoteThread下斷點,停住創建的待注入的程序。然后另起一個X64Dbg附加上,直接看一下記錄的地址的內容。首先我看的是參數部分,直接在內存窗口選好0x288字節保存到文件,方便一一對應,如下圖所示:

  下圖就是所謂的ShellCode,在函數頭部下一個斷點:

  然后放開RunAsDate程序,被注入的程序就停在上一步下的斷點上,然后我們一步一步的跟,看看調用的是什么函數,先跟到第一個,發現是LoadLibraryW函數:

  其次就是GetProcAddress函數,用來獲取DllInitDate函數地址:

  然后校驗獲取是否成功,傳參FILETIME結構體調用InitDate函數:

  根據IDA和X64Dbg的分析,每個數據的對應如下圖所示:

深入分析

  通過初步分析我們知道了這個軟件的基本原理。但功能實現全部在所謂的dateinjo1_64.dll當中。我們可以在注入的程序時,直接從臨時文件夾薅出來。在之前函數分析也知道它是直接把資源釋放出來的,直接用資源編輯工具也能提取出來,這我就不繼續描述了,直接開始分析。具體分析過程就不詳細描述了,完全憑自己的正向開發經驗,這個Dll很簡單,最后命名好名字后如下圖所示:

  綜上可以看出,它是通過對GetLocalTimeGetSystemTimeGetSystemTimeAsFileTime這三個函數進行掛鈎子。怎么掛鈎子的可以在詳細介紹一下:

  可以看出,它是通過構造一個movjmp eax進行Hook,感覺不直觀,來看一下下面一個被RunAsDate注入的被掛鈎的函數:

  通過上圖,知道為什么代碼要這樣構造了吧?

總結與思考

  • RunAsDate並不高大上,原理很簡單,其實本質就是用了遠程線程注入+Hook所有獲取時間函數的API的方式實現時間的控制。
  • 有些人利用RunAsDate,來破解一些軟件時間使用限制。想要保護自己商業軟件的合法權益,可以通過它的實現原理,檢查是否程序模塊有沒有它的Dll,有的話直接退出程序或者修復Hook。也可以檢查函數是否被Hook的方式進行反制。
  • 使用IDA時,不要過於依賴F5,雖然直觀方便,但它反編譯出的偽C代碼並不是完全正確的,甚至是錯誤的,如果想弄對必須進行修改調整。
  • 軟件的Hash值:EF847F60C02856AB013438D7A55A6CC1
  • 64位的注入的Dll的IDA分析結果:藍奏雲下載 —— 密碼:f854


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM