指向结构指针数组的指针

时间:2017-01-19 02:26:03

标签: c++ pointers assembly reverse-engineering cheat-engine

我甚至没有一个问题,我认为这是某种确认,我正确理解了这个主题 我正在进行一些反向改造研究,这就是我所拥有的。 假设我们有结构/类,看起来像这样:

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

在我们正在研究的流程的内存中,我们有一系列这些结构。

所以,我所拥有的是指向结构指针数组的指针 如果我错了,现在可以请你纠正我。要读取此数组的第一个元素的x值(实际结构,而不是指针),我必须按照以下步骤操作:

  1. 读取指针指向的值(4个字节)。
  2. 没有任何偏移读取先前读取的值指向的值,也是4个字节(这将引导我到结构开始的地址)
  3. 现在我必须为此添加等于n的偏移量。并从步骤2的地址中读取值(step2result + n + 1)。
  4. 我是对的吗?我会得到第一个结构包含的实际X吗?要从第二个中获取X值,我只需要在步骤2中添加偏移量(+4个字节)?

    我认为我这样做是正确的,但我实际上无法从指针到达结构。我会说,指向数组的指针是100%正确的。

    感谢阅读,会等待答案。如果您需要更多信息,请提出要求。

    P.S。没有得到任何破解或任何一切只是为了教育目的

    增加:
    好吧,我试图简化这个只是让它更难以解释和理解。现在我要尝试解决它。​​
    一种结构描述了游戏中的NPC参数。整个结构的大小为0x1200。前16个字节只是ID信息,然后在此信息之后是64字节的字符串,它就是名称。然后去X / Y / Z的坐标。这些之后的一切都无关紧要 它不是很难找到,这是截图如何: structure /
    所以我可以找到其他结构,只需将0x1200加到或减去该结构开始的地址即可 我搜索了结构开始的地址,并找到了指向它的指针。 然后我扫描了对找到的指针的访问并获得了类似的东西:

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

    然后我搜索eax值并找到指向eax的指针 这就是我认为这是指针数组的原因 希望我能更具体地解释一下这一点。

2 个答案:

答案 0 :(得分: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 ++中更多一点,尽管它很乐意“衰退”#34;当使用这样的指针时,它并不完全相同。

当我使用testA作为指针时,它不指向任何其他指针,它直接指向数据。

所以你没有一个级别,而是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 ++,你只需将数据直接存储在内存中。正如你所看到的,我不得不付出相当大的努力来获得两个间接级别。

现在您可能想知道为什么x位于+20而不是+17。因为填充,C ++将根据类型对齐结构成员,int喜欢对齐,所以它是。

这也应该解释28,这是该结构的大小。

同样在你的问题中你有:

  

step2result + n + 1个

+1来自哪里?也许你对此感到困惑:

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

但那是因为"abc"就像db 'a', 'b', 'c', 0 =定义了四个字节。当您将其定义为a,b,c为三个字节时,该数组将为3个字节:

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

当您按char定义[n]数组时,没有涉及+1,该数组具有完全n个字符。如果你把C字符串文字放入其中,它们最多可能是(n-1)个字符,因为第n个字节将被零终结符占用。

您可以在编译here之后检查该源的外观。

这可能会以最佳方式回答您的问题。

您可能需要特别注意内存内容定义,我在testA数组定义的初始行添加了一些注释:

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
...

答案 1 :(得分:0)

Hahahahaha ....... 对不起,但我无法阻止我的笑声,因为这是我在Stackoverflow上的第二天,我问问题是哪个回答这个困境。我不太明白你要做什么,但我很确定你没有考虑填充。我昨天学会了填充,所以我会尽力帮助你。

好吧,每个数组都有一个指向其第一个元素的指针作为数组的名称。所以你有默认指针,或者你可以创建自己的指针。在指针数组中对结构进行derefrence非常容易。您面临的主要问题是访问结构成员。

//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 */

查看code.Structure A已打包,因此不会执行填充。结构B是A的填充版本.B是你正在使用的。随着n的变化,padding会有所不同。

这里我对大多数填充采用n = 5.这前5个字节分配给数组str。现在接下来的3个字节用于填充。这样做是为了RAM可以一次访问8个字节而不是一次一个字节,就像打包的strcuture一样。这提高了性能。填充没有标准,因此它随架构和编译器而变化。在64位架构上,一次访问8个字节。这就是为什么64位比32位更快,而游戏不支持32位。 为了访问int x,你需要将数组的地址偏移8字节而不是5.要再次访问float,增加偏移量为8Bytes。 注 - 这里只输出数组的地址,而不是单独的数组成员。您可以通过递增1来实现相同目的。

如果你没有读到C ++中的内存对齐。

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