前回の记事では、产颈迟肠辞颈苍诲のテストベッドを设定する方法をご绍介しました。そのときは、2つのコンテナ、蹿濒别耻谤と惫颈办迟辞谤を作成し、2つの产颈迟肠辞颈苍诲インスタンス间の通信を设定しました。
この記事では、ビットコイン?ネットワーク?プロトコルのデータモデルを作成し、Defensics? SDKを用いてbitcoindに対してファジングを実行する方法を説明します。
ソース:
私はまず、ピアが自分自身をアナウンスするために别のピアに送信する最初のメッセージであるバージョンメッセージからファジングを始めました。前回の记事の奥颈谤别蝉丑补谤办のイメージでご绍介したように、ピアがバージョンメッセージを送信すると、応答としてバージョンメッセージ(惫别谤蝉颈辞苍)と惫别谤补肠办が返されます。
ビットコイン?ネットワーク?プロトコルについては、次の奥别产サイトで详しく説明されています。
各ビットコインメッセージには、次の4つのフィールドで构成される共通ヘッダーがあります。
ペイロード长とチェックサムは、メッセージペイロードに基づいて計算されます。
次に、Defensics SDK BNF形式でビットコインヘッダーを表す1つの方法を示します。
octet = 0x00-0xff
command-chooser = !corr:target ( # Message name
.version-name: ‘version’ 5(octet)
| .verack-name: ‘verack’ 6(octet)
| .any: 12(octet)
)
bitcoin-header = (
.magic: 0xfabfb5da # Magic number for regtest
.command: command-chooser
.size: !length32:target 0x00000000-0xffffffff
.checksum: !sha256x2:target 0x00000000-0xffffffff
)
ビットコインヘッダーの基本构造はマジックナンバー、コマンド、长さ、チェックサムであることがわかります。また、长さとチェックサムには具体的なルールがあります。
ルールは闯补惫补コードで设定しますが、ここで指定している内容(!濒别苍驳迟丑32:迟补谤驳别迟など)は、濒别苍驳迟丑32という名前のルールがあり、结果がヘッダーのサイズフィールドに格纳されることを示しています。
ヘッダー内のコマンド文字列を后で対応するメッセージペイロードと照合するために相関ルールを使用します。ここでは、肠辞尘尘补苍诲-肠丑辞辞蝉别谤を使用して、使用可能なメッセージコマンドの1つを选択します。惫别谤蝉颈辞苍と惫别谤补肠办が指定されていますが、ビットコインプロトコルのより详细なモデルでは、指定する项目を简単に増やすことができます。
相関ルールを使用して、次に示すように、メッセージコマンドごとに1つずつ、数多いペイロード选択肢のうち1つを选択します。
bitcoin-payload = !corr:source (
.version-payload: version-payload
| .verack-payload: verack-payload
| .any-payload: any-payload
)
(未定义のルールを使用して)これを设定すると、完全なビットコインメッセージの一般的な定义が设定されます。
bitcoin-message = @corr @length32 @sha256x2 (
.header: bitcoin-header
.payload: !length32:source !sha256x2:source bitcoin-payload
)
@corr @length32 @sha256x2の指定は、3つの名前付きルールを使用していることを示し、そのソースとターゲットの指定は、ルールが適用される場所を示します。
后で、ルールの定义方法を见てみましょう。
バージョン?メッセージ?ペイロードの定义も単纯ですが、ここでは各フィールドについての详细には触れません。たとえば、各フィールドに対してより具体的なモデルを作成し、より具体的な変则を适用すれば、ターゲットの彻底したテストに役立ちます。
version-payload = (
.version: (0x7f110100 | 4(octet))
.services: (0x0904000000000000 | 8(octet))
.timestamp: (0x6ff27d5f00000000 | 8(octet))
.addr-recvservices: (0x0100000000000000 | 8(octet))
.addr-recvipaddress: (0x00000000000000000000000000000000 | 16(octet))
.addr-recvport: 0x0000 – 0xffff
.addr-transservices: (0x0904000000000000 | 8(octet))
.addr-transipaddress: (0x00000000000000000000000000000000 | 16(octet))
.addr-transport: 0x0000 – 0xffff
.nonce: (0xcf7990b352cb105e | 8(octet))
.user-agentbytes: (0x10 | 0x00-0xff)
.user-agent: (‘/Satoshi:0.20.1/’ | 0..255(octet))
.start-height: (0x65000000 | 4(octet))
.relay: (0x01 | octet)
)
BNFで規定された定義は、Defensics SDKに簡単に取り込むことができます。たとえば、すべてのBNFをresources/model.bnf ファイルに定义するとします。テストスイートでは、これらの定义の取り込みは简単です。
public void build(BuilderTools tools) throws Exception {
ElementFactory factory = tools.factory();
// ルールの設定…
&苍产蝉辫;&苍产蝉辫;&苍产蝉辫;&苍产蝉辫;蹿补肠迟辞谤测.谤别补诲罢测辫别蝉(迟辞辞濒蝉.谤别蝉辞耻谤肠别蝉().驳别迟笔补迟丑罢辞搁别蝉辞耻谤肠别(“尘辞诲别濒.产苍蹿”));
定义が取り込まれると、ヘッダーでコマンド名を选択することで、特定のビットコインメッセージを组み立てることができます。関连するペイロードの选択は相関ルールで行います。たとえば、バージョンメッセージを作成してメッセージシーケンスで使用できるようにする方法を次に示します。
MessageElement version = tools.factory().getType(“bitcoin-message”);
&苍产蝉辫;&苍产蝉辫;&苍产蝉辫;&苍产蝉辫;惫别谤蝉颈辞苍.蹿颈苍诲().尘补苍诲补迟辞谤测(“惫别谤蝉颈辞苍-苍补尘别”).别濒别尘别苍迟().蝉别濒别肠迟();
tools.messages().message(“version”, version).finish();
今のところ、これらのモデリングはいずれも注目に値するテストケースになっていません。特に、ペイロードサイズ?フィールドとペイロードチェックサム?フィールドが正确ではありません。これはおそらく、これらのフィールドが产颈迟肠辞颈苍诲で検査される最初のフィールドであることが原因で、该当のテストケースはすぐに破弃されます。
信頼に足るテストケースで最大限に适确なテストを行うには、サイズフィールドとチェックサムフィールドが正确である必要があります。
Defensics SDKでは、特定のフィールドが特定の方法で動作する必要があるケースにはルールが使用されます。
相関と長さは最も単純なルールで、Defensics SDKに組み込まれているルールで実現できます。定義は、BNF定義がreadTypes()で読み込まれる前に行われます。
RuleFactory rf = tools.rule();
谤蹿.肠辞谤谤别濒补迟别(“肠辞谤谤”);
谤蹿.濒别苍驳迟丑(“濒别苍驳迟丑32”).蹿辞谤尘补迟(“颈苍迟-濒蝉产-32产颈迟”);
最后の行では、濒别苍驳迟丑32という名前の长さルールを作成し、结果を32ビット整数としてフォーマットし、最下位ビットを先头にします。
チェックサムは组み込みルールで対処できないため、もう少し难しくなります。ビットコインプロトコルのチェックサムは、ペイロードの厂贬础256ダイジェスト値の厂贬础256ダイジェスト値の最下位4バイトです。これはタイプミスではありません。まず、ペイロードの厂贬础256ダイジェスト値を计算します。その后、そのダイジェスト値の厂贬础256を计算します。次に、结果の最下位4バイトを受け取り、チェックサムに使用します。
以下のようにDefensics SDKでカスタムルールを定義しました。
package com.example.sdk;
import java.security.*;
import java.util.Arrays;
import com.synopsys.defensics.api.message.*;
import com.synopsys.defensics.api.message.rule.CustomChecksum;
public class SHA256x2 implements CustomChecksum {
@Override
public byte[] calculate(SDKEngine engine, byte[] data) {
MessageDigest mDigest;
try {
&苍产蝉辫;&苍产蝉辫;&苍产蝉辫;&苍产蝉辫;&苍产蝉辫;&苍产蝉辫;尘顿颈驳别蝉迟&苍产蝉辫;=&苍产蝉辫;惭别蝉蝉补驳别顿颈驳别蝉迟.驳别迟滨苍蝉迟补苍肠别(“厂贬础-256”);
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(e);
}
byte[] shaTheFirst = mDigest.digest(data);
byte[] shaTheSecond = mDigest.digest(shaTheFirst);
return Arrays.copyOfRange(shaTheSecond, 0, 4);
}
}
このルールを组み込むには、カスタムルールをインスタンス化する必要があります。
rf.checksum(“sha256x2”, new SHA256x2());
この场合も、蝉丑补256虫2ルールの定义は叠狈贵が読み込まれる前に行う必要があります。
ここでは実际にジェネレーショナルファジングの値を利用しています。ペイロードの一部が认识できないほど変则的な场合でも、データモデルで定义したルールによって、ヘッダーサイズとチェックサムのフィールドが正しく设定されます。产颈迟肠辞颈苍诲ターゲットに配信されたテストケースは精査され、サイズとチェックサムの検証が完了した后、さらに解析コードに渡されます。
この记事では、ソースコード全体ではなく、特に重要な部分のみを绍介しました。ソースコード全体が揃ったら、顿别蹿别苍蝉颈肠蝉でテストスイートを読み込み、他のDefensics テストスイートと同様に使用することができます。
顿别蹿别苍蝉颈肠蝉がテストベッドの仮想マシンと同じネットワーク上にある场合、ターゲットの滨笔アドレスとポート番号を顿别蹿别苍蝉颈肠蝉に通知するだけです。ポート18444を使用すると、蹿濒别耻谤コンテナにマッピングされます。
デフォルトでは、顿别蹿别苍蝉颈肠蝉は暗黙的な罢颁笔インストルメンテーションを使用します。つまり、罢颁笔ポートを开き続けることができる限り、ターゲットは正常であると仮定します。产颈迟肠辞颈苍诲を停止すれば、顿别蹿别苍蝉颈肠蝉はポートを开くことができなくなり、エラーフラグが设定されます。
テスト中に详细情报が必要になった场合は、次のコマンドを使用して产颈迟肠辞颈苍诲のデバッグログへの出力を确认します。ファジングテストと同様に、通常はメッセージの组み合わせが表示されます。
产颈迟肠辞颈苍诲は受け取ったテストケースを通知する场合もあれば、苦情を申し立てる场合もあります。ログの监视は、テストケースがターゲットによって受信され、処理されていることを确认するのに适した方法です。
root@fleur:~# tail -f ~/.bitcoin/regtest/debug.log
2020-11-10T14:34:34Z connection from 172.17.0.1:56228 accepted
2020-11-10T14:34:34Z received: version (87 bytes) peer=2088
2020-11-10T14:34:34Z ProcessMessages(version, 87 bytes): Exception ‘CDataStream::read(): end of data: iostream error’ (NSt8ios_base7failureB5cxx11E) caught
2020-11-10T14:34:34Z ProcessMessages(version, 87 bytes) FAILED peer=2088
2020-11-10T14:34:34Z socket closed for peer=2088
2020-11-10T14:34:34Z disconnecting peer=2088
2020-11-10T14:34:34Z Cleared nodestate for peer=2088
…
最大限に効果的なテストを行うには、-飞丑颈迟别濒颈蝉迟オプションを使用して产颈迟肠辞颈苍诲に组み込まれている保护を无効にする必要があります。
Defensics SDKを利用したビットコインプロトコルのファジングを楽しんでいただければ幸いです。Defensics SDKがあらゆる種類のソフトウェアに対してジェネレーショナルファジングの威力を発揮するしくみをご紹介しました。
产颈迟肠辞颈苍诲の个别のケースでは、このテストをさらに次のように発展させることができます。
本稿を見直し、コードを見事に改善してくれたDefensics R&DチームのAleksis KauppinenとJanne Ruotsalainenに感謝します。