20

I took a basic 40-hr Reverse Engineering course a few summers ago. While teaching us to use IDAPro, the instructor demonstrated, rather quickly and without explaining much, how to label certain variables in the ASM as members of a structure, basically equivalent to a good old fashioned struct in C/C++, and treat them as such wherever they are seen in the rest of the code. This seems rather useful to me.

What he did not cover, however, was how to identify a structure. How do you know when a group of variables does in fact constitute a structure and not just a set of related variables? How can you be sure that the author used a struct (or something similar) there?

  • Out of curiosity, was that my (Rolf Rolles) class? – Rolf Rolles Mar 20 '13 at 7:16
  • No, it was not. – Ken Bellows Mar 20 '13 at 14:50
  • I believe I've taught the section of the course you're referring to (although probably not when you took it). We generally spend more time on the subject now than we used to. Sorry about the confusion! – user1354557 Mar 22 '13 at 0:08
  • This was a private course internal to my company, so unless you happen to be employed by the same folks and actively working in this field, it's unlikely. I imagine there are a lot of these around. – Ken Bellows Mar 22 '13 at 13:45
20

There are very common patterns that you will find in code that denote structure usage.

Dereferencing offsets:

If you have a pointer which is dereferenced at some non-zero offset, you are probably dealing with a structure. Look for patterns like:

mov eax, [ebp-8]  ; Load a local variable into eax
mov ecx, [eax+8]  ; **Dereference a dword at eax+8**

In this example, we have a variable that contains a pointer, but we care about the contents of memory at some specific offset ahead of the pointer. This is exactly how structures are used: We get a pointer to the structure, and then dereference the pointer plus some offset to access a specific member. In C, the syntax for this is: pMyStruct->member_at_offset_8.

Side note: Do not confuse dereferencing at the offset of some variable with dereferencing at offsets of the stack pointer or the frame pointer (esp or ebp). Of course, you could think of the local variables and function arguments as being one large structure, but in C, they are not explicitly defined as such.

More subtle pointer offsets:

You don't actually need to dereference anything to detect a structure member. For example:

mov eax, [ebp-8]     ; Load a local variable into eax
push 30h             ; num = 30h
push aSampleString   ; src = "Sample String"
add eax, 0Ch
push eax             ; dst = eax + 0xC
call strncpy

In this example, we are copying up to 0x30 characters from some source string to eax + 0xC (see strncpy). This tells us that eax probably points to a structure with a string buffer (of at least 0x30 bytes) at offset 0xC. For example, the structure may look something like:

struct _MYSTRUCT
{
    DWORD a;      // +0x0
    DWORD b;      // +0x4
    DWORD c;      // +0x8
    CHAR d[0x30]; // +0xC
    ...
}

In which case, the sample code would look like:

strncpy(&pMyStruct->d, "Sample String", sizeof(pMyStruct->d));

Side note: It is possible (though unlikely) that we could be copying to a large string buffer at offset +0xC, but you would be able to determine this through context. If, say, offset +0x8 were an integer, for example, then it's definitely a struct. But if we copied a string of fixed length 0xC to address eax then copied another string to address eax+0xC, it's probably one giant string.

All reads / all writes:

Let's say you have a struct (not a pointer to a struct) as a local variable your stack. Most of the time, IDA doesn't know the difference between a struct on the stack or a bunch of individual local variables. But a huge tip-off that you're dealing with a structure is if you only ever read from a variable without writing to it, or (less so) if you only write to a variable without reading from it. Here's an example of each:

lea eax, [ebp+var_58]  ; Load THE ADDRESS of a local variable into eax
push eax
call some_function
mov eax, [ebp+var_54]  ; Let's say we've never touched var_54 before...
test eax, eax          ; ...But we're checking its value!
jz somewhere
...

