扭曲變換加密 【目前防止軟體被破解最好的方法】

wx紅杉樹發表於2007-10-16
扭曲變換加密
作者:劉濤濤 me@liutaotao.com
網址:http://liutaotao.com/nqby.txt
一,一般來講,加密就是加殼
我們經常考慮,一個可執行檔案,怎麼樣加密才能安全呢?
一般用的手段,是加殼。加殼工具的工作原理,就是把可執行檔案的程式碼與
資料都進行加密變換,作為資料存放。生成的目標檔案入口程式碼是加殼軟體
準備好的防跟蹤程式碼。經過漫長的防跟蹤程式碼後,會把原始可執行檔案的程式碼
與資料段恢復,然後跳轉到原來的入口處,繼續執行。這樣做的缺點是,不管
你的加密多強,防跟蹤程式碼多牛,只要一執行,在記憶體中就全部恢復了。只要
把記憶體映象dump下來,反彙編一下,就清清楚楚了。甚至有工具可以直接把
dump下來的記憶體映象存為可執行檔案。這樣加密就徹底失敗了。
簡單加殼是不安全的,這大家都知道了。我們一般把上述簡單的加殼方式叫“壓縮殼”。
所以現在的加殼軟體都在上述“壓縮殼”的基礎上,多做了一些工作,比如:
* 防止記憶體被 dump 。這實際上是不可能做到的。因為Windows作業系統就不是
一個安全系統,你怎麼可能做到記憶體不被dump呢?曾有一個殼,我用了多種方法
dump都不成功。但最後還是找到了一個方法成功dump了。我這才驚歎dump原來有
這麼多種方法,真是防不勝防。
* 修改檔案入口程式碼。因為一般軟體都是用常用的幾種編譯器編譯生成的。如果
加殼軟體知道你是用什麼編譯器編的(這很容易),把入口程式碼破壞掉,用另外一
段功能類似的程式碼替換它。這樣dump下來的程式碼就比較難找到正確的入口,直接
被存為一個EXE的可能性就小多了。但還是會被反彙編的。
* 還有一些加殼軟體,支援對一個或幾個重點函式加密。甚至使用了虛擬機器。但他們
都只能重點加密少數幾個函式,不可能把所有函式都加密。而且對這個函式還有很多
要求。這可以想象。如果用匯編寫一個函式,不加ret它可能連函式結束地址都找不到,
怎麼可能加密呢
******
儘管加殼軟體可以使用以上多種技術防止被跟蹤,分析,還原,但我認為,它們仍然沒
沒擺脫“殼”的這個中心思想。以上的這些技術不過是在“殼”的大前提下所做的一些
小的插曲。它仍然是不安全的
二,扭曲編譯的思想
做個比喻。加殼保護就好比是你桌上有寶貝,為了保護它,你在屋外圍了一圈鐵絲網。只
要有人突破了這道鐵絲網,進入你的屋子,一眼就看到了桌上的寶貝。這當然不安全。
重點函式加密的思想,就好比是,我屋外圍了一圈鐵絲網,我還把寶貝放進了
保險箱裡。如果有人突破了鐵絲網,進入屋子,一眼就看到了保險箱。雖然保險箱不會被輕
易開啟,但他如果把保險箱搬走,慢慢分析呢?這也不夠安全。
最安全的,就是進了屋子,卻什麼也找不著。沒有目標,這才是最讓人頭疼的。
現在的編譯器,都是追求生成高效率的執行程式碼。這些程式碼的模式基本一成不變。有經驗
的程式設計師看反彙編程式碼簡單跟看原始碼一樣,毫無祕密可言。如果我們有一個編譯器,它的編譯
目標不是為了高效,而是為了防止被讀懂,那該多好啊!我有C++原始碼,我能看懂。一旦編譯,
誰也別想通過反彙編看懂我想做什麼,或者很難。遺憾的是,這樣的編譯器還沒有。
如果我們自己編一個這樣的編譯器呢?不現實。工作量太大了。即使是找一個開源的C++編譯器
來改工作量也不得了。
直接做一個會加密的編譯器行不通。而一旦編譯連線生成EXE後,就只能加殼了。難道就沒有辦法
了嗎?我想出一個主意,就是加密編譯的中間檔案OBJ,輸出ASM檔案,用ML編譯成OBJ,然後再link連線!
這個方法有幾個好處:
* OBJ檔案格式相對簡單。不象處理C++原始檔那麼工作量大。
* OBJ檔案中保留了很多原始檔的資訊,比如符號名,程式碼與資料,標號等等。方便加密。這些信
息很多在LINK的過程中被丟掉了。所以LINK為EXE後再處理就極不方便了。
* 這是一個全新的思想!對程式碼的加密已經不限於加殼,而是加密每一個函式,每一條指令。再也
沒有一目瞭然的彙編了。
* 可以很容易設定加密的強度。可以根據需要,對一部分程式碼輕量級加密,而對另一部分程式碼重點
加密。
* 可以巢狀加密。重複使用幾種加密變換,無限制地使程式碼膨脹
* 因為是加密OBJ檔案,所以不管DLL還是EXE都可順利加密,驅動程式也可以
基於這個思想,我們的加密軟體就要出臺了!我們暫時叫它扭曲變換器 1.0
三,扭曲變換器
有了思想,就開始動手編碼。原以為OBJ檔案格式是有文件的,工程進度應該很快。沒想到其中還是
有很多內容需要考慮。每每說這是最後一個問題,解決了就沒事了,卻總是後延。前前後後居然寫
了差不多半年時間。
主要遇到的技術問題:
* 彙編器ML會把所有的程式碼放到一個段中,這是不可以的。CL則通常是一個函式一個段。
* 彙編器ML不能生成 COMDAT 段。儘管文件中講它支援COMMON,但加了這個關鍵字無效果。
* 彙編器ML不支援 WEAKEXTERN
* 彙編器ML只支援 defaultlib 這一個 drectve 關鍵字,其它 export, include 等關鍵字不支援.
總之,CL編譯的OBJ其中有很多屬性是ML無法生成的。微軟的masm真的該升級了。
還有一些問題:
* 分不清程式碼與資料。資料段中肯定是資料,但程式碼段中卻有可能不是程式碼,是資料。這時如果你試圖
反彙編它,就會出錯。
* ?????
不管怎樣,這些問題都一一解決了(別問我怎麼做的)。
採用的程式碼扭曲方法:
* 用 JMP 把程式碼打亂。這已經不是什麼新鮮的招數了,但它依然有效。
* 用 JMP 把多個函式纏繞在一起。這樣可以讓分析者找不到函式從什麼地方開始,到什麼地方結束。
* 把 call 改掉。破解者對 call 是極敏感的,這舉可以讓他找不到一個 call。比如,我可以把
 call sub1
    改為:
 mov eax, offset sub1 + 3
 push offset @1
 sub eax, 3
 jmp eax
    @1:
