合計 昨日 今日

共有メモリに構造体中のポインタの「中身」をコピーする方法について

フォーラムルール
フォーラムルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
Name: tarojo
[URL]
Date: 2014年9月11日(木) 07:31
No: 1
(OFFLINE)

 共有メモリに構造体中のポインタの「中身」をコピーする方法について

はじめて質問します。

現在共有メモリに複数の構造体をメンバに持った構造体を確保しようとしており、それを他のプロセスで参照しようとしています。
ローカルで共有メモリに確保する構造体ごと作成し、ローカル構造体を共有メモリ構造体にmemcpyして更新しようと考えていました。

しかしメンバの構造体は「ポインタとして宣言した構造体」や、ダブルポインタをメンバとして持っており、単純にmemcpyするとメンバのポインタはローカルのアドレスを持ってしまいます。

共有メモリに確保されたままのアドレスで、そのアドレス先にコピーする方法はないでしょうか。

中身について、
構造体のメンバである「ポインタとして宣言した構造体」はcharの配列をメンバとして持っています。
下のコード例での「stKid_t *strSt」が、memcpyするとローカルのアドレスがコピーされてしまいます。
このポインタの指す先はそのままで、指す先の中身を変更したいと考えています。

ちなみに共有メモリのサイズはsize(stPrnt_t)ではなく、各メンバの実体分のサイズを足し上げた分を確保しています。
コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// ポインタとして宣言した構造体の使い方
typedef stKid
{
char str1[128];
char str2[256];
 
}stKid_t
 
typedef stPrnt
{
stKid_t *strSt;
}stPrnt_t
 
// 例えばこのような使い方をしたい
stPrnt_t stPrnt;
// ローカルでmallocやらデータ格納やら
for (i = 0; i < 10; i++) {
printf("%s¥n", stPrnt.strSt[i].str1);
}

Name: YuO
[URL]
ハッカー(150,489 ポイント)
Date: 2014年9月11日(木) 10:03
No: 2
(OFFLINE)

 Re: 共有メモリに構造体中のポインタの「中身」をコピーする方法について

基本的に,共有メモリであれファイルへの出力であれ,ポインタをオフセットに変換して取り扱う必要があります。

なお,VC++固有の仕様としてベース ポインターというのがあります。
これは正に内部的にもポインタを(ポインタ+オフセット)として取り扱えるようにする機能です。
これを使えば,記述は楽になるかと思います。
Offtopic :
使ったことがないですが……。
また,普通に考えると
コード[C++]: 全て選択
1
*pointer
コード[C++]: 全て選択
1
*(base_pointer + offset)
になるのですから,ポインタアクセスが遅くなりそうです

Name: sleep
[URL]
Date: 2014年9月11日(木) 17:30
No: 3
(OFFLINE)

 Re: 共有メモリに構造体中のポインタの「中身」をコピーする方法について

プロセス同士で仮想メモリ空間は共有されないので、
仮想メモリ空間内のアドレス(ポインタ)をコピーしたところで
別のプロセスでは別の仮想メモリ空間なんですよね。

同じアドレス(ポインタ)を参照したところで、似て非なる場所なので
格納されているのは全然関係ないものです。

別のプロセスで使用するためには、結局どこかのタイミングで全ての値を
何かしらのカタチでコピーすることになりますね。

OpenProcess で、例え相手プロセスの仮想メモリ空間を参照できたところで
結局、値にアクセスするためには自分の仮想メモリ空間に値を持ってこないといけない訳で
そのタイミングでどうしても ReadProcessMemory による値コピーが必要になります。

Name: sleep
[URL]
Date: 2014年9月11日(木) 19:09
No: 4
(OFFLINE)

 Re: 共有メモリに構造体中のポインタの「中身」をコピーする方法について

迷ったんですが・・・
こういう方法もある、という、あくまで情報としてだけ提供しておきます。
特にこの件については答える気は無いので、必要であれば自分の力で調べてください。

別プロセスの仮想メモリ内アドレス(ポインタ)経由で
OpenProcess、WriteProcessMemoryを使用することで
値の書き変えをするという方法を、一応、例という形でだけ示してはおきます。
(ビルド環境:Windows7、VS2013update3)

