『アナライジング・マルウェア』の感想

アナライジング・マルウェア ―フリーツールを使った感染事案対処 (Art Of Reversing)
自分の直感に最初から従っておけば良かった。でももう遅い。買ってしまったものは仕方がない。結論から言うと、この本に買う価値はない。
そこら辺のウェブページを切り貼りしました、って感じ。この本でしか得られない情報ってImmunity Debuggerのしょうもないノウハウ(笑)ぐらいしかない。
まず、問題なのはこの一冊で全くの初心者がマルウェア解析を出来るようになると謳っている点。そんなことは全然ない。確かに中学生が作ったようなマルウェアならレジストリのどこを操作しているのかぐらいなら分かるようにはなりそうだけど、そんなので溢れている訳じゃないでしょ?このレベルの自称マルウェア解析者が増えればマルウェア作成者は楽になるね。凝ったことしなくても解析を諦めてもらえるから。「あれー?このマルウェア、実行させてもBlackMantaに何も表示されないぞ?きっと何もしてないんだな」ってね。
それと、気付いたのだけれど統一的な対処が困難なアンチ・リバースエンジニアリングについての記述がほとんどないね。パッカーについての言及はあるけれど(パッカーのために一章割かれている)、メモリーにオリジナルコードが早い段階で全て展開されることが前提で、そうでないときの対処法が全く書かれていない。

私たちが一番苦心したことといえば、読者がアセンブリ言語を読むことを極力小さくすることです。(p.vi)

と前書きにもあるし意図してのことなのだろうけど。VMプロテクションにはどうするんだろうね?こんなのでマルウェアを解析できるようになると考えたのか本当に疑問。
編集作業も雑。こんな箇所が至るところにある:

OEPに制御を移すようなjmp/call命令では、オペランドが即値であったり、汎用レジスタであったりすることがあるので、この点に着目してOEPを探してもよいでしょう。(p.61)

オペランドが即値でも汎用レジスターでもないようなjmp/call命令っていったいどんな命令だろう?jmp espとか?そういえば双方向リストの図もおかしかった。二つのリンクのうち、片方が前方、もう片方が後方を指してないといけないのに片方が両方向を指している。(p.225の図)
こうして見るとサルカニ合戦しているみたい。木に登れないカニを初めとする動物たちには木の上から善人を装い渋柿だけを与える。しかしカニたちは熟れた柿の色を知らないので喜んで食べる。アホか。もしかしたら本人は本気で渋柿を熟れている柿だと勘違いしているのかもしれないけれど。
というわけで、この本を買って読むのは無意味。ラノベでも読んでた方がましです。

NetAgent Security Contest 2010 参戦記

11/27-11/28に開催されたNetAgent Security Contest 2010に参加しました。問題は8問出題され、私に解けたのは1,3,6,7番の問題です。各問題について私がどのように解いたかを書こうと思います。

Level 1 (Activation)

使用したツール: OllyDbg

2010年8月14日に起動させると表示されるパスワードを求めよ、というのが問題。まずは普通にOllyDbgで開く。MessageBoxを呼び出しているところの近くでGetSystemTime関数の呼び出しを発見した。呼び出しまで実行して返されたSYSTEMTIME構造体を2010年8月14日になるよう書き換えた。

Level 3 (Crypto)

使用したツール: Reflector, Visual C++

この問題が一番面白かった。問題はC++製のDLLを呼び出しているC#製のプログラムで暗号化したファイルを復号化せよ、というもの。ReflectorでC#製のプログラムを開くと暗号化処理が普通に見える。問題文で暗号化の鍵は示されているが復号化には別の鍵が必要となる。平文1バイトと鍵から暗号化結果が一意に定まるのでそのテーブルを作って復号化する。以下にソースを記す。

int main()
{
    unsigned int g_n = 13511;
    unsigned int g_e = 2645;
    unsigned int numArray;
    unsigned int num3;
    unsigned int temp;
    unsigned char *table;
    FILE *fp;
    FILE *out;
    
    table = calloc(0x10000, 1);
    for (num3 = 0; num3 < 0x100; num3++) {
        fnCrypt_cp(1, &numArray, &num3, g_n, g_e);
        table[numArray] = num3;
    }
    
    fp = fopen("password", "rb");
    out = fopen("decrypted", "wb");
    while (fread(&temp, 1, 4, fp)) {
        fwrite(&(table[temp]), 1, 1, out);
    }
    fclose(out);
    fclose(fp);
    free(table);
    
    return 0;
}

復号化したファイルをバイナリエディタで開くとPNGという文字列が見えるのでpngとして見るとパスワードが書いてある。

Level 6 (UnPack EXE)

使用したツール: うさみみハリケーン

Phant0mでデバッガーチェックを無効化してもOllyDbgで開けなかったので、別のやり方で取りかかることにした。うさみみハリケーンを使ってゲーム改造の要領で勝利数が格納されているアドレスをいくつかに絞り込んだあと10000を超えるように書き換えるとパスワードが表示された。一応解けたからいいが、正攻法ではどのように解くのか気になる。

Level 7 (JavaScript)

使用したツール: Firebug

どこからも参照されていない変数Answerに入る値を求めよ、というのが問題。zipを開くと難読化されたJavaScirptを含んだ.htmlファイルが一つあった。JavaScriptの難読化では最終的にevalしていることが多いので、まず以下のJavaScirptを挿入してevalをフックした。呼び出されるとFirebugのConsoleに引数が出力されるはずだ。

var hook = (function(hooked, hooking) {
    return (function(arg) {
        hooking(arg);
        return hooked(arg);
    });
});
window.eval = hook(window.eval, console.log);

goボタンを押してみてもFirebugのConsoleには何も表示されなかったのでevalは使用されていないことが分かる。
難読化されたJavaScriptを眺めていると何か見覚えがある。調べてみたらjjencodeの出力結果と類似している。解説のPowerPointによると、jjencodeではNumber.constructorでeval相当のことをしているようなので、ものは試しとこれもフックしてみる。

// hook関数は上で書いたので省略する
window.Number.constructor = hook(window.Number.constructor, console.log);

goボタンを押すとConsoleにいくつか出力された。中でも以下が注意を引く。

var s="$=/p..D/.exec(window.Na['Pwd']);";
alert(s);
var Answer=s.replace(/\W/g,"");

"$=/p..D/.exec(window.Na['Pwd']);".replace(/\W/g,"")を実行して出力された結果を入力してみると「solved!」と表示された。どうやらこれが正解だったようだ。

感想

様々な種類の問題が出題され、とても楽しめました。ただ、フォレンジック系の問題に全く手が出なかったのが残念です。使うツールから分からない状態なので模範解答に期待します。