buffer_overflow 第3章 筆記5 函數名稱的雜湊值
前情提要:
上一章的筆記我們在kernel32.dll裡面,最後找到了LoadLibraryA()的函數位置,其實可以開始用組合語言寫一個自動定位,並且找到LoadLibraryA()位址的程式,但為什摸不開始動手。原因:
我們可以找到存放函數字串的位置,在一個一個字元比對是否為"LoadLibraryA",不過這在組合語言的角度,有點難寫,過程會變的很麻煩。
所以:
本節要用,另外用雜湊值(hash)的方式,來比對。
雜湊值:
簡單來說,"love you"字串經過某種運算可以得到一個固定長度為3的數值,例如=245 ,"oiasjdoiashfoaisjd"字串經過運算後,也得到一個長度3的數值,例如=889
雜湊值有好多種,本例:
使用團體:The Last Stage of Delerium 的演算法,基本運算式如下,
extern char *c; //存放字串
unsigned h =0; //存放雜湊值
while(*c) h=((h<<5)|(h>>27))+*c++; //19為32位元減去13而來,13就是組語0xd
當然你也可以使用其他的雜湊值來計算,不過雜湊值產生的結果盡量不要太長,也盡量不要有英文字母,為了方便比對。
以下程式為該雜湊值小程式以C++撰寫:
原碼來自書本上 :非常感謝作者fon909
//file name fonsimplehash.cpp
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;
unsigned const ROTATE_CONSTANT = 13;
unsigned hash(string const &s) {
char const *c = s.c_str();
unsigned h = 0;
while(*c) h=((h<<(32-ROTATE_CONSTANT))|(h>>ROTATE_CONSTANT))+*c++;
return h;
}
unsigned little_endian(unsigned h) {
return (h<<24)|(h<<8 & 0x00FF0000)|(h>>8 & 0x0000FF00)|(h>>24);
}
void usage() {
cout << "Usage: fonSimpleHash <Function Name> [Function Name #2 #3 ...]\n"
<< "Output hash values for input function names\n"
<< "Version 1.0\n"
<< "Email: fon909@outlook.com\n"
<< "Example1: ./fonSimpleHash LoadLibraryA\n"
<< "Example2: ./fonSimpleHash LoadLibraryA WinExec ExitThread\n"
<< "Or you can put function names in a text file, ex: names.txt,\n"
" one function name per line, and try ./fonSimpleHash < names.txt\n";
}
int main(int argc, char **argv) {
if(argc <= 1) usage();
else {
cout << left << setw(24) << "Function Name" << setw(12) << "Hash Value" << '\n'
<< setw(24) << "-------------" << setw(12) << "----------" << '\n';
for(int i = 1; i < argc; ++i) {
cout << setw(24) << argv[i]
<< hex << setw(12) << little_endian(hash(argv[i])) << '\n';
}
}
}
用組合語言來寫:
-------------------------------------------------------------------------------------------------------------------
假設現在esi的位置是函數字串頭的位置
compute_hash:
xor edi, edi //edi 跟edi 做xor運算 所以edi為0
xor eax, eax // eax為0
cld
compute_hash_again:
lodsb
test al, al
jz compute_hash_finished //如果是00(NULL)就跳到compute_hash_finished,
ror edi, 0xd //將edi 向右旋轉13位元 ,像跑馬燈一樣
add edi, eax
jmp compute_hash_again
compute_hash_finished:
-----------------------------------------------------------------------------------------------------------------
其中:
1.
cld是clear direction flag 的意思 使direction flag為0, direction flag有兩種情況,0跟1,當flag設為0時,esi方向向前 ,當設為1時 esi方向向後 ,為了跟下面的lodsb 配合,lodsb會把esi位址的一個位元組載入到eax,lodsb會根據當前flag是0還是1決定esi+1 還是esi -1,向是往下一個字元移動。
2.
al為EAX 的最後一bytes, test會把al一個個bit 做 AND運算,也就是說當我們lodsb把esi位址的一個位元組載入到eax後,用test來知道是否到了字串尾NULL字元00。
3.
jz 當ZF=1就跳轉到compute_hash_finished 運算結果為0時 ZF被設定為1,若比較相同兩數ZF也會被設為1。
回想一下:
1.
我們要在kernel32.dll找到LoadLibrary函數,我們不想一一比對"LoadLibrary"字串,
所以把"LoadLibrary"變成雜湊值。
2.
假如已經找到LoadLibrary函數的實際位置之後,就可以使用LoadLibrary函數把msvcrt.dll加載進來,我們需要使用msvcrt.dll裡面的兩個函數printf , exit,我們也不想比對他們的名稱,所以
把他們也都變成雜湊值。
留言
張貼留言