Data structures (
classes, etc) are higher level elements that completely disappear after the compilation step takes place. Meaning that the concepts themselves are completely invisible in the binary. Of course, certain code patterns may still suggest or imply that a data structure was present in the code, there are no assembly equivalent (nor one is needed) to the
After providing the above context, I'll address the specific questions and statements you've made throughout the question:
I believe that
ebp + offset are local variables?
You accurately understand that
ebp is used to reference the stack and that therefore any
ebp based reference is probably a reference to a stack-based variable.
Could it really just be tonnes of local variables?
It could. However as I've explained in the first paragraph it is impossible to know for sure whether the original source code had a lot of local variables or a single, a few, or maybe even nested structures.
It is however, unnecessary (unless your goal is to reach binary identical code reconstruction) to specifically use the same constructs used in the original code. If your goal is to gain a decent understanding of the code, you should feel free to implement those data structures as you see fit and will be the most intuitive representation for you. You should rely on IDA's structure definitions you make the assembly code as readable to you as possible, without putting too much thought as to how the original source code was written.
Or could it actually be a data structure and this is how IDA is representing it?
Therefore, this is not how "IDA represents it", but how any compiler will translate the code to assembly. IDA just helps with an interactive interface to the disassembled machine code.
ebp is the base address of the structure
As the function starts with a rather standard
mov ebp, esp / sub esp, IMM it is unlikely that
ebp itself points to a structure. It points to the stack offset at where a new stack frame was created. This is a very common practice. It is very likely, however, that a structure begins at a certain offset on the stack, and
ebp is used to reference it, using the offset from the start of the stack frame.
mov instructions between the
mov [...], offset value are padding bytes
Assuming you're talking about the
mov, REG, IMM instructions, these are probably register initializations that are used further down the line. They are spread between the stack-based
mov instructions for performance reasons. To oversimplify, pipeline optimizations allow modern processors to assign register values somewhat in parallel to slower RAM write operations, resulting in an overall faster execution.
I'm guessing they are just padding bytes since
esi doesn't contain a value (it was
xor at the top of the screenshot) and is
mov ebp + offset quite a lot in this block.
esi register does contain a value, that value is simply zero.
xoring a register with itself is a common way to set it to zero, which is shorter than a
mov REG, 0 instruction to encode. Additionally,
mov OFFSET, REG is shorter than
mov OFFSET, 0, so overall the compiler saves us a few bytes of code.