0. はじめに
とある事情でバイナリファイル(実行プログラム)をイジることになった。バイナリエディタを使えば一発なのだが,インストールが面倒なのとテキスト形式で変更前後の差分を残しておきたいということで
- バイナリを16進数ダンプするスクリプト
- 16進数ダンプをバイナリに戻すスクリプト
の二つを同時に作ることにした。
1. 基本方針
バイナリファイルの読み書きには ADODB.Stream
オブジェクトを使う。
バイナリファイルの一括読み込み
var stream = WScript.CreateObject("ADODB.Stream");
stream.Type = 1; /* 1:adTypeBinary */
stream.Open();
stream.LoadFromFile(filename);
var bin = stream.Read(-1); /* -1:adReadAll */
stream.Close();
バイナリファイルの一括書き込み
var stream = WScript.CreateObject("ADODB.Stream");
stream.Type = 1; /* 1:adTypeBinary */
stream.Open();
stream.Write(bin);
stream.SaveToFile(filename, 2); /* 2:adSaveCreateOverWrite */
stream.Close();
問題は,こうして読み込んだバイナリデータ,あるいは書き込むときに用意するバイナリデータのいずれも JavaScript の既存のデータ型としてアクセスすることはできないということだ。バイナリデータと16進数テキストの相互変換には MSXML2.DOMDocument
オブジェクトを使う。
var document = WScript.CreateObject("MSXML2.DOMDocument");
var element = document.createElement("hex");
element.dataType = "bin.hex";
こうして宣言した element
オブジェクトに対して nodeTypedValue
メンバにバイナリデータを書き込むと明示的な変換メソッドを呼び出さなくても text
メンバを参照するだけで16進数文字列を取得することができる。また逆に text
メンバに16進数文字列を書き込むだけで nodeTypedValue
メンバを参照すればバイナリデータが得られる。
ちなみに element.Type = "bin.base64"
とすると,BASE64文字列のエンコード/デコードが可能になる。
2. 基本仕様
16進数ダンプのデータは逆変換の手間を考えてデータのみとする。すなわちアドレス無し,ASCII文字列も無しとする。なお,16進数ダンプは見易いように1バイトごとに空白,16バイトごとに改行を挟むが,逆変換時はその限りではなく,フリーフォーマットとする。※単純にスペースやタブ,改行コードは無視する。
16進数ダンプのサンプル
4d 5a 90 00 03 00 00 00 04 00 00 00 ff ff 00 00
b8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 f0 00 00 00
0e 1f ba 0e 00 b4 09 cd 21 b8 01 4c cd 21 54 68
/* 以下略 */
3. 実装コード
BIN2HEX.JS の実装コード
BIN2HEX.JS
//------------------------------------------------------------------------------
// メイン関数
//------------------------------------------------------------------------------
function main(args) {
//--------------------------------------------------------------------------
// ヘルプメッセージ
//--------------------------------------------------------------------------
if(args.Count < 2) {
WScript.StdErr.WriteLine("Usage: bin2hex(.js) [input(.hex)] [output(.bin)]");
return false;
}
//--------------------------------------------------------------------------
// 入力ファイルのチェック
//--------------------------------------------------------------------------
var fs = WScript.CreateObject("Scripting.FileSystemObject");
if(!fs.FileExists(args(0))) {
WScript.StdErr.WriteLine("ファイル " + args(0)+ " は存在しません!!");
return false;
}
//--------------------------------------------------------------------------
// 入力ファイル(バイナリファイル)の一括読み込み
//--------------------------------------------------------------------------
var stream = WScript.CreateObject("ADODB.Stream");
stream.Type = 1; /* 1:adTypeBinary */
stream.Open();
stream.LoadFromFile(args(0));
var bin = stream.Read(-1); /* -1:adReadAll */
stream.Close();
//--------------------------------------------------------------------------
//出力ファイル(テキストファイル)の書き込み
//--------------------------------------------------------------------------
var document = WScript.CreateObject("MSXML2.DOMDocument");
var element = document.createElement("hex");
element.dataType = "bin.hex";
element.nodeTypedValue = bin;
var ts = fs.CreateTextFile(args(1), true);
var s = element.text;
for(var i = 0; i < s.length; i += 32 ) {
var t = s.substr(i, 32);
var a = [];
for(var j = 0; j < t.length; j += 2)
a.push(t.substr(j, 2));
ts.WriteLine(a.join(" "));
}
ts.Close();
return true;
}
//------------------------------------------------------------------------------
// メイン関数の呼び出し
//------------------------------------------------------------------------------
var args = WScript.Arguments.Unnamed;
var ret = main(args);
try {
WScript.Quit(ret);
} catch(e) {
/* 何もしない */
}
HEX2BIN.JS の実装コード
HEX2BIN.JS
//------------------------------------------------------------------------------
// メイン関数
//------------------------------------------------------------------------------
function main(args) {
//--------------------------------------------------------------------------
// ヘルプメッセージ
//--------------------------------------------------------------------------
if(args.Count < 2) {
WScript.StdErr.WriteLine("Usage: hex2bin(.js) [input(.hex)] [output(.bin)]");
return false;
}
//--------------------------------------------------------------------------
// 入力ファイル(テキストファイル)の存在チェック
//--------------------------------------------------------------------------
var fs = WScript.CreateObject("Scripting.FileSystemObject");
if(!fs.FileExists(args(0))) {
WScript.StdErr.WriteLine("ファイル " + args(0) + " は存在しません!!");
return false;
}
//--------------------------------------------------------------------------
// 入力ファイル(テキストファイル)の一括読み込み
//--------------------------------------------------------------------------
var ts = fs.OpenTextFile(args(0));
var buf = ts.ReadAll();
ts.Close();
//--------------------------------------------------------------------------
// 余分な空白・改行コードを取り除く
//--------------------------------------------------------------------------
buf = buf.replace(/\s/g, "");
//--------------------------------------------------------------------------
// 出力ファイル(バイナリファイル)の一括書き込み
//--------------------------------------------------------------------------
var stream = WScript.CreateObject("ADODB.Stream");
var doc = WScript.CreateObject("MSXML2.DOMDocument");
var element = doc.createElement("hex");
element.dataType = "bin.hex";
element.text = buf;
stream.Type = 1; /* 1:adTypeBinary */
stream.Open();
stream.Write(element.nodeTypedValue);
stream.SaveToFile(args(1), 2); /* 2:adSaveCreateOverWrite */
stream.Close();
return true;
}
//------------------------------------------------------------------------------
// メイン関数の呼び出し
//------------------------------------------------------------------------------
var args = WScript.Arguments.Unnamed;
var ret = main(args);
try {
WScript.Quit(ret);
} catch(e) {
/* 何もしない */
}