ファイルの16進ダンプとその逆
はじめに
[追記]
以前は「バイナリ,ASCIIの相互変換」というタイトルでしたが,言葉がふさわしくないと思ったのでタイトルを「ファイルの16進ダンプとその逆」に変更し,内容も大幅に改変しました。
言語はCとPython,
Windows 8.1にCygwin入れた環境でテストしました。
ファイルの16進ダンプ
ファイルの16進ダンプとは,ファイルデータを16進数のデータの集まりとして表示することです。
形式は、16進を2個で1セットとし、1セットずつは1つの半角空白で区切られ、0x10B(バイト)ごとに改行されているものとします。
左に何バイト目かを表す行番号のようなもの、右に対応するASCIIがあったほうが見栄えがよくなると思いまが,ダンプの逆をやることを考慮してあえてそこは手を抜きました。
実行結果として載せてるのはHelloWorldをダンプしたものです。
- Pythonの場合
#!/usr/bin/env python #-*- coding: utf-8 -*- import sys argv = sys.argv argc = len(argv) if argc != 2: print 'Usage python hex.py [file_name]' quit() count = 0 try: f = open(argv[1],'rb') except: print 'Usage %s is not found.' % argv[1] quit() for char in f.read(): print '%02x' % ord(char), count += 1 if count % 16 == 0: print f.close()
- 実行結果
$ python hex.py Hello.exe 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 69 73 20 70 72 6f 67 72 61 6d 20 63 61 6e 6e 6f 74 20 62 65 20 72 75 6e 20 69 6e 20 44 4f 53 20 6d 6f 64 65 2e 0d 0d 0a 24 00 00 00 00 00 00 00 d1 35 16 c1 95 54 78 92 95 54 78 92 95 54 78 92 (中略) 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 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
- C言語の場合
#include <stdio.h> int main(int argc, char *argv[]) { int n; unsigned long long int count = 0; FILE *fp; if(argc != 2){ puts("Usage: hex [file_name]"); return 0; } if((fp = fopen(argv[1], "rb")) == NULL) printf("Usage: %s is not found.\n", argv[1]); else{ while((n = fgetc(fp)) >= 0){ printf("%02x ", n); count++; if(count%16 == 0) putchar('\n'); } fclose(fp); } return 0; }
- 実行結果
$ gcc -o hex hex.c $ ./hex Hello.exe 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 69 73 20 70 72 6f 67 72 61 6d 20 63 61 6e 6e 6f 74 20 62 65 20 72 75 6e 20 69 6e 20 44 4f 53 20 6d 6f 64 65 2e 0d 0d 0a 24 00 00 00 00 00 00 00 d1 35 16 c1 95 54 78 92 95 54 78 92 95 54 78 92 (中略) 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 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
ファイルの16進ダンプの逆
16進ダンプによって作成されたテキストファイルから元のファイルを復元します。
- Pythonの場合
#!/usr/bin/env python #-*- coding: utf:8 -*- import sys argv = sys.argv argc = len(argv) if argc != 2: print 'Usage: python unhex.py [file_name]' quit() try: infile = open(argv[1],'rb') except: print 'Usage: %s is not found.' % argv[1] quit() fname = raw_input('out file name:') outfile = open(fname,'wb') a = [str(x) for x in infile.read().split()] for i in a: outfile.write(chr(int(i, 16))) infile.close() outfile.close()
- 元ファイルとの比較
$ python hex.py Hello.exe > Hello.txt $ python unhex.py Hello.txt out file name:Hello2.exe $ chmod a+x Hello2.exe $ ./Hello2.exe Hello World $ diff Hello.exe Hello2.exe
diffコマンドで何も表示されないということは同じファイルということ。
- C言語の場合
#include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { int n; char str[255]; FILE *fp,*fp2; if(argc != 2){ puts("Usage: unhex [file_name]"); return 0; } if((fp = fopen(argv[1], "rb")) == NULL){ printf("Usage: %s Not Available.\n", argv[1]); return 0; } printf("out file name:"); scanf("%254s%*[^\n]", str); if((fp2 = fopen(str, "wb")) == NULL){ printf("Usage: %s Not Available.\n", str); return 0; } while(fscanf(fp, "%x", &n) != EOF) fputc(n, fp2); fclose(fp); fclose(fp2); return 0; }
- 元ファイルとの比較
$ gcc -o unhex unhex.c $ ./unhex Hello.txt out file name:Hello3.exe $ chmod a+x Hello3.exe $ ./Hello3.exe Hello World $ diff Hello.exe Hello3.exe
diffコマンドで何も表示されないということは(ry
より汎用性が高いスクリプト
#!/usr/bin/env python #-*- coding: utf:8 -*- import sys argv = sys.argv argc = len(argv) if argc != 2: print 'Usage: python unhex.py [file_name]' quit() try: infile = open(argv[1], 'rb') data = infile.read().replace(' ', '').replace('\r', '').replace('\n', '') except: print 'Error: %s is not found.' % argv[1] quit() outfile = open(raw_input('out file name:'), 'wb') i=0 while i+1 < len(data): outfile.write(chr(int(data[i:i+2],16))) i+=2 infile.close() outfile.close()
コマンドを使う
実際には自分でコードを書くよりもLinuxのodコマンドやxxdコマンドを使うことのほうが多いような気がします。
$ od -t x1 -w16 -A x Hello.exe 000000 4d 5a 90 00 03 00 00 00 04 00 00 00 ff ff 00 00 000010 b8 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00 000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000030 00 00 00 00 00 00 00 00 00 00 00 00 f0 00 00 00 000040 0e 1f ba 0e 00 b4 09 cd 21 b8 01 4c cd 21 54 68 000050 69 73 20 70 72 6f 67 72 61 6d 20 63 61 6e 6e 6f 000060 74 20 62 65 20 72 75 6e 20 69 6e 20 44 4f 53 20 000070 6d 6f 64 65 2e 0d 0d 0a 24 00 00 00 00 00 00 00 000080 d1 35 16 c1 95 54 78 92 95 54 78 92 95 54 78 92 000090 d3 05 a5 92 96 54 78 92 d3 05 98 92 86 54 78 92 0000a0 d3 05 99 92 92 54 78 92 48 ab b3 92 97 54 78 92 (中略) 007730 34 30 68 35 6c 35 b4 35 a8 39 b4 3c d4 3e d8 3e 007740 f4 3e f8 3e 14 3f 18 3f 34 3f 38 3f 58 3f 00 00 007750 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 * 007a00
$ xxd -p Hello.exe > Hello2.txt $ xxd -r -p Hello2.txt > Hello4.exe $ diff Hello.exe Hello4.exe