條件跳轉/控制流
這部分會涉及到高級語言中FOR,WHLIE 和 DO/WHILE 語句。
NOP 指令只是填充材料,用它來取代一些有用的地方。
通常如果 ECX 空閒的話我會用它來配合 LOOP 指令只用,這都得看情況。
循環 2 次
// Parity Flag
另一種循環兩次的方法是如果你所有的寄存器都用完了,你可以使用進位標誌。我們首先清空它(如果還沒有的話),將該標誌保存到棧中,執行我們的代碼,重新保存標誌符,填充,然後再循環。
// using the carry flag
循環 3 次
對於PF,將一個寄存器設置為0,並inc 直到PF=1.
對於SF,將一個寄存器設置為0,並增加 43 到 63 的一個數,直到SF=1。
對於ZF,將一個寄存器設置為0,並增加 85 知道ZF=1.
// Parity Flag
你可能會說,我們也可以簡單的講 EAX 設置為3,然後 dec 直到0,這也是正確的。
// Zero Flag
如果可以用ECX,我們可以用LOOP
// ECX
當 ECX 空閒,就用 LOOP。
如果我們不能用 ECX,將 AL 設置為 -1 然後用 sub,相比於第 2 個例子,這樣能夠為我們節省一個字節。如果你喜歡用加法,你也可以將 AL 設置為 1。
// Zero Flag
循環 4 次
這個會比較簡單,因為對於ZF,256能被4整除,對於SF,256能被128整除。
// Sign
你可以繼續這樣做,但是從上面的例子中你應該學會自己寫。
關於實現涉及條件跳轉的控制流的最後一部分是使用相對偏移量。比如說,現在情況是這樣,有一個其他函數的返回值,你想偽造一個函數來混淆代碼,讓分析代碼變得更困難。這是很基礎的。
雖然這裡只測試了TRUE或FALSE,但如果要測試-1或小於0的有符號值也很簡單啊。
字符轉換
在很多情況下你會想要把一串小寫字母轉換為大寫以及相反操作,或者實現unicode到ansi的轉換。這裡只是展示如何轉換拉丁字母表。
大/小寫轉換
轉換大小寫不過是開關切換鍵的事。觀察下面的字母以及它們的二進制值。
a = 01100001 A = 01000001
b = 01100010 B = 01000010
c = 01100011 C = 01000011
對於小寫,第五位是1(我是從零開始算的)。
如果你想要一串字符串全是小寫或全是大寫,用XOR。
// flip the case
如果你想將字符串都變成小寫但又不想每一位都比較,用OR。
// convert to lowercase
至於要轉換成大寫,將它和0xDF進行 and,這樣第五位就會變為0. 這裡有個問題是,如果是數字或者該字母沒有對應的小寫字母的話,這個方法將失效。
// convert to uppercase
當然你也可以用BTS把它設置為小寫,但是這需要佔用更多字節。
// set to lowercase
或者如果你只想改變那個字母,用BTR。
// flip case
如果你的那串字符串裡有數字或者其他字符會發生什麼呢?有一次我在shellcode裡用了轉化小寫而不是轉換為大寫,因為大寫需要條件跳轉而當時我並不想跳轉。
拿metasploit的代碼 block_api.asm 來舉個例子。
將字符串轉換為小寫,你可以用下面的代碼。記住,這裡是從InLoadOrderLinks 讀取DLL的相關信息,跟Metasploit 讀的不一樣。
;
你肯定不能直接把這段代碼插入到原來的metasploit代碼中去,因為生成的hash值完全不一樣。這只是給你介紹一直方法。
如果你用OR來設置數字0~9的第五位,它們不會有任何改變,因為它們的第五位本來就是1 啊。
同樣的,對於用來做分開模塊名和後綴的“.”也一樣不會改變。比如,用OR可以將KERNEL32.DLL 變成 kernel32.dll 而不需要任何條件跳轉。
但是如果是用SUB指令來講其轉換為大寫,你需要條件跳轉。
Ansi 和 Unicode
好吧,這並不完全是unicode轉換因為我們只用到拉丁字母表。Unicode字符串以兩個null字節作為結束標誌,所以一旦遇到null應該停止。
; esi = unicode in
致謝
有很多人通過分享他們的知識和想法幫我間接完成了這篇博文。文章中的代碼借鑑了這些人的想法:
drizz, r!sc, d0ris, jb, Z0MBiE, WiteG, Vecna, Mental Driller, GriYo, JPanic, Qkumba/Peter Ferrie, Jacky Qwerty, Super, hh86, benny 還有一些我忘了的。
本文由 看雪翻譯小組 lumou 編譯,來源 modexp
如果你喜歡的話,不要忘記點個贊哦!