2017-01-19 14 views
0

私は疑問も持っていません。私はこれが何らかの確認であり、対象を正しく理解していると思います。
私はリバースエンジニアリング研究をやっています。ここに私が持っているものがあります。構造体へのポインタの配列へのポインタ

struct { 
    char str[n] 
    int x 
    float a 
} 

そして、我々は我々が見ているプロセスのメモリ内のこれらの構造体の配列を持っている: のは、私たちはこのようになります構造/クラスを、持っているとしましょう。

だから、私が持っているのは構造体へのポインタの配列へのポインタです。
私が間違っていれば、あなたは私を修正してください。この配列の最初の要素(ポインタではなく実際の構造体)のx値を読み取るには、以下の手順に従わなければなりません:

  1. ポインタが指す値(4バイト)を読み取ります。
  2. オフセットがなければ、以前に値が読み取られた値も4バイトで読み込まれます(構造体が始まるアドレスにつながります)
  3. これにnに等しいオフセットを追加する必要があります。そして、ステップ2のアドレスから値を読み取る(step2result + n + 1)。

私は正しいですか?最初の構造に含まれる実際のXを取得できますか? 2番目の値からX値を取得するには、step2(+4バイト)にオフセットを追加するだけですか?

私はこれを正しくやっていると思っていますが、実際にポインタから構造体に到達できません。配列へのポインタは100%です。

お読みいただきありがとうございます。回答を待っています。それ以上の情報が必要な場合は、それを求めてください。

p.s.何もひどくひびが入ったり、教育的目的のためにすべてのものを取得したりすることはありません。

追加:
OK、これを単純化しようとすると、説明と理解が難しくなりました。今私はそれを修正しようとします。
1つの構造は、ゲームのNPCパラメータを記述します。構造体全体のサイズは0x1200です。最初の16バイトはID情報です。この情報が64バイトの文字列になった後は、その名前です。その後、X/Y/Z座標になります。これらの後のすべては重要ではありません。だから私はちょうどその構造が始まるアドレスには0x1200を加算または減算して他の構造を見つけることができます
structure /

はそれを見つけるのはとても難しい波平、ここではそれがどのように見えるかのスクリーンショットです。
構造体が始まるアドレスを検索し、それに対するポインタが見つかりました。 それから私は、ポインタを発見し、そのようなことだへのアクセスのためにスキャン:その後、私はeax値を検索し、私はこれがの配列であると考え、なぜだeax
で指し示すポインタを見つけ

mov [eax+edx*4+00320], ecx 

をポインタ。
私はこれをもう少し具体的に説明しました。

+0

構造体の配列を:

あなたは、メモリの内容の定義に特別な注意を払う必要があり、私はtestA配列定義の最初の行にいくつかのコメントを追加しました。必要以上に間接指示のレベルが1つ増えます。 – Jester

答えて

0

はははは....... 申し訳ありませんが、これは私の笑いを止めることができません。これはStackoverflowでの2日目です。私はこのジレンマに答える質問をしました。あなたがしようとしていることを私はかなり理解できませんが、あなたがパディングを説明していないと確信しています。私は昨日パッディングを学んだので、ここでお手伝いします。

まあ、すべての配列は、配列の名前として最初の要素へのポインタを持っています。あなたはデフォルトのポインタを持っているか、独自のポインタを作ることができます。あなたが直面している主な問題は、structメンバーにアクセスすることです。

//This answer is architecture and compiler dependent 
//My settings are TDM GCC 4.9.2 64bit and Windows 10 
const int n = 5; 

#pragma pack(push, 1) 
struct A{ 
    char str[n]; 
    int x; 
    float a; 
}; 
#pragma pack(pop) 

struct B{ 
    char str[n]; 
    int x; 
    float a; 
}; 

int main(){ 

    printf("Size of A is %d\n", sizeof(A)); 
    printf("Size of B is %d\n", sizeof(B)); 
    B k; 

    for(int i=0; i<n; i++) 
     printf("Address of str[%d] in k is %x\n",i, &(k.str[0])); 

    printf("Address of int x in k is %x\n", &(k.x)); 
    printf("Address of float a in k is %x\n", &(k.a)); 

} 
/* 
Result - 

Size of A is 13 
Size of B is 16 
Address of str[0] in k is 9ffe30 Address of array 
Address of str[1] in k is 9ffe30 Address of str[1] in k is 9ffe31 
Address of str[2] in k is 9ffe30 Address of str[2] in k is 9ffe32 
Address of str[3] in k is 9ffe30 And so on.. 
Address of str[4] in k is 9ffe30 
Address of int x in k is 9ffe38 Address of Array + 8Bytes 
Address of float a in k is 9ffe3c //Address of Array + 2*8Bytes 

n -- padding 
4k+1 -- 3 
4k+2 -- 2 
4k+3 -- 1 
4k -- 0 */ 

コードを見てください。構造Aはパディングされないようにパックされています。構造BはパッディングされたバージョンのAです.Bは使用しているものです。パディングはnが変化するにつれて変化します。

