EEPROM Protected Program

MatiasZ

Regular
I'm writing a program in assembler for a college class. It's a simple version of a game, and is done 100% in 8086 ASM code. I've also had to write a driver to communicate with a 512Bytes EEPROM memory connected to the parallel port, and I'm supposed to protect the game with that EEPROM memory, using it as a dongle.

It's fairly easy to make a simple protection (read a couple of memory positions, see if you find what you should), but on the next revision of the software, I need to crack someone elses protection, so someone is going to do the same with mine. Basically, I'm trying to think of a way of protecting the game with the EEPROM in a way that when debugging the code, it's not evident what to write to the EEPROM to make it work. The crackers are going to have only my binary program, and they need to figure out how to write the EEPROM so that when is plugged in my program works.

I've got a couple of ideas, but none are really convincing so far, and I'm not even sure there's a way of doing this really good (or much better) without encryption (which we can't do). What I've thought of is recording the whole memory beforehand, filling each position with data depending on this position, using some simple (or maybe not so simple) algorithm so to know what should be read/written in each position at any time given, and to "hide" the algorithm in the game, making it disperse around the code, and under conditional situations related to the status of the game. Anyway, any ideas/comments are welcomed and appreciated. If anything wasn't clear enough, please let me know.

Regards,

Matias
 
Encryption would be the best, but if you aren't allowed to I'd guess the best way would be store execution-relevant data in th EEPROM. Just so that the 'disassembler' doesn't know exactly what data that is, eg. slot = eeprom[p]; data = eeprom[p]; mymemory[slot] = data;
Needs to be something that isn't easily deduced from reading the source though.
If you're allowed to run from rw-memory it could even be pure machine code, an algorithm that would severely affect gameplay or such.

Might be possible to somehow slightly sabotage your own program, an intended rounding error or the like, that'll allow the program to run, but grow more and more unstable unless the error is 'fixed' by copying/adding something from the EEPROM.

Didn't quite follow the explanation of your idea, but if it's somehow related to obfuscating the code it'll just be a temporary obstacle and not a very big one if a good disassembler (person or program) is working on it; but that might not be relevant in this case ;)

The whole no encryption thing is a bit odd, and depending on how you perceive it it might mean that you aren't allowed to store anything that would make the program unrunnable without the EEPROM.

All MHO of course, I'm not expert no the subject :p
 
Encryption would be the best, but if you aren't allowed to I'd guess the best way would be store execution-relevant data in th EEPROM. Just so that the 'disassembler' doesn't know exactly what data that is, eg. slot = eeprom[p]; data = eeprom[p]; mymemory[slot] = data;

I'm not sure I understand this precisely. The idea would be to read an address(well, data meaning an address) and data from the memory, and compare it to that data in my program? I don't see how this is much different from reading n bytes from the memory and comparing it directly to my data in the program, I see the difference but don't actually see how it's harder to disassemble because of this. Could you elaborate it a little more?


Might be possible to somehow slightly sabotage your own program, an intended rounding error or the like, that'll allow the program to run, but grow more and more unstable unless the error is 'fixed' by copying/adding something from the EEPROM.

Sounds interesting, but too hard. I need to be able to implement it in... 6 hours most, and I'm not an assembler experienced programmer :(

Didn't quite follow the explanation of your idea, but if it's somehow related to obfuscating the code it'll just be a temporary obstacle and not a very big one if a good disassembler (person or program) is working on it; but that might not be relevant in this case ;)