In this example, we're reading from var_54 without ever writing anything to it (within this function). This probably means that it is a member of a structure which was accessed from some other function call. In this example, it's implied that var_58 might be the start of that structure, since its address is pushed as the argument to some_function. You can verify this by following the logic of some_function and checking if its argument is ever dereferenced (and modified) at offset +0x4. Of course, this doesn't necessarily have to happen in some_function -- it could happen in one of its child functions, or one of its child functions, etc.

A similar example exists for writing:

xor eax, eax
mov [ebp+var_28], eax  ; Let's say this is the *only* time var_28 is touched
lea eax, [ebp+var_30]
push eax
call some_other_function
...

When you see local variables being set and then never referenced again, you can't just forget about them, because they could very likely be members of a structure which is passed on to another function. This example implies that a structure (which starts at var_30) is written to at offset +0x8 before the address of that structure is passed to some_other_function.

Both of these examples in C might look like:

some_function(&myStruct);
if (myStruct.member_at_offset_4) ...

and

myStruct.member_at_offset_8 = 0;
some_other_function(&myStruct);

Side note: Although each of these examples used local variables, the same logic applies to globals.

Documented functions that expect structures:

This one's probably obvious, and IDA will handle this for you almost all of the time, but an easy way to know when you have a structure in your code is if you call a documented function that expects a certain structure. For example, CreateProcessW expects a pointer to a STARTUPINFOW structure. This one shouldn't require an example.

How do I know if these patterns actually indicate structure usage?

One final point I want to make is that in all of these cases, yes, technically, the author of the program could written their code without the use of structures. They also could have written their code by defining every function as __declspec(naked) with a large __asm inline. You'd never be able to tell. But arguably, it doesn't matter. If there are logical groups of values that are stored contiguously in memory and passed from function to function, it is still meaningful to annotate them as structures. Almost all of the time, this is how the author wrote their code anyway.

If you need me to elaborate on anything, let me know.

  • 2
    Wow. This is impressively thorough. – Ken Bellows Mar 22 '13 at 13:43
  • "Of course, you could think of the local variables and function arguments as being one large structure, but in C, they are not explicitly defined as such." It's also worth pointing out, though, that IDA's interface for specifying stack variables within a function is pretty much the same as that for defining structures. Conflating the two in C isn't all that helpful, but in IDA, it's good to recognize the similarities. – Joshua Taylor May 18 '16 at 12:39
22

You can't. In C, structs are there for the readers of the C program and their use in the program image is kinda optional. It's entirely possible that in the original program some crazy jerk decided to do everything with perfectly-sized char* buffers and cast and add appropriately, and you would never know the difference.

The 'struct' labeling is entirely for your benefit as a code viewer. It could very well be that struct labels you apply to a program are actually two variables always stored next to each other. This won't matter as long as it doesn't lead you to false conclusions about what the program does though.

  • 1
    Very interesting. Can you give an example of a case when defining a struct could lead to the wrong conclusion? – Ken Bellows Mar 19 '13 at 19:57
  • @KenB You risk making false conclusions even if the programmer did use a struct, for example if the struct is in a union so that the data structure can have different layouts depending on the code paths or stage in the object lifecycle. – Gilles Mar 19 '13 at 20:29
  • one (kind of) contrived example I can think of is function identification by argument type. there is a call eax, and you know the parameter is a pointer passed as an argument, and because you believe that pointer points to a struct of type baz, you then believe that 'eax' could contain a function with signature 'void (*)(struct baz *);'. but if your structure identification is incorrect, then this doesn't need to be the case. – Andrew Mar 19 '13 at 20:40
  • 3
    A pity that this answer was accepted too quickly and now gets upvote just because of it. While it's not always possible to do reliably, it's definitely possible to recover or at least guess struct layout in many cases, as evidenced by other answers. – Igor Skochinsky Mar 20 '13 at 2:51
  • 1
    I don't think it's constructive to say it's not possible. Though you can't say for sure if it was a struct, as you say it can be of great "benefit as a code viewer". You must be conservative about what conclusions your draw - follow the execution path first! - but it can be a very useful tool. – Robert Mason Mar 20 '13 at 12:38