Process1.cpp
コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
 
#include <windows.h>
 
#include <iostream>
using namespace std;
 
 
typedef
struct stKid {
    char str1[128];
    char str2[256];
} stKid_t;
 
typedef
struct stPrnt {
    stKid_t *strSt;
} stPrnt_t;
 
 
 
DWORD GetProcessID();
 
 
int main()
{
    DWORD idProcess = GetProcessID();
    cout << "idProcess: " << idProcess << endl;
 
    HANDLE hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, idProcess);
    if (!hProcess){
        cerr << "OpenProcess(): " << GetLastError() << endl;
        cin.ignore();
        return -1;
    }
 
    LPVOID pInterProcessSharedPointer = VirtualAllocEx(hProcess, NULL, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    if (!pInterProcessSharedPointer){
        cerr << "VirtualAllocEx(): " << GetLastError() << endl;
        cin.ignore();
        return -1;
    }
 
    stKid_t stK = { "hello", "world" };
    stPrnt_t stP = { &stK };
 
    DWORD   len = 0;
    BOOL    res = 0;
    if (!WriteProcessMemory(hProcess, pInterProcessSharedPointer, &stP, sizeof(stPrnt_t), &len)){
        cerr << "WriteProcessMemory(): " << GetLastError() << endl;
        cin.ignore();
        return -1;
    }
 
    printf("pInterProcessSharedPointer: 0x%p\n", pInterProcessSharedPointer);
    cout << endl;
    cout << stP.strSt->str1 << endl;
    cout << stP.strSt->str2 << endl;
    cout << endl;
    cout << "このタイミングで Process2のプログラムの pInterProcessSharedPointer を、こちらの pInterProcessSharedPointer で書き換えてビルドしてください。" << endl;
    cout << "その後、Process2の実行を開始し、止まったらEnterを押下してください。" << endl;
    cin.ignore();
 
    cout << stP.strSt->str1 << endl;
    cout << stP.strSt->str2 << endl;
 
    cout << endl;
    cout << "Enterを押下するとプロセスを終了します。";
    cin.ignore();
 
    VirtualFreeEx(hProcess, pInterProcessSharedPointer, 0, MEM_RELEASE);
    CloseHandle(hProcess);
    return 0;
}
 
DWORD GetProcessID()
{
    DWORD idProcess = NULL;
    char name[512] = { 0 };
    GetConsoleTitle(name, sizeof(name));
    cout << name << endl;
    string cosole_name(name);
 
    DWORD idThread = GetWindowThreadProcessId(FindWindow(NULL, cosole_name.data()), &idProcess);
    if (!idThread || !idProcess) {
        cerr << "GetWindowThreadProcessId(): " << GetLastError() << endl;
        cin.ignore();
        return -1;
    }
 
    return idProcess;
}


Process2.cpp
コード[C++]: 全て選択
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
 
#include <windows.h>
 
#include <iostream>
using namespace std;
 
 
typedef
struct stKid {
    char str1[128];
    char str2[256];
} stKid_t;
 
