TAG

首都機能移轉 (2) 歌詞 (2) 靠北文 (40) 戲言 (30) 糟糕 (7) ACG (23) Assembly (2) Boost (2) C (31) C++ (69) CMake (4) CSIE (67) Debian (34) Design_Pattern (2) Django (1) Eclipse (1) en_US (13) FFmpeg (3) FoolproofProject (26) FreeBSD (2) Git (4) GNU_Linux (65) IDE (5) Java (11) JavaScript (19) KDE (15) Khopper (16) KomiX (3) Kubuntu (18) Life (1) Lighttpd (2) Mac_OS_X (2) Opera (1) PHP (2) PicKing (2) Programing (21) Prolog (1) Python (7) QSnapshot (2) Qt (30) Qt_Jambi (1) Regular_Expression (1) Shell_Script (7) Talk (98) VirtualBox (7) Visual_Studio (13) Windows (18) zh_TW (36)

2008年12月31日 星期三

Self extract loader

以下說明一個較為粗糙的自解檔寫作方式。完成品分為三個部分,啟動器,分隔識別字,以及被包起來的本體,也就是說是二個檔案硬接在一起,中間用一個字串當分隔。
本體程式為了方便,我用一個簡單的shell script做範例,在真實狀況可以是任何檔案。檔名為start.sh。
#! /bin/sh
echo 'Hello, world!'
分隔識別字我使用字串"__METAMAGIC__",magic number[!]請挑選很難重複的字串。
接下來就是關鍵的啟動器了:
#include <fstream>
#include <string>
#include <list>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <cstdlib>

int main( int argc, char * argv[] ) {
    using namespace std;
    ifstream fin( argv[0], ios::binary );

    if( fin.is_open() ) {
        list< char > self;
        copy( istreambuf_iterator< char >( fin ),
              istreambuf_iterator< char >(),
              back_inserter( self ) );
        fin.close();

        string magic( "!_METAMAGIC__" );
        magic[0] = '_';
        list< char >::iterator result = search( self.begin(), self.end(), magic.begin(), magic.end() );
        if( result == self.end() ) {
            cerr << "Can not find piece!" << endl;
        } else {
            advance( result, magic.length() );
            ofstream fout( "/tmp/injection", ios::binary );
            if( fout.is_open() ) {
                copy( result, self.end(),
                      ostreambuf_iterator< char >( fout ) );
                fout.close();
                list< char >().swap( self );
                system( "chmod a+x /tmp/injection && /tmp/injection && rm -f /tmp/injection" );
            } else {
                cerr << "Can not write `/tmp/injection\'!" << endl;
            }
        }
    } else {
        cerr << "Can not open " << argv[0] << "!" << endl;
    }

    return 0;
}
首先以二進位的方式讀取自己,並存到容器內:
copy( istreambuf_iterater< char >( fin ),
      istreambuf_iterator< char >(),
      back_inserter( self ) );
istreambuf_iterator是讀取輸入緩衝區的迭代器,使用copy演算法把內容一字不漏地複製進容器內。
接著要找分隔字了,這裡就要一點技巧:
string magic( "!_METAMAGIC__" );
magic[0] = '_';
不直接使用"__METAMAGIC__"是因為字面常數會被放到text區段,如此一來會被干擾識別字搜尋。
接著就可以愉快的使用STL找字串了:
list< char >::iterator result = search( self.begin(), self.end(), magic.begin(), magic.end() );
如果找不到就會回傳self.end(),這裡先假設它找得到,就直接寫入:
advance( result, magic.length() );
ofstream fout( "/tmp/injection", ios::binary );
if( fout.is_open() ) {
    copy( result, self.end(),
          ostreambuf_iterator< char >( fout ) );
    fout.close();
因為search回傳的是"__METAMAGIC__"字串的開頭,所以要用advance用來把迭代器往前推,讓它指向第二段檔案開頭。輸出檔放在/tmp下是因為這裡通常是任何人皆可使用。
list< char >().swap( self );
這行只是為了把list裡的空間釋放,對程式本身來說可有可無。如果啟動器本身會執行很久,那麼釋放不必要的部分也許比較好。
最後再執行它,就完成了:
system( "chmod a+x /tmp/injection && /tmp/injection && rm -f /tmp/injection" );
之後編譯它:
g++ -O3 -o sfx sfx.cpp
Do something evil--把二段程式黏起來:
echo -n '__METAMAGIC__' | cat sfx - start.sh > inject
之後執行inject就可以看到:
$ ./inject
Hello, world!
這種方式不免讓人聯想到某些病毒的入侵方式--在看似正常的檔案內插入攻擊代碼,利用自己製作的shell或其他程式[?]的bug,解出攻擊碼並執行。
當然這個啟動器是肥了點,因為完全只用C++內建的標準工具,惡意的啟動器通常都只有幾K而己。

沒有留言:

張貼留言