Jump to content
  • Advertisement
Sign in to follow this  
Akusei

Multiple structs that are similar

This topic is 2574 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have a program I'm working on and it requires that there be multiple structures that hold the same data but are all slightly different. All the code to process the data within the structures is basically the same... with some tiny differences. Right now I'm defining a base class and multiple derived classes that each define their structures as private members. I'm overriding the base class functions that use the structures so I can process the data properly and get the correct data with the structure defined within the appropriate class.

The problem with this is that it produces a lot of extra code that seems to be copied and pasted all over the place; not to mention the maintenance of this is a nightmare. There has to be an easier way. I'm thinking of maybe making the base class a template class and still define the separate structures, but add all the unused fields for each structure to the end of any given struct so that the base class template doesn't complain that a field doesn't exist in the provided structure type. Is there a better way to do this?

NOTE: Yes, the structure members sound related to malware and that is because they are. I am a malware reverse engineer and am creating a tool to make my life easier when I come across a certain type of malware.

5 example structures that are slightly different. The DWORD is a typedef to unsigned long and BYTE is a typedef to unsigned char.
[source lang="cpp"]
struct ConfigData
{
DWORD Signature;
char CopyFilename[16];
char ID[8];
char Password[10];
char GUID[39];
ConnectionInfoType1 Connections1;
ConnectionInfoType1 Connections2;
BYTE DoStartupReg;
BYTE CopyToDir;
BYTE DoMelt;
BYTE BePersistent;
BYTE DoKeylogger;
char Reserved[7];
char PIMutexName[10];
char KeyloggerMutexName[10];
};

struct ConfigData
{
DWORD Signature;
char CopyFilename[16];
char ID[8];
char Password[10];
char GUID[39];
ConnectionInfoType1 Connections1;
ConnectionInfoType1 Connections2;
BYTE DoStartupReg;
BYTE CopyToDir;
BYTE DoMelt;
BYTE BePersistent;
BYTE DoKeylogger;
char PIMutexName[10];
char KeyloggerMutexName[10];
};

struct ConfigData
{
DWORD Signature;
char CopyFilename[16];
char ID[8];
char Password[10];
char GUID[39];
ConnectionInfoType1 Connections1;
ConnectionInfoType1 Connections2;
BYTE DoStartupReg;
BYTE CopyToDir;
BYTE DoMelt;
BYTE BePersistent;
BYTE DoKeylogger;
char PIMutexName[10];
char KeyloggerMutexName[10];
BYTE Reserved[117];
BYTE DoInject;
char InjectFile[20];
};

struct ConfigData
{
DWORD Signature;
char CopyFilename[16];
char ID[8];
char Password[11];
ConnectionInfoType1 Connections1;
ConnectionInfoType1 Connections2;
BYTE DoStartupReg;
BYTE CopyToDir;
BYTE DoMelt;
BYTE BePersistent;
BYTE DoKeylogger;
BYTE Reserved[46];
char PIMutexName[12];
};

struct ConfigData
{
DWORD Signature;
char CopyFilename[16];
char ID[8];
char Password[32];
char GUID[39];
ConnectionInfoType1 Connections1;
ConnectionInfoType1 Connections2;
BYTE DoStartupReg;
BYTE CopyToDir;
BYTE DoMelt;
BYTE BePersistent;
BYTE DoKeylogger;
char PIMutexName[10];
char KeyloggerMutexName[10];
char Reserved[50];
BYTE DoInject;
char InjectFilename[20];
};
[/source]

Maybe unions would help in this situation; I'm not sure because I've never really had a need for them in the past and therefore have no experience with them.

Thanks

Share this post