Yeah I know, that's why I said I didn't like it :(

The whole no encryption thing is a bit odd, and depending on how you perceive it it might mean that you aren't allowed to store anything that would make the program unrunnable without the EEPROM.

The no encryption thing is because we haven't got through that subject yet on this class, so it's not the objective of this work. I know it is the correct way to do it, but is not what I've been asked to do right now.

All MHO of course, I'm not expert no the subject :p

Thanks a lot for the input, and keep it coming! :D
 
Do a checksum over a certain portion of the SW and/or the EEPROM, do some inversions or wraparounds or add some random number, compare the regions to some variable in your SW with added inversions or such. That should suffice unless the guys are really skilled.
 
I'm not sure I understand this precisely. The idea would be to read an address(well, data meaning an address) and data from the memory, and compare it to that data in my program? I don't see how this is much different from reading n bytes from the memory and comparing it directly to my data in the program, I see the difference but don't actually see how it's harder to disassemble because of this. Could you elaborate it a little more?
The data wouldn't be compared at all, it would be directly used.
For instance, if you had a bunch of constants. You'd initialize all of them, but initialize one or more to bad values that would disrupt the whole program; then read the correct value(s) from the EEPROM without telling which is changed.

If you need a comparison to display an "Unauthorized user. Please insert dongle" message, this would be a bit useless as the cracker will just backtrace to the condition and choose the other branch. Either bypassing the whole 'protection' or at the very least getting more information on what has been 'disrupted'.
 
You can break that into a few loosely related steps in the program so it's harder to detect/trace back.
 
Yes, I need to show a message telling that the dongle is not correct, so that makes the problem a little harder (or easier to solve for the dissassembler).

Ok, thank you all for the input. I've taken a little of everything you proposed and came up with the following solution.

I'm gonna write the memory beforehand, with randomly generated numbers, with the only condition that the first byte must be "negative" (>=0x8$), and the following 3 bytes must be positive (<0x8$), and repeat that loop untill filling the memory. In that way, al memory positions mod 4 = 0 will be negative, and the others will be positive. The idea of this is to mislead as to what is being read, since most of the data is discarded. Then, I will "randomly" choose the memory position to read by using some game real time status variables, and checking wether the positions to read are negative or positive. By knowing this, I can predict if the multiplication of both data should be positive or negative, and I'll enable a flag telling which is the case. Afterwards, I'll read the two values and multiplie them, and compare against the flag to see wether it should be a positive or negative result. If I get a bad read, I'll increment a counter. Once I reach certain number of missreads, the program will cease to function. The idea is to spread all these bits of code around the game, so that is more dificult to trace, and to put it in places that only happen in certain specific (but not too sporadic) situations of the game.

Any comments, ideas, or fundamental flaws you see on this design are welcomed ;)
 
Could have part of the program stored on the eeprom, it loads the code and runs it. Sprinkle throughout the binary. Add some self modifying bits and xor the works. :)
 
It all depends on how mean you want to be to the poor fellow student who will have to crack your program :)
 
If you can't use encryption and the program has to actually detect a valid dongle there is really nothing you can do except obscure the checking code ... I'd not bother myself, too much work for something which screws your fellow students too. Loose/loose situation.
 
Could have part of the program stored on the eeprom, it loads the code and runs it. Sprinkle throughout the binary. Add some self modifying bits and xor the works. :)

This is really a good idea. I thought of it but discarded it because it wouldn't comply exactly with the specification of the work, and it would have made it literally impossible for the guys cracking our code to actually crack it, since they wouldn't really have all the program.

MfA said:
If you can't use encryption and the program has to actually detect a valid dongle there is really nothing you can do except obscure the checking code ... I'd not bother myself, too much work for something which screws your fellow students too. Loose/loose situation.

I agree. However, the person that gets to crack our software is not really supposed to be able to achieve it, just explain what they found as a protection method, and if not possible to break it, how far they got in the process. So I'm not actually having them fail the work for having a hard protection.

Any way, I finally implemented what I explained before and it worked pretty well, and everything was disperse around the code and it even took me time to debug it when something was not working as expected. We presented the work today and got a really good feedback from the professor.

Thank you all for your input, it really shows off the type of fellows that hang around here ;)

Regards,

Matias
 
I think the best way to do it, is to make some time-critical loops in your code, read a bunch of values from the slow eeprom, do some obscure calculations and store them on multiple locations in your code or data.

The idea is to make it look like you're calculating some needed values, while you actually use the time it takes to read and calculate the values to have your loop take the right amount of time to complete. Anything time-critical would work, something hardware-related would be best. And you can set up such a condition in code yourself, with another loop, or an interrupt that jumps to wrongly-patched code.

If the program needs the values to function, it's pretty hard to crack in a short time.

Edit: if you have a real-time clock, writing some data that has to be equal to the clock when executed would be fine, especially if you use multiple loops.
 
Last edited by a moderator:
Back
Top