以下說明一個較為粗糙的自解檔寫作方式。完成品分為三個部分,啟動器,分隔識別字,以及被包起來的本體,也就是說是二個檔案硬接在一起,中間用一個字串當分隔。
本體程式為了方便,我用一個簡單的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.cppDo something evil--把二段程式黏起來:
echo -n '__METAMAGIC__' | cat sfx - start.sh > inject之後執行inject就可以看到:
$ ./inject Hello, world!
這種方式不免讓人聯想到某些病毒的入侵方式--在看似正常的檔案內插入攻擊代碼,利用自己製作的shell或其他程式[?]的bug,解出攻擊碼並執行。
當然這個啟動器是肥了點,因為完全只用C++內建的標準工具,惡意的啟動器通常都只有幾K而己。
當然這個啟動器是肥了點,因為完全只用C++內建的標準工具,惡意的啟動器通常都只有幾K而己。
沒有留言:
張貼留言