ファイルの16進ダンプとその逆

はじめに

[追記]
 以前は「バイナリ,ASCIIの相互変換」というタイトルでしたが,言葉がふさわしくないと思ったのでタイトルを「ファイルの16進ダンプとその逆」に変更し,内容も大幅に改変しました。

 言語はCとPython,
 Windows 8.1Cygwin入れた環境でテストしました。

ファイルの16進ダンプ

 ファイルの16進ダンプとは,ファイルデータを16進数のデータの集まりとして表示することです。
形式は、16進を2個で1セットとし、1セットずつは1つの半角空白で区切られ、0x10B(バイト)ごとに改行されているものとします。
 左に何バイト目かを表す行番号のようなもの、右に対応するASCIIがあったほうが見栄えがよくなると思いまが,ダンプの逆をやることを考慮してあえてそこは手を抜きました。
 実行結果として載せてるのはHelloWorldをダンプしたものです。

#!/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
#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進ダンプによって作成されたテキストファイルから元のファイルを復元します。

#!/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コマンドで何も表示されないということは同じファイルということ。

#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