無限不可能性ドライブ

『ニューラルネットワーク自作入門』に刺激されてExcelVBAでニューラルネットワークを作ってみたものの、やっぱり数学やらなきゃと思い少しずつやってきたのもあって、自分の知識の整理とかそういった感じです。

【VBA】画像ファイルを(意図的に)壊して不慮の事故を防ぐ あるいは暗号化してみる(1)

f:id:celaeno42:20181227232104p:plain

俺の嫁フォルダががが…

あ!ちょっ…そのフォルダは開けちゃだめ!!なんてことがあったりなかったりするかもしれませんが、そういう場合に慌てないようにあまり見られたくない画像ファイルなんかは暗号化とかしておくとそういった不慮の事故が防げるかもしれませんね。まぁ、すでにひらきなおってる人はそんな必要はありませんが(私)。
今回は VBA で画像ファイルを意図的に壊しちゃいましょうというお話です。ある意味、暗号化といっていいかもしれません。

バイナリ

コンピュータは 0 と 1 ですべて処理をしているという話はどこかで聞いたことがあるかもしれません。ファイルだってそうなんです。

f:id:celaeno42:20181227232720p:plain

たとえばこの(かわいい)PNG画像ですが、バイナリエディタというちょっと特殊なエディタで開くとこんな感じになっています。

f:id:celaeno42:20181227233136p:plain

0 と 1 ではありませんね。それは 16進数で表示されているからです。これを 2進数で表示してみましょう。

f:id:celaeno42:20181227233613p:plain

上の画像では先頭が「89」になっていますが、下の画像では「1000 1001」になっています。
16進数の「8」は 2進数では「1000」、16進数の「9」は 2進数では「1001」なので、ちゃんと対応していますね。

このように、ファイルの実体は膨大な 0 と 1 の羅列になっています。
ちなみに、8bit で 1byte となるので、2進数の「1000 1001」が 1byte に相当します(1, 0 それぞれが 1bit です)。
16進数では「89」が 1byte ですね。

ファイルを壊してみる

ではちょっといたずらをして 2進数の先頭の 1 を 0 に書き換えてみましょう。

【いたずら前】
f:id:celaeno42:20181227234551p:plain
(かわいい)サムネイル画像がちゃんと表示されてますね。


【いたずら後】
f:id:celaeno42:20181227234758p:plain
(かわいい)サムネイル画像が表示されなくなってしまいました。
さらに…

f:id:celaeno42:20181227234940p:plain:w320

たった1か所、わずか 1bit を書き換えただけでファイルが壊れてしまいました。mjk…

まぁ、今回は書き換えた場所がわかっているので、元に戻せばちゃんとファイルも直ります。
f:id:celaeno42:20181227234551p:plain

かわいいですね。

このようにちょっとでもデータを書き換えるとファイルが壊れてしまいます。
さて、どうすればファイルが壊れるかがわかったのでこれを VBA で実装できればよさそうです。

バイナリモードで開く

先ほどはバイナリエディタで開きましたが、これと同じように VBA で読み込みができればいいですね。VBA ではバイナリモードで開くことで実現できます。

【標準モジュール】

Public Sub ReadBinary()
    Dim filePath As String
    Dim fileNo As Long
    Dim i As Long
    Dim buffer() As Byte        'バイナリデータ格納用の配列
    
    filePath = "C:\Users\xxxx\Desktop\ファイル暗号化\新しいフォルダー\image01.png"
    
    fileNo = FreeFile
    'ファイルをバイナリモードで開いてbuffer配列に格納する
    Open filePath For Binary As #fileNo
        ReDim buffer(LOF(fileNo))
        Get #fileNo, , buffer
    Close #fileNo
    
    '先頭8バイトだけ表示する
    For i = 0 To 7
        Cells(1, i + 1).Value = i + 1
        Cells(2, i + 1).Value = buffer(i)
    Next

End Sub

ファイルのデータを格納する配列変数「buffer」を宣言します。変数の型が Byte型 になっていることに注意してください。
通常テキストファイルを開く場合は、[ Open filePath For Input As #fileNo ] のような書き方をすると思います。今回は画像ファイルをバイナリモードで開きたいので、[ Input ] の部分が [ Binary ] となっています。
ファイルを開いたら、データを格納する配列変数「buffer」のサイズを「ReDim」で設定し、「Get」で読み込んだデータを「buffer」に格納しています。「#fileNo」と「buffer」の間のカンマが 2つになっていることに注意してください(引数が一つ省略されています)。

読み込んだデータの先頭 8byte だけをシートに表示させています。

f:id:celaeno42:20181228222327p:plain

バイナリエディタで開いたものと比較してみましょう。

f:id:celaeno42:20181228222908p:plain

なんかちょっと違いますね。
エクセルの方は「137 80 78 71 …」となってますが、バイナリエディタでは「89 50 4E 47 …」となっています。
これはエクセルの方が 10進数で表示されているためなので、16進数に変換してみましょう。
ワークシート関数の「=DEC2HEX()」を使うと、10進数を16進数に変換できます。

f:id:celaeno42:20181228223222p:plain

バイナリエディタで開いたものと同じになりました(1桁のところは頭の0が省略されていると考えてください)。

バイナリデータを書き換える

バイナリモードで読み込むことができたので、これをちょっと書き換えて保存しなおしてみます。

Public Sub ReadWriteBinary()
    Dim filePath As String
    Dim fileNo As Long
    Dim i As Long
    Dim buffer() As Byte
    
    filePath = "C:\Users\celae\Desktop\ファイル暗号化\新しいフォルダー\image01.png"
    
    fileNo = FreeFile
    'ファイルをバイナリモードで開いてbuffer配列に格納する
    Open filePath For Binary As #fileNo
        ReDim buffer(LOF(fileNo))
        Get #fileNo, , buffer
    Close #fileNo
    
    '先頭8バイトだけ表示する
    For i = 0 To 7
        Cells(1, i + 1).Value = i + 1
        Cells(2, i + 1).Value = buffer(i)
    Next

'----追加----

    '先頭の1byteを16進数で90に書き換える
    buffer(0) = &H90
    
    fileNo = FreeFile
    'buffer配列をファイルとして書き出す
    Open filePath For Binary As #fileNo
        Put #fileNo, , buffer
    Close #fileNo
    
'------------

End Sub

先頭の 1byte のみを書き換えてみました。16進数として扱いたいので「&H」を付けています。
書き換えた内容をファイルとして書き出します。読み込み時は「Get」を使いましたが、今回は「Put」を使います。こちらも「#fileNo」と「buffer」の間のカンマが 2つになっていることに注意してください(引数が一つ省略されています)。

では、書き換えたファイルをバイナリエディタで開いた内容を見てみましょう。

f:id:celaeno42:20181228225558p:plain

先頭が「90」に書き換わり、画像が表示されなくなっていますね。
先頭を「89」に書き換えればまた正しく表示されるようになります。

f:id:celaeno42:20181228232225p:plain

かわいいですね。

今回は 1ファイルだけを扱いましたが、次回はフォルダ内の画像ファイルをまとめて変換する処理を書いていく予定です。


バイナリエディタは「TSXBIN」を使わせていただきました。
※今回使用した(かわいい)画像は「V☆カツ」で作成しました。


f:id:celaeno42:20181212233850p:plain