シェルコード
最近のプログラムでは、文字列にUnicodeを使っている。多くの場合、ASCII文字列は処理前にUnicodeに変換される。UTF-16なら各文字に2バイトを使用する(一部は4バイト)。ASCII文字列をUTF-16に変換すると、元の文字列の各バイトの後ろにゼロバイトが挿入される。Phrack 61号で Obscou が示したとおり、この変換後も正しく動作するシェルコードを書くことは可能である。任意のシェルコードをUTF-16化したときに動作するよう自動変換するプログラムが存在している。これも、自己書き換えコードを基本としている。
シェルコードには、「ローカル型」と「リモート型」があり、攻撃者がシェルコードの動作するマシンの制御を獲得するのか(ローカル型)、それともネットワーク上の別のマシンの制御を獲得するのか(リモート型)で決まってくる。
一般にシェルコードはヌル文字を終端とする文字列として対象プロセスに注入されるため、ヌル文字(一般に0x00)をその途中で使うことはできない。途中にヌル文字があると、そこまでしか文字列としてコピーされない。従ってヌル文字に相当するコードがシェルコードの途中にある場合、シェルコードは最後まで実行されない。
途中にヌルバイトを含むシェルコードからヌルを含まないシェルコードを生成するには、同じ効果を持つ別の命令列に置き換える。例えば、IA-32アーキテクチャで以下の命令があるとする。
即値の1が符号拡張されるため、ヌルバイトが命令に含まれている。
この命令列は先の命令と同じ効果があるが、バイト数が少なくなるだけでなく、ヌルバイトも含まない。
ローカル型シェルコードは、攻撃者がそのマシンに限定的なアクセス権があるとき、より特権レベルの高いプロセスの脆弱性を利用するものである。成功すると、シェルコードによって攻撃者は攻撃対象プロセスと同等レベルの特権を得る。ローカル型シェルコードは比較的作成しやすい。この形式のシェルコードが行うのは、シェルの実行ファイルを実行することだけである。
ある状況では、印字可能な文字(制御文字以外)や英数字だけしか注入できない場合がある。そのような状況ではシェルコードを書くのに使える命令の種類は大きく制限される。そのための技法は Phrack 57号で Rix が発表しており、それによれば任意のコードを英数字のみのコードに変換することができる。よく使われる技法は自己書き換えコードで、デコーダ部分は制限されたコードのみで動作するよう書く必要がある。シェルコード本体も例えば英数字のみのコードに符号化しておき、デコーダがそれを注入後に書き換え、その後実行する。