ここでは、ほとんどのパディングでn = 5としています。ここで最初の5バイトは配列strに割り当てられています。次の3バイトがパディングのために割り当てられます。これは、パックされたstrcutureの場合に行われるように、一度に1バイトではなくRAMが一度に8バイトにアクセスできるように行われます。パフォーマンスが向上します。パディングの標準はないため、アーキテクチャやコンパイラによって異なります。 64ビットアーキテクチャでは、一度に8バイトがアクセスされます。これは、64ビットが32ビットより高速で、ゲームが32ビットをサポートしない理由です。 int xにアクセスするには、配列のアドレスを8バイトではなく5バイトオフセットする必要があります。floatに再度アクセスするには、オフセットを8Byte増やします。 注 - ここでは、arrayのアドレスだけが出力され、arrayの別のメンバーには出力されません。 1つずつインクリメントすることで同じことが達成できます。

C++でメモリのアライメントを読み取らない場合は、

https://en.wikipedia.org/wiki/Data_structure_alignment

3

あなたの質問はアセンブリと精度は非常に重要である理由不幸な方法で、デモンストレーションを行います地雷の実際いっぱいです。

だから、私は何を持っていることはあなたが文法的に正しくない匿名の構造体を示し、そして突然あなたには、いくつかのポインタを持っている...

のですか?それはそのようには機能しません。あなたは匿名の構造といくつかの構文エラーを持っています。

ここで私は実際にデータの実際の定義なしであなたの質問の残りの部分がほとんど意味を持たないので、答えで止めるのがうまくいくと思います。しかし、あなたがこのような心の何かに持っていたとしましょう:

struct STRUCT_A { 
    char  str[17]; 
    int  x; 
    float a; 
}; 

STRUCT_A testA[3]{ 
    {"a1", 1111, 1.111}, 
    {"a2", 2222, 2.222}, 
    {"a3", 3333, 3.333} 
}; 

int foo(unsigned index) { 
    return testA[index].x; 
} 

だから、私はここに持っていることは、アレイtestAです。配列は単なるポインタではなく、コンパイル時にはC++のほんの少しですが、ポインタを使用すると喜んで "崩壊"しますが、まったく同じではありません。

testAをポインタとして使用する場合、それ以上のポインタは指しておらず、データを直接指しています。

したがって、あなたは1つのレベルではなく、2つのレベルのOPでの間接的な間接化があります。最初の要素のxを読むには、単にmov eax,[testA + 20]を実行します。ポインタがロードされていません(例はx86 32bターゲットから、他のターゲットでは+20が異なる場合があります)。

あなたが持っていただろう:

その後
STRUCT_A* testA_alias = testA; 
    // now this ^^ alias is no more array, it's just pointer 
    // (array silently decays into pointer during compilation, when asked to) 
STRUCT_A** testA_indirect = &testA_alias; 

を第二の要素のxをフェッチするために:

mov eax,[testA_indirect] ; eax = &testA_alias 
mov eax,[eax]    ; eax = testA (or &testA .. not sure how to write it, "address of data") 
mov eax,[eax + 28*1 + 20] ; eax = testA[1].x 

私のように、実際に私はこの部分で答えを編集しなければならなかった(間接の二つのレベルを作成するために管理私はアセンブリをC++で間違って読んだ、不完全なIntelの構文は私を混乱させる)。

まだ、私はどこのポインタを手に入れているのですか?これはJavaではなく、C++であり、データをメモリに直接格納するだけです。ご覧のとおり、2つの間接レベルを取得するためにかなりの努力を払わなければなりませんでした。

xがではなく+20にある理由が不思議に思うかもしれません。パディング、C++は型に応じて構造体のメンバを揃えるので、intは整列が大好きです。

これは、構造体のサイズである28についても説明します。

step2resultを+ N + +1から来たことを1

:あなたの質問でも

あなたはありますか?たぶん、あなたはこのことで混乱している:

char str[]{"abc"};    // str[4] 

しかし​​が定義されてdb 'a', 'b', 'c', 0 = 4バイトのようなものであるためです。あなたは[n]によってchar配列は、何があるの+1関与、その配列が正確n文字を持っていないことを定義したよう

char str2[]{'a', 'b', 'c'};  // str2[3] 

:あなたが唯一、Bのようにそれを定義した場合、3バイトとしてC、配列は3バイトになります。 C文字列のリテラルを入れた場合、n番目のバイトはゼロ終端文字で占有されるため、長さは最大(n-1)文字までです。


コンパイル後のソースの外観を確認することができますhere

これは、おそらくあなたの質問に最良の方法で答えます。ただ構造体が次々にパックされ

testA: 
     .string "a1"  ; three bytes 'a', '1', 0 defined 
     .zero 14   ; remaining 14 zeroed to have char[17] 
     .zero 3   ; padding for "int x" 
     .long 1111  ; int x 
     .long 1066284351 ; float a 
     .string "a2"  ; char[17] of second element 
... 
+0

元の質問を再読しました。外部バイナリからいくつかのデータ構造があるようですが、間接とポインタのいくつかの層が関係しています。そうであれば、あなたはあなたの質問にそれを十分に説明できませんでした。そのような構造体をC++で正しく定義する方法が不明な場合は、メモリの内容自体を表示してください。構造、ポインタなどの奇妙な用語では、高レベルの巨大な難読化ではなく、生のバイトを持つことが常にベストです。 ..すべてはまだメモリの単純なバイトです...私にとって少なくとも生のバイトが最適です。:D – Ped7g

+0

最初の投稿にいくつかの情報を追加しました。私はあなたが正しいと理解していれば、ゲームプロセスは.bin – WardS

+0

で終わっているので外部バイナリです。そして、実際には、これはうまくいかない場合 - これは変更します。それはなぜ実際のポインタから構造体へのアクションを繰り返すことができないのかという疑問です。 – WardS

関連する問題