Struct Array problem

So can somebody help me with normalizing this code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
SYS_ERROR MapProfile(SYS_DI_ADAPTER const * a0, SYS_DI_PROFILE * a1, ACTION_TO_TYPE const * a2)
{
	a1->ClearItems();
	const DWORD* v3;
	for(v3 = &a2->m_8; v3; v3+=3)
	{
		SYS_DI_ADAPTER_OBJECT * v4 = a0->FindObject(*v3, 0xFFFFFFu);
		if(v4)
		{
			SYS_DI_PROFILE_ITEM* v7 = new SYS_DI_PROFILE_ITEM(*(v3-1), v4);
			v7->m_12 = a1->m_4;
			a1->m_4 = v7;
		}
		else if(*(BYTE*)(v3-2))
		{
			a1->ClearItems();
			return SYS_ERROR_UNKNOWN;
		}
	}
	return SYS_ERROR_NO;
}


"a2" is a pointer to ACTION_TO_TYPE structure wich is using in this function but I can't understand it's format.

So I think the structure ACTION_TO_TYPE must be somethink like this but I'm not sure:
1
2
3
4
5
6
struct ACTION_TO_TYPE
{
    union { DWORD m_dw; BYTE m_by[4]; } m_0;
	DWORD m_4;
	DWORD m_8;
} ; //size of 12 

or like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
struct ACTION_TO_TYPE_IN_STRUCT 
{
	DWORD m_0;
	union { DWORD m_dw; BYTE m_by[4]; } m_4;
	DWORD m_8;
} ; //size of 12

struct ACTION_TO_TYPE
{
    union { DWORD m_dw; BYTE m_by[4]; } m_0;
	DWORD m_4;
	ACTION_TO_TYPE_IN_STRUCT m_8[]; 
} ; 


Can you help me and give me idea how to interpret this structure? Please help!
Last edited on
No replays why? Please help!
There's hardly any information in this code.

1
2
const DWORD* v3;
for(v3 = &a2->m_8; v3; v3+=3)

Tells me that an ACTION_TO_TYPE structure contains an m_8 member which is a DWORD.
If we suppose a2 is an array and that this loop is used to access m_8 in the different elements of the array (that's a lot of ifs), this means that the size of an ACTION_TO_TYPE structure is 3 times the size of a DWORD (padding bytes included).
Yes but if it is an array with 12 bytes size, why the pointer is initaliazed firstly with m_8 (NOTE I use m_8 just for looking good actually it must be *((BYTE*)a2+8))? And why then are accessed members above the array (*(BYTE*)(v3-2))? Any more suggestions?
Last edited on
Clearly this code has been written by a sadist without any care for readability or maintainability.
One example: for(v3 = &a2->m_8; v3; v3+=3). If &a2->m_8 is not NULL, this loop may never end.
The only thing that can stop the loop is this condition if(*(BYTE*)(v3-2)) which may read uninitialized memory.

As for *(BYTE*)(v3-2), I guess that the first byte of a valid ACTION_TO_TYPE structure must always be 0.
It's hard to say more.
Last edited on
OH I realize a Big Mistake while decompiling this code (yeah the "sadist without any care for readability or maintainability" is me! :O). Actually this need to be the actually code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
SYS_ERROR MapProfile(SYS_DI_ADAPTER const * a0, SYS_DI_PROFILE * a1, ACTION_TO_TYPE const * a2)
{
	a1->ClearItems();
	const DWORD* v3;
	for(v3 = (DWORD*)a2 + 2; *v3 /* not v3 */ ; v3+=3)
	{
		SYS_DI_ADAPTER_OBJECT * v4 = a0->FindObject(*v3, 0xFFFFFFu);
		if(v4)
		{
			SYS_DI_PROFILE_ITEM* v7 = new SYS_DI_PROFILE_ITEM(*(v3-1), v4);
			v7->m_12 = a1->m_4;
			a1->m_4 = v7;
		}
		else if(*(BYTE*)(v3-2))
		{
			a1->ClearItems();
			return SYS_ERROR_UNKNOWN;
		}
	}
	return SYS_ERROR_NO;
}


Anyway this actually doesn't make the things clear for me :(. And this code was decompiled so actually I'm wondering for some sort of like behavior of code written not by a sadist (with the rules of C++).

Anyway I found the solution myself :):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
SYS_ERROR MapProfile(SYS_DI_ADAPTER const * a0, SYS_DI_PROFILE * a1, ACTION_TO_TYPE const * a2)
{
	a1->ClearItems();
	ACTION_TO_TYPE const * v3;
	for(v3 = a2; v3->m_8; ++v3)
	{
		SYS_DI_ADAPTER_OBJECT * v4 = a0->FindObject(v3->m_8, 0xFFFFFFu);
		if(v4)
		{
			SYS_DI_PROFILE_ITEM* v7 = new SYS_DI_PROFILE_ITEM(v3->m_4, v4);
			v7->m_12 = a1->m_4;
			a1->m_4 = v7;
		}
		else if(v3->m_0.m_by[0])
		{
			a1->ClearItems();
			return SYS_ERROR_UNKNOWN;
		}
	}
	return SYS_ERROR_NO;
}
Last edited on
This looks like the construction of some sort of linked list.

If I had to guess, I'd say a1 is a linked list, a2 an array of elements to add, and a0 some kind of list of authorized values to add to the a1.

Line 7 looks like it checks to see if *v3 is registered in a0, and if that's the case it returns a pointer v4 somehow linked to it.
It then creates a new node using *(v3-1) and v4 and adds it to a1.

Line 14 the fact the only the first byte is checked could be explained by the fact that the first member of a ACTION_TO_TYPE structure is in fact a boolean.
A compiler will generally add padding bytes after a boolean to align the memory address of the next member properly, which could explain why 4 bytes are taken but only 1 is used.

With that hypothesis, I can suggest the following structures:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct ACTION_TO_TYPE
{
    bool booleanValue;
    int value;
    int action;
};

struct SYS_DI_PROFILE_ITEM
{
    int value;
    SYS_DI_ADAPTER_OBJECT * actionPtr;
    SYS_DI_PROFILE_ITEM * nextNode;
    
    // constructor
    SYS_DI_PROFILE_ITEM(val,actPtr) : value(val), actionPtr(actPtr), nextNode(NULL) {;}
};

struct SYS_DI_PROFILE
{
    SYS_DI_PROFILE_ITEM * listHead;
    /* ... */
};


And your code could be rewritten like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
SYS_ERROR MapProfile(SYS_DI_ADAPTER const * a0, SYS_DI_PROFILE * a1, ACTION_TO_TYPE const * a2)
{
	a1->ClearItems();
	for(int n = 0; a2[n].action != 0; n++)
	{
		SYS_DI_ADAPTER_OBJECT * actionPtr = a0->FindObject(a2[n].action, 0xFFFFFFu);
		if(actionPtr != NULL)
		{
			SYS_DI_PROFILE_ITEM* newNode = new SYS_DI_PROFILE_ITEM(a2[n].value, actionPtr);
			newNode->nextNode = a1->listHead;
			a1->listHead = newNode;
		}
		else if( a2[n].booleanValue == true )
		{
			a1->ClearItems();
			return SYS_ERROR_UNKNOWN;
		}
	}
	return SYS_ERROR_NO;
}

Topic archived. No new replies allowed.