Link to post
Share on other sites
Advertisement
Use composition instead of inheritance. Define a struct Header which contains all the common data (i.e. stuff that's always the same size and at the same location), then have each "extended" struct contain a Header instance followed by the variant data. Place common logic in the Header struct member function implementations, and you should be good to go.

Share this post


Link to post
Share on other sites

Use composition instead of inheritance. Define a struct Header which contains all the common data (i.e. stuff that's always the same size and at the same location), then have each "extended" struct contain a Header instance followed by the variant data. Place common logic in the Header struct member function implementations, and you should be good to go.


Ok, this makes sense, but how does it prevent me from creating 5 separate classes that use these structures? For example:

[source lang="cpp"]
struct Header
{
int common1;
int common2;
char common3;
};

struct ConfigData1
{
Header Common;
int configdata1;
char configdata2;
};

struct ConfigData2
{
Header Common;
short testdata;
};
[/source]

ConfigData2 contains "testdata" which is in no other structures which is why it's a variant but I still need to create a function that would access testdata and can't be polymorphic where testdata might not exist. I can see where the above would prevent me from having to manage many different structures and somewhat centralizes it but as far as accessing the data in the structures it still binds me to knowing the structure type, if that makes any sense. Am I just not understanding?

EDIT: The common data in all the structures I originally posted starting at the first member, would only allow me to have a Header struct with 3 member variables.

Share this post


Link to post
Share on other sites
My first recommendation would be to fix the organization of your data. If I understand you right, you have a very complex and fragile layout, and that will be a righteous pain no matter what you do.

Share this post


Link to post
Share on other sites

My first recommendation would be to fix the organization of your data. If I understand you right, you have a very complex and fragile layout, and that will be a righteous pain no matter what you do.


Exactly, and that's what I'm trying to figure out. How do I organize this to make it more manageable? Would it be better to not use a structure but instead a map of a structures that outline each item in the original structure; like offset and size of data in bytes?

EDIT: I'm not in control of the structure layout. The data in the structure is dictated by the 3rd party "program" for which I am using this tool with.

There are 5 different versions on the 3rd party program. The programs configuration is stored as a binary data structure and this configuration is very similar across multiple version with small differences here and there. I'm trying to coalesce all the versions into one manageable configuration structure so I can print the configuration no matter what version my tool is run against.

Version1 \
Version2 -> Detect Version and get config manager for that version -> print config data using polymorphism
version3 /
...

All classes should have the get member function GetInject() (as an example) but not all versions support this member variable in the configuration data structure, so what I've done so far is created a base class that has all the member functions defined that map to all the possible data in the config structure so that I can override the functions and do what I need to in the derived classes, otherwise if the structure variable does not exist, it defaults to the base class behavior.

Share this post


Link to post
Share on other sites
Why not just have one struct that is a superset of all the fields with maybe an enum field tracking which of the 5 different versions on the 3rd party program the struct represents. Or use an abstract class with setters and getters and 5 concrete derived classes that will set/get the fields they have and throw otherwise.

Share this post


Link to post
Share on other sites
Work at the meta-data level instead of worrying about the binary objects themselves. Create some code that stores a structure description and a copy of the raw binary data. This way, every data blob you have comes with a descriptor of its contents. Then build your functionality to interact with these self-describing objects instead of the original data itself.

For example, if Version1 is a DWORD and a BYTE, you store a descriptor that consists of "DWORD(1), BYTE(1)" in whatever representation you find handy. (I recommend a "record" struct which holds a type, a count, and some kind of identifier, then store a container of sequential records to describe the data format.) If Version2 then consists of a DWORD and two BYTEs, you create the descriptor "DWORD(1), BYTE(2)". If Version3 is DWORD(1), BYTE(1), DWORD(1) then you create that descriptor, and so on. You can make all of this data driven if you like, for easier maintenance.

Now, for argument's sake, let's call the first DWORD "ID", the BYTE (or pair of BYTEs, if you're using Version2) "Flags", and the second DWORD "Hash." You write a general function GetID() which takes a self-describing data block as a parameter. The function inspects the block, finds the descriptor record corresponding to "ID", and returns the appropriate value. Ditto for Flags. Hash can be handled by looking for the appropriate descriptor, and performing some default behavior (or generating an error) if the given data/version don't contain the Hash field.


Again, you can make this largely data-driven so that configuring the interpretations of each struct version is just a matter of editing a config file.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!