11

Finding structs is tricky, but can help in understanding the code a lot. As Andrew said, structs are just an C abstraction and in assembly, it is just a blob of memory and there is no fool proof way of identifying structs. However, for simpler programs, some heuristics can be helpful. For example, "small" sized arrays are more likely to be structs than giant arrays. Seeing, for example, ints read from a loop would make it appear to be an array, while reading a couple of ints at some constant offset would make it look more like a struct. Another way is seeing the same group of dereferences happening in different areas of the code. If two different functions take some pointer as paramater and both try to deference offset 0x10 followed by 0x18 followed by 0x14 or something, it could be code setting fields of a struct. Also, any access of different sized data dereferenced from a single inputted pointer is a good indicator.

9

The easiest way of knowing when you are dealing with a structure, is when the code is calling functions for which you know (or documentation states) takes a structure as an argument.

For example the in_addr structure of the inet_ntoa function.

Given that IDA didn't figure this out in the first place.

  • Very good point. – Ken Bellows Mar 20 '13 at 14:41
8

I try to look for situations where a pointer to a chunk of data is passed into a function, then when it's used in that function, different offsets from it are treated as different data types. That indicates to me that 1) it's not an array of a single data type, and 2) it's being referenced from a base address rather than being passed as a separate parameter. It also helps when you see it being allocated dynamically (malloc or new) then being filled in with data of various types.

In IDA, I always advise my students to go ahead and create the IDA structs for things they suspect to be structures, and fill them in with elements as they see them being referenced, even if they later turn out to be wrong/misled. It's very much an iterative process, filling it in with more detail as you gain more understanding about the program and how it uses that data as you go, so it's important to mark up things you figure out as you figure them out.

8

There's several methods you can use (most of which have been hinted at by previous answers), but for completeness' sake I'll list some here.

  1. Look for calls to libraries that expect structures. Unless the executable is doing something it really shouldn't and casting pointers and then feeding them to the library function and hoping it doesn't crash (unlikely), then here you can get a good feel for what sections of memory are structures.

  2. Look at how parts of memory are passed/returned from functions. Some compilers/calling conventions/platforms pass and return small structures (which can fit in a couple of registers) differently than other types of data. For example, if you see data being returned in both eax and edx then you're probably dealing with a small struct.

  3. Look at how the memory is addressed. If it appears that sections of memory are getting modified past one register size through a pointer, it's most likely (again, unless they're doing something unusual) an array or struct. Also, like Yifan said, if there's a lot of accesses via constant offset you're probably looking at a struct, or a small array that is used in a manner similar to a struct (such as unsigned char rgb[3]). And if you're looking at different size chunks of data, then it's almost definitely a struct.

However, with any what-if scenario, as long as the use of data is consistent with your model, it doesn't matter if in the original code it is just an array of bytes with some conventions about what goes where or a full-blown struct. Use whatever model helps you reason about the code.

To address the union issue about structs having different memory layout in different code branches, I'll address the two most common usages of unions (in my experience):

  • Type punning: In this case, you probably won't even see it in the compiled code. Though it's technically undefined behavior AFAIK, most compilers will treat it like a reinterpret_cast<>()

  • Algebraic Types: Usually, this is accompanied by a tag which makes it easy to identify. If you see code in several places that checks some integer and then treats the memory different based on that value, then you can guess this is what is going on. If certain functions don't check the tag, then that also tells you something about the function - assuming the author isn't lazy and want their code to break, they probably consider it an invariant that that function will only be called in branches that already know that it is of a certain type.

  • 1
    FWIW, eax:edx return usually means a 64-bit integer, not a small struct. – Igor Skochinsky Mar 20 '13 at 13:57
1

As others have said, I usually look for a function call where a reference is passed - but I also look for instances where a malloc'ed buffer is returned (this is how you know/confirm the size of the structure) - and various members of the 'buffer' are set/initialized.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.