* 把 ret 改掉。破解者對 ret 是極敏感的,這舉可以讓他找不到一個 ret。比如,我可以把ret寫作
 push ecx
 mov ecx, [esp+4]
 add esp,8
 jmp ecx
* 改條件跳。條件跳也是極敏感的指令,比如我們可以把
        cmp reg1, reg2
        jge L_DST    
    L_NEXT:
寫作:
        push eax
        mov eax, reg1
        sub eax, reg2
        shr eax, 1fh
        neg eax
        and eax, L2 - L1
        add eax, L1
        jmp eax
    L1:
        pop eax
        jmp L_DST
    L2:
        pop eax
    L_NEXT:
 再看這個,你能看懂是什麼意思嗎
push offset @@L - offset L_3 + 23h
jmp L_1
L_2:
        jz L_3
        ret 4
L_3:      
        add dword ptr [esp+4], offset L_3 - 23h
        add esp,4
        ret
L_1:
call L_2
        ...
 這裡出現了call和ret,但又不是一般所期望的那種。這裡的call不代表你發現了一個函式呼叫。
這裡的ret不代表是一個函式的結束。
* 使用堆疊代替暫存器。比如:
 MOV     EAX, DWORD PTR [ECX+0AD8h]
 PUSH    EAX
 MOV     ECX, DWORD PTR [EAX]
