有比cracker更加瞭解破解所在嗎?所以就讓我這個cracker來談談軟體保護的方法。本文基於borland c++6.0調試通過。你最好懂點內聯彙編。如有錯誤告訴我。我家裡沒有上網,手頭的開發資料只有borland的協助文檔(其實很不錯)。
首先是anti OD。
1、IsDebuggerPresent
BOOL IsDebuggerPresent(VOID)
這個函數相信大家用得最多。但是也沒有什麼用處。隨便都可以被搞定。
微軟說:
If the current process is running in the context of a debugger, the return value is nonzero.
If the current process is not running in the context of a debugger, the return value is zero.
翻譯過來就是:
如果當前的應用程式在調試器下運行,則傳回值不為0,如果不在調試器下運行,則傳回值為0。
通常我們可以這麼寫:
bool isdebuggeron;
isdebuggeron=IsDebuggerPresent();
if (isdebuggeron)
{……
}
省略符號表示你想要的代碼。當然傻子才會這麼寫。我們用OD一調試就可以在輸入函數中看到IsDebuggerPresent,而IsDebuggerPresent的作用只有用來發現調試器。所以,這個東西絕對不可以出現在輸入函數列表之中。
讓它不顯示在輸入表中方法很多,我來介紹一種不常用的。
我們用IDA分析kernel32.dll。找到IsDebuggerPresent如下:
; BOOL IsDebuggerPresent(void)
.text:7C812E03 public IsDebuggerPresent
.text:7C812E03 IsDebuggerPresent proc near ; CODE XREF: .text:loc_7C874FB1 p
.text:7C812E03 mov eax, large fs:18h
.text:7C812E09 mov eax, [eax+30h]
.text:7C812E0C movzx eax, byte ptr [eax+2]
.text:7C812E10 retn
.text:7C812E10
.text:7C812E10 IsDebuggerPresent endp
然後我們來自己寫一個my IsDebuggerPresent函數。
bool my IsDebuggerPresent ()
{
__asm
{
mov eax, large fs:18h
mov eax, [eax+30h]
movzx eax, byte ptr [eax+2]
}
}
這個就是我們自己的 IsDebuggerPresent,不會再在輸入表中出現 IsDebuggerPresent啦!
簡單實用!
2、FindWindow
HWND FindWindow(
LPCTSTR lpClassName, // pointer to class name
LPCTSTR lpWindowName // pointer to window name
);
Parameters
lpClassName
Points to a null-terminated string that specifies the class name or is an atom that identifies the class-name string. If this parameter is an atom, it must be a global atom created by a previous call to the GlobalAddAtom function. The atom, a 16-bit value, must be placed in the low-order word of lpClassName; the high-order word must be zero.
lpWindowName
Points to a null-terminated string that specifies the window name (the window's title). If this parameter is NULL, all window names match.
Return Values
If the function succeeds, the return value is the handle to the window that has the specified class name and window name.
If the function fails, the return value is NULL. To get extended error information, call GetLastError.
上面的是很簡單的英語。
lpClassName是指向類名字串的指標。
lpWindowName是視窗標題。如果這個參數值為NULL,則所有的類名都會受到檢測。
傳回值,如果函數調用成功,返回視窗的控制代碼。如果不成功,返回NULL。
我們用OD自己的外掛程式視窗工具,就可以知道,它的類名是OllyDBG。lpWindowName我們傳入NULL就好了。
char *str="OllyDBG";
h_od=FindWindow(str,NULL);
if (h_od)
{
……
}
這樣寫就好了。如果OD開著,那麼,我們就可以發現它。
3、GetTickCount
這個可是老牌的檢測方法了。老,但是經典。
我們可以根據因為在調試的時候,在某些地方時間和原來的直接載入相比暫停太多了。所以可以根據時間的長短來猜測是否被調試。我這邊設定的是1s。
int t1,t2;
t1=GetTickCount();
……
t2=GetTickCount();
if (t2-t1>1000)
{
……
}
上面是最簡單和最常用的三個。
我們聽說過虛擬機器吧。聽起來怎麼都不像是一種簡單的菜鳥可以實現的方法。其實,有些時候是我們自己太低估自己了。虛擬機器,最通俗的講就是把一條指令用另外的方式實現。
我們可以在設計演算法的時候可以先把演算法進階語言彙編化,然後再把彙編中的,這些指令用這些函數代替。當然我們有一些個我們自己的原則
1、 邏輯關係複雜的要
2、 能夠讓cracker看懂了之後讚歎的要
3、 廢代碼和效率低下的不要
4、 執行速度特別慢的不要
我想了一個禮拜。終於想到一點可以見人的代碼。所以就貼出來給大家看看。
我們知道
0 xor 0=0
0 xor 1=1
1 xor 0=1
1 xor 1=0
0 and 0=0
0 and 1=0
1 and 0=0
1 and 1=1
0 or 0=0
0 or 1=1
1 or 0=1
1 or 1=1
得出一條定律:xor+and=or。
int and_(int a,int b)
{
__asm
{
mov eax,a
mov ebx,eax
or eax,b
xor ebx,b
sub eax,ebx
}
}
int or_(int a,int b)
{
__asm
{
mov eax,a
mov ebx,eax
and eax,b
or ebx,b
add eax,ebx
}
}
int xor_(int a,int b)
{
__asm
{
mov eax,a
mov ebx,eax
or eax,b
and ebx,b
sub eax,ebx
}
}
上面這寫個xor,and,or是通過邏輯關係類比實現的。讓我們再來根據它們的定義來寫一些個函數。
二進位下:
and是同為1時則為1否則為0
int myand(int a,int b)
{
__asm
{
mov eax,a
mov ebx,b
mov ecx,32
xor edx,edx
l4:
shl edx,1
shl eax,1
jc l1
shl ebx,1
jmp l3
l1:
shl ebx,1
adc edx,0
l3:
dec ecx
jnz l4
mov eax,edx
}
}
這個東西代碼品質一般,但是為了符合我上面的代碼,所以我為了易讀性,捨棄了一些效率。稍微解釋一下。我們依次把a和b依次左移1位,如果兩個都有溢出,那麼edx=edx+1,否則就是edx=edx+0。還要依次讓EDX左移。如果你非要把上面的代碼看懂,那麼對彙編指令的瞭解是不可以少的。比如說只有你瞭解了ADC,SHL指令會對標誌寄存器有影響,再加上對它們本身的瞭解才能看懂。
寫這個東西太費腦力了。or和xor我不寫了。