基礎用語 2進ビットパターン・論理演算
2進ビットパターン(16進数と2進数の対応表)
当ソフトウェア『うさみみハリケーン』のようなプロセスメモリエディタでは、プロセス(プログラムの実行単位)が使用するプロセスメモリ(仮想的なメモリ空間)上のバイナリデータ(1か0の2進法で構成されるデータ)を、人間にとっての視認性や操作性を高めるため16進数を使って表示や編集を行います。 コンピュータが扱うバイナリデータとしては、2進数の1または0が格納される1桁を「ビット」と呼びます。さらに16進数でバイナリデータを扱う際の基本単位は「バイト」と呼び、1バイトは2進数でいう8ビットで構成されます。なお、16進数の値は、進数を別途明示しない場合は値の後に「h」か値の前に「0x」をつけて表示します。また、4ビットのまとまりを「ニブル」と呼びます。 16進数の表示について、PCゲーム用改造コードあるいは家庭用ゲーム機の改造コードに関し、「+Ah」といった表現が使われることがありますが、この場合は特定のアドレス/オフセットを基準にして16進数で0x0Aを加算したアドレス/オフセットを意味します。ちなみに、プロセスメモリエディタが表示するプロセスメモリ上の番地を「アドレス」、ヘキサエディタ(日本での通称はバイナリエディタ)が表示するファイル先頭からの相対距離を「オフセット」と呼びますが、日本ではアドレスとオフセットが混同されているケースが少なくありません。 以下の基本的な対応表を覚えてしまうことをお奨めします。情報セキュリティに関連するプログラム解析において、2進ビットパターンの知識は、各種フラグのオン・オフを各ビットの状態で格納しているケースの解析等に役立ちます。 2進数での各ビットには1または0のいずれかの値が格納され、その組み合わせで特定の値を表していることに注目して下さい。同じパターンで数値を表す上位4ビットと下位4ビットを分けて考えると分かりやすいと思います(上位4ビットと下位4ビットの間にスペースを入れてみました)。 |
10進数 | 16進数 | 2進数(8つの数字は1バイトを構成する8ビットを意味する) |
1 | 01 | 0000 0001 |
2 | 02 | 0000 0010 |
3 | 03 | 0000 0011 |
4 | 04 | 0000 0100 |
5 | 05 | 0000 0101 |
6 | 06 | 0000 0110 |
7 | 07 | 0000 0111 |
8 | 08 | 0000 1000 |
9 | 09 | 0000 1001 |
10 | 0A | 0000 1010 |
11 | 0B | 0000 1011 |
12 | 0C | 0000 1100 |
13 | 0D | 0000 1101 |
14 | 0E | 0000 1110 |
15 | 0F | 0000 1111 |
例えば16進数の値A4hは、2進数で表すと1010 0100となります。この2進ビットパターン「1010 0100」での最下位ビット(ここではもっとも右)の0が格納されている場所を「ビット0」と呼び、逆に最上位ビット(ここではもっとも左)の1が格納されている場所を「ビット7」と呼びます。その間は右から順に「ビット1」から「ビット6」です。値が4バイトで構成されるダブルワード(DWord)なら「ビット0」から「ビット31」となります。 |
論理演算 概論
論理演算では、16進の値を一旦2進ビットパターンに変換して論理演算後に16進に戻します。
算術演算と異なり、2進ビットパターン上での演算が行われていることに注目して下さい。
論理演算では本来"∀"等の論理演算記号を用いますが、以下ではわかりやすくするために、算術演算記号の"+"と"X"を用いました。また、当ソフトウェアの改造コードでは、コードとして記述する際にORとXORの記号を記述しやすくするために、別の記号を用いています。なお、論理演算を表す記号は、プログラミング言語では本来の論理演算記号とは別の記号を用いるケースがあります。 論理演算は種類ごとに0と1の演算パターンが異なっており、この演算パターンを2進ビットパターン上で「ビット0」なら「ビット0」同士、「ビット7」なら「ビット7」同士で適用します。 |
OR(論理和)
論理和におけるビット演算パターン 0+0=0 0+1=1 1+0=1 1+1=1 14hと35hの論理和演算例 |
14h(00010100) |
… | ビット0(一番右側)は0 |
35h(00110101) |
… | ビット0(一番右側)は1 |
35h(00110101) |
ビット0では0+1で1、ビット1では0+0で0、ビット2では1+1で1、ビット3では0+0で0、 ビット4では1+1で1、ビット5では0+1で1、ビット6では0+0で0、ビット7では0+0で0。 |
AND(論理積)
論理積におけるビット演算パターン 0X0=0 0X1=0 1X0=0 1X1=1 14hと35hの論理積演算例 |
14h(00010100) |
… | ビット0(一番右側)は0 |
35h(00110101) |
… | ビット0(一番右側)は1 |
14h(00010100) |
解説 上のビット演算パターンを見ればわかるように、特定ビットの中身が0ならば、その演算対象となるビットの中身が1か0かに関わらず演算結果は0となります。この性質を利用して、例えば0Fh(00001111)で1バイトの値にAND演算を行えば、演算対象の値の下位4ビットには影響を与えずに上位4ビットの各ビットを0にしてしまうことが可能です。このような操作を「ビットマスクをかける」や「ビットのフィルタリングを行う」ということがあります。なお、実際には「上位4ビット」ではなくダブルワードの値に対するバイトやワード単位でのマスクが一般的です。 |
XOR(排他的論理和)
排他的論理和におけるビット演算パターン 0+0=0 0+1=1 1+0=1 1+1=0 14hと35hの排他的論理和演算例 |
14h(00010100) |
… | ビット0(一番右側)は0 |
35h(00110101) |
… | ビット0(一番右側)は1 |
21h(00100001) |
解説 上のビット演算パターンの下2例を見ればわかるように、特定ビットの中身が1ならば、その演算対象となるビットの中身は0から1またはその逆へと反転します(ビット反転)。また、同じ値で2度XOR演算を施せば演算対象は元の値に戻ります。この性質を利用して、PCゲームやマルウェア等でデータの簡単な暗号化と復号化に用いられることがあります。さらに同じ値同士でXOR演算を施せば演算結果は00hとなるため、ソフトウェアの内部処理上でレジスタ(CPU内にある特殊なメモリのこと)や特定メモリアドレスに格納された値のクリアに用いられることもあります。 なお、かつて日本では、PCゲームでのセーブデータやシナリオデータの簡単な暗号化と復号化に、1バイトの値(FFh等)で全データに対しXOR処理を行う手法が頻用されていました。このようなケースでは、ヘキサエディタ(日本での通称はバイナリエディタ)でバイナリデータを見れば、元々のデータで00hであった箇所は全てXOR処理を施す値(FFh等)になりますので、XORによる簡易暗号化が容易に推測可能でした。 |