C++のprivateプロパティを改竄する

前回、C++(gcc)のインスタンスの正体はインスタンス変数(プロパティ)を格納するための記憶領域だと言うことがわかったので、それを元にチョットお遊び。


ソース(Hoge9.cpp)

#include <stdio.h>

class Hoge9 {
private:
    int hoge;
public:
    void printHoge();
};

void Hoge9::printHoge() {
    printf("hoge = %d\n", hoge);
}

int main(int argc, char *argv[]) {
    Hoge9 hoge9;
    hoge9.hoge = -1;
    hoge9.printHoge();
    return 0;
}


コンパイル

$ g++ -o Hoge9 Hoge9.cpp
Hoge9.cpp: In function ‘int main(int, char**)’:
Hoge9.cpp:5: error: ‘int Hoge9::hoge’ is private
Hoge9.cpp:16: error: within this context

プライベートなプロパティにアクセスすんじゃねーよ的なことを言われる。


ソース改(Hoge9_2.cpp)

#include <stdio.h>

class Hoge9 {
private:
    int hoge;
public:
    void printHoge();
};

void Hoge9::printHoge() {
    printf("hoge = %d\n", hoge);
}

int main(int argc, char *argv[]) {
    Hoge9 hoge9;
    *(int *)&hoge9 = -1;
    hoge9.printHoge();

    // おまけ
    *(int *)&hoge9 = -2;
    hoge9.printHoge();
    *(int *)&hoge9 = -3;
    hoge9.printHoge();

    return 0;
}

インスタンスの先頭アドレスはhogeプロパティのアドレスとなるので、そこに値を書きこむ。


コンパイル&実行

$ g++ -o Hoge9_2 Hoge9_2.cpp
$ ./Hoge9_2 
hoge = -1
hoge = -2
hoge = -3

改竄した値が表示されている。privateなプロパティを改竄することができた。
アクセス指定子はコンパイル時にしか効果を持たないようだ。


もうひとつ実験。今度は分割コンパイルを行ってみる。
Hoge10.h

#ifndef __HOGE10_H__
#define __HOGE10_H__

class Hoge10 {
private:
    int hoge;
public:
    void printHoge();
};

#endif // __HOGE10_H__


Hoge10.cpp

#include <stdio.h>
#include "Hoge10.h"

void Hoge10::printHoge() {
    printf("hoge = %d\n", hoge);
}


これらからオブジェクトファイルを作成。

$ g++ -o Hoge10.o -c Hoge10.cpp


hoge プロパティを public へ変更したヘッダファイルを用意。
Hoge10_2.h

#ifndef __HOGE10_H__
#define __HOGE10_H__

class Hoge10 {
public:
    int hoge;
    void printHoge();
};

#endif // __HOGE10_H__


メインプログラムを用意
Hoge10_main.cpp

#include "Hoge10_2.h"

int main(int argc, char *argv[]) {
    Hoge10 hoge;
    hoge.hoge = 1;
    hoge.printHoge();
}


コンパイル&実行

$ g++ -o Hoge10_main.o -c Hoge10_main.cpp 
$ g++ -o Hoge10 Hoge10_main.o Hoge10.o
$ ./Hoge10
hoge = 1

ヘッダファイルのアクセス指定子を変更するだけでprivateプロパティとしてコンパイルされたはずのオブジェクトファイル内のプロパティにアクセスできた。やはりアクセス指定子はコンパイル時にしかチェックされていないようだ。

まとめ

  • private プロパティもポインタ経由で改竄可能
  • アクセス指定子はコンパイル時にしかチェックされない