可以寫作:
 PUSH    EAX
 PUSH    ECX
 MOV     EAX, DWORD PTR [ESP]
 ADD     EAX, 0AD8h
 MOV     EAX, DWORD PTR [EAX]
 MOV     DWORD PTR [ESP+04h], EAX
 PUSH    DWORD PTR [ESP+04h]
 MOV     EAX, DWORD PTR [ESP]
 MOV     DWORD PTR [ESP+08h], EAX
 MOV     EAX, DWORD PTR [ESP]
 MOV     EAX, DWORD PTR [EAX]
 MOV     DWORD PTR [ESP+04h], EAX
 MOV     EAX, DWORD PTR [ESP]
 MOV     ECX, DWORD PTR [ESP+04h]
 ADD     ESP, 08h
你能看懂嗎?很明顯,這個變換是不可逆變換。因為它本來使用了哪個暫存器,已經沒有辦法知道了。
* ……還可以想出很多扭曲變換的方法。化繁為簡只有一種方法,化簡為繁可以有無窮多種方法。
還有一些功能:
* 在C語言中,使用 #pragma code_seg(".code$curve_NoChange") 來指示後面的程式碼不做任何加密。因
為有時候有些程式碼含有大量的迴圈,加密它會嚴重影響效率。
* 在C語言中,使用 #pragma code_seg(".code$curve_Max") 來指示後面的程式碼重點加密。比如後面是
與註冊演算法相關。
現在的扭曲變換器我叫它1.0版,已經非常穩定了。我用它把VC6的庫檔案LIB都處理了一遍,再用LIB.exe工具
寫回LIB檔案中,我們就有了一套加密後的庫。如果用這套庫來LINK你的軟體,分析者就很難從彙編中找到哪
個是printf哪個是strcpy,IDA也無法識別MFC靜態連結的庫函式了。能把VC6的庫都加密一遍不出錯,我
相信它已經很強壯很穩定了。
用它來加密我們做的一個共享軟體,就再也沒人寫出序號產生器了。去讀懂大量的經過以上變換的程式碼是不可
想象的。但還是有人暴破了,真佩服他。我會不斷豐富加密方法,讓暴破者都放棄。
現在的扭曲變換器還只支援VC6使用的COFF格式的OBJ,下一步,要分析一下VS2005的OBJ格式,儘快支援它。
我經常喜歡反彙編一下,分析點什麼東西。我有不少朋友也經常在做反彙編或破解的工作。我不希望扭曲變換
器在網上一公佈,被廣泛使用。有一天我想分析點什麼卻無法下手。所以,這軟體暫時還不提供下載,也不出售。
如果你有一段小程式想測試一下,可以把OBJ發給我,我加密一下給你。如果你有一個商業專案需要安全地加密,
也可以談談。
附上一個為CCG寫的crackme,用扭曲變換器加密,帶部分原始碼,供參考。
 http://liutaotao.com/CrackMe.zip

 LiuTaoTao 2006.7.7

allyesno:附bob破解過程

我們以 使用者名稱 12345678
 錯誤的註冊碼 33333333  為例題

在40147E處下斷,我們可以看到 程式把 我們的註冊碼 算成了16進位制的 12 12 12 12 AF AF AF AF 然後後面繼續,在40180A處把我們的註冊碼 算成 15 16 17 18 B6 B7 B8 B9。。。。。
然後再 405310 把我們的 使用者名稱 展成 31 32 33 34 35 36 37 38。。
到此處為止 在堆疊裡 把我們的使用者名稱 註冊碼展成
堆疊地址 12FF18:
假註冊碼部分16進位制:
32 12 12 12 AF AF AF AF 15 16 17 18 B6 B7 B8 B9 1D 1E 1F 20 BE BF C0 C1 25 26 27 28 C6 C7 C8 00
使用者名稱部分:
31 32 33 34 35 36 37 38 31 32 33 34 35 36 37 38 31 32 33 34 35 36 37

展開後就是使用者名稱與註冊碼混合運算了,

在40226A 處可以看到運算的結果(這個過程最為複雜,的確沒時間仔細分析,所以沒細看)

在 4083DD 處我們可以看到 
SUB EAX,3467ABDE  

這就是爆破口,把他改成 MOV EAX,00
爆破成功!!!!

小弟不才,獻醜了!!!

我很懷戀我的老師(紫竹),希望我的老師身體健康,萬事如意!
QQ:6237126

:eek: 我不能傳附件嗎?哭。。。。反正就是爆破 4083DD處,把他改為 B8 00 00 00 00, 執行就OK了!!!

 

相關文章