
暗号化されたシェルコードを隠しコールを使用して呼び出すシンプルなPoC
隠し呼び出しを使用して暗号化されたシェルコードを実行するシンプルなPoC。
このコードは、シェルコードへの実際のジャンプ命令を隠すためのシンプルなトリックを使用しています。これにより、静的解析やエミュレーションが難しくなります。
いつものように、このコンセプトが他の論文ですでに説明されている場合は、メッセージを送ってください。喜んで参考文献に追加します。
このテクニックで使用するメモリレイアウトは標準的なもので、図1に示されています。
┌───────────────────────────────┐
│ │
│ │
│ │
│ │
│ encrypted shellcode │
│ │
│ │
│ │
│ │
│ │
│ │
├───────────────────────────────┤
│ │
│ │
│ decryption loop │
│ │
│ │
└───────────────────────────────┘
図1. メモリレイアウト
復号ループはシェルコードを復号し、そこにジャンプします。すべての「魔法」は復号ループの中にあります。シェルコードの復号が完了すると、復号ループは自身のコードの復号を開始します。最初の命令の復号により、次のループ反復で実行されるシェルコードへのジャンプが発生します :)
以下に関連部分を示します:
@decryption:
xor word ptr [eax], 06799h ; <jmp esi bytes> XOR <first two bytes of this instruction>
add eax, sizeof word
dec ecx
loop @decryption
ご覧のとおり、復号ループには復号されたシェルコードにジャンプする命令は含まれていません。アセンブリコードは以下のバイナリ形式にアセンブルされます:
66:8130 9967 | xor word ptr ds:[eax],6799
83C0 02 | add eax,2
49 | dec ecx
E2 F5 | loop 450006
この場合、復号キー は 06799h でなければなりません。なぜなら、8166h(復号ループの最初の命令の最初の2バイト)と6799h(XORキー)のXOR演算結果がe6ffhとなり、これは jmp esi にアセンブルされるからです。言い換えれば:
0x8166 (xor word ptr ds:[eax],...) XOR 0x6799 (decryption key) == 0x6eff (jmp esi)
ESIレジスタをシェルコードの先頭に設定することで、実行を達成できます :)
以下はデバッグの例です。最初に暗号化されたシェルコードがコピーされ、その後にシェルコードを復号して呼び出すコードが続きます。復号ループの3回目の実行後、命令 xor word ptr ds:[eax],6799 が jmp esi に変化していることがわかります。

このテクニックを使用する手順は次のとおりです:
復号コードをより識別しにくくするために、シェルコードを呼び出す代替方法を使用することが可能です。より自由度を高めるために、各反復で暗号化されるチャンクサイズを大きくすることも検討できます。私のPoCでは jmp esi が2バイトを必要とするため2バイトを使用しましたが、4バイトまたは8バイトのチャンクサイズを使用することもでき、オペレーターは4バイトまたは8バイトのチャンクに収まるより多くの選択肢を持つことができます。シェルコードの呼び出し方法に応じて、暗号化定数も変更されます。