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,我們也不想比對他們的名稱,所以
把他們也都變成雜湊值。


所以使用上面的fonsimplehash.exe工具計算這三個雜湊值:




可以看到:
LoadLibraryA 的雜湊值為8e4e0eec
printf               的雜湊值為1e3ca7d5
exit                  的雜湊值為741e48cd


有了雜湊值,就可以開始來寫自動從kernel32.dll抓取LoadLibraryA的組合語言。





下一節:拼出我們第二個shellcode。



                            ã€Œholy shit」的圖片搜尋結果










留言

這個網誌中的熱門文章

buffer_overflow 第1章 筆記 介紹

Buffer_overflow 第2章 筆記1 函數堆疊的故事

buffer_overflow 第3章 筆記6 拼出我們第二個shellcode。