typedef
struct stPrnt {
    stKid_t *strSt;
} stPrnt_t;
 
 
DWORD GetProcessID(const char name[]);
DWORD GetProcessID();
 
 
int main()
{
    //Process1.exe のパス
    char name[512] = { "C:\\Process1.exe" };
 
    //注意: ビルド&実行前に、相手プロセスのPIDを指定する必要がある。
    DWORD idProcess = GetProcessID(name);
    cout << "idProcess: " << idProcess << endl;
 
    HANDLE hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, idProcess);
    if (!hProcess){
        cerr << "OpenProcess(): " << GetLastError() << endl;
        cin.ignore();
        return -1;
    }
 
    DWORD   len = 0;
    stKid_t stK_clone = { 0 };
    stPrnt_t stP_clone = { 0 };
    stP_clone.strSt = &stK_clone;
 
 
    //注意:
    // Process2が止まったら、Process1画面で表示されている相手プロセスの pInterProcessSharedPointer と
    // 同じ値にハードコーティングで書き換えた後、ビルドして実行してください。
    LPVOID pInterProcessSharedPointer = (LPVOID)0x001C0000;
 
 
    stKid_t *strSt;
    if (!ReadProcessMemory(hProcess, (LPCVOID)pInterProcessSharedPointer, &strSt, sizeof(strSt), &len)) {
        cerr << "ReadProcessMemory(): " << GetLastError() << endl;
        cin.ignore();
        return -1;
    }
    // (LPCVOID)strSt->str1 は、相手プロセスの仮想メモリ空間内のメモリアドレスを指しているだけで、こちらのプロセスで直接中身が参照できるわけではない。
    if (!ReadProcessMemory(hProcess, (LPCVOID)strSt->str1, stP_clone.strSt->str1, sizeof(stP_clone.strSt->str1), &len)) {
        cerr << "ReadProcessMemory(): " << GetLastError() << endl;
        cin.ignore();
        return -1;
    }
    // (LPCVOID)strSt->str2 は、相手プロセスの仮想メモリ空間内のメモリアドレスを指しているだけで、こちらのプロセスで直接中身が参照できるわけではない。
    if (!ReadProcessMemory(hProcess, (LPCVOID)strSt->str2, stP_clone.strSt->str2, sizeof(stP_clone.strSt->str2), &len)) {
        cerr << "ReadProcessMemory(): " << GetLastError() << endl;
        cin.ignore();
        return -1;
    }
 
 
 
    //★相手プロセスの仮想メモリ空間内の値を書き換える
    char str1[128] = "good evening";
    if (!WriteProcessMemory(hProcess, (LPVOID)strSt->str1, str1, sizeof(str1), &len)){
        cerr << "WriteProcessMemory(): " << GetLastError() << endl;
        cin.ignore();
        return -1;
    }
 
 
    cout << stP_clone.strSt->str1 << endl;
    cout << stP_clone.strSt->str2 << endl;
 
    cout << endl;
    cout << "Process1でEnterを押下して、stP.strSt->str1 の内容が書き換わったか確認してください。" << endl;
 
    cout << endl;
    cout << "Enterを押下するとプロセスを終了します。";
    cin.ignore();
    CloseHandle(hProcess);
    return 0;
}
 
DWORD GetProcessID(const char name[])
{
    DWORD idProcess = NULL;
    cout << name << endl;
 
    DWORD idThread = GetWindowThreadProcessId(FindWindow(NULL, name), &idProcess);
    if (!idThread || !idProcess) {
        cerr << "GetWindowThreadProcessId(): " << GetLastError() << endl;
        cin.ignore();
        return -1;
    }
 
    return idProcess;
}


Process1 で確保したヒープメモリの内容を
Process2 側でアドレス(ポインタ)のみの参照から 相手プロセスのヒープメモリの内容を書き換えています。

例を示すために
共有メモリにセットされたローカルアドレス(ポインタ)を
相手プロセスのヒープメモリのアドレス(ポインタ)で見立てて代用してます。

説明用に書き殴ったものなので、コードを短くするために
めんどうな実行方法を取らなければ実行できないようになってます。

準備
-----------------------------------------------------------------------------------------------------------
Process2.cpp の 27行目
コード[C++]: 全て選択
1
char name[512] = { "C:\\Process1.exe" };

を、Process1.cpp をビルドして完成した実行ファイルのパスに書き換えておきます。
-----------------------------------------------------------------------------------------------------------

実行方法
-----------------------------------------------------------------------------------------------------------
① Process1 起動
→ Process1 が途中で止まり、メッセージが表示されるので指示にしたがう。
② Process2 の コード 49行目 を指示どおり、Process1の pInterProcessSharedPointer で直に書き換え、ビルドして起動。
コード[C++]: 全て選択
1
LPVOID pInterProcessSharedPointer = (LPVOID)0x001C0000;

③ Process2の動作が止まったら、Process1側の画面でEnterキーを押して確認。
→ Process1側の stP.strSt->str1 が "hello" から "good evening" に書き換わっている。
④ Process1、Process2 共に終了。
-----------------------------------------------------------------------------------------------------------

ただ、折角の仮想メモリ空間を超えてまで、こんなことをする必要があるかは疑問です。


Return to C言語何でも質問掲示板

オンラインデータ

このフォーラムを閲覧中のユーザー: なし & ゲスト[22人]