We have developed an application in Delphi, and there the small database can be Encrypted. We new that the encryption is no fool proof, but we were in an impression that it is not easily breakable. To our horror we discovered that the crypting of this database is actually nothing. The password can be encrypted and found in less than a minute. There are even tools available im web to do just that.
The Encrypting and Decrypted is done in this way:
procedure EncryptBuffer(var Buffer; NumBytes: Integer;
Key1: Byte; Key2: Byte);
begin
asm
PUSH EDI
PUSH EBX
XOR ECX,ECX
MOV ECX,NumBytes
JCXZ @@Done
XOR EBX,EBX
MOV BL,Key1
MOV BH,Key2
MOV EDI,Buffer
@@Loop1:
XOR EAX,EAX
MOV AL,
XOR AL,BL
XOR AL,BH
MOV ,AL
INC BL
DEC BH
INC EDI
LOOP @@Loop1
@@Done:
POP EBX
POP EDI
end;
end;
procedure DecryptBuffer(var Buffer; NumBytes: Integer;
Key1: Byte; Key2: Byte);
begin
asm
PUSH EDI
PUSH EBX
XOR ECX,ECX
MOV ECX,NumBytes
JCXZ @@Done
XOR EBX,EBX
MOV BL,Key1
MOV BH,Key2
MOV EDI,Buffer
@@Loop1:
XOR EAX,EAX
MOV AL,
XOR AL,BH
XOR AL,BL
MOV ,AL
INC BL
DEC BH
INC EDI
LOOP @@Loop1
@@Done:
POP EBX
POP EDI
end;
end;
So my question is, is there anything we could quite fast and simply do to alter this code in some way, so at least those ready made tools could not break it? Maybe one more assembler line somewhere there in between?
Of course the right way to do would be to switch to some other database. But the conversion will take time, and some instant, temporary help would be great.
In the old days I used to know something about CHASM assembler. But the time has passed, and I am not able to change anything in that code.
Any suggestions, please?
Mark
The Encrypting and Decrypted is done in this way:
procedure EncryptBuffer(var Buffer; NumBytes: Integer;
Key1: Byte; Key2: Byte);
begin
asm
PUSH EDI
PUSH EBX
XOR ECX,ECX
MOV ECX,NumBytes
JCXZ @@Done
XOR EBX,EBX
MOV BL,Key1
MOV BH,Key2
MOV EDI,Buffer
@@Loop1:
XOR EAX,EAX
MOV AL,
XOR AL,BL
XOR AL,BH
MOV ,AL
INC BL
DEC BH
INC EDI
LOOP @@Loop1
@@Done:
POP EBX
POP EDI
end;
end;
procedure DecryptBuffer(var Buffer; NumBytes: Integer;
Key1: Byte; Key2: Byte);
begin
asm
PUSH EDI
PUSH EBX
XOR ECX,ECX
MOV ECX,NumBytes
JCXZ @@Done
XOR EBX,EBX
MOV BL,Key1
MOV BH,Key2
MOV EDI,Buffer
@@Loop1:
XOR EAX,EAX
MOV AL,
XOR AL,BH
XOR AL,BL
MOV ,AL
INC BL
DEC BH
INC EDI
LOOP @@Loop1
@@Done:
POP EBX
POP EDI
end;
end;
So my question is, is there anything we could quite fast and simply do to alter this code in some way, so at least those ready made tools could not break it? Maybe one more assembler line somewhere there in between?
Of course the right way to do would be to switch to some other database. But the conversion will take time, and some instant, temporary help would be great.
In the old days I used to know something about CHASM assembler. But the time has passed, and I am not able to change anything in that code.
Any suggestions, please?
Mark
Making the code more convoluted will not help, as the implementation/approach is obviously flawed.
If you want real encryption, look into provable encryption algorithm such as Blowfish. Find an open-source implementation, link to its encryption/decryption functions, and be done with it.
The other -- more intrusive -- option is to utilize an alternative database software such as SQLite, which have encryption capabilities.
If you want real encryption, look into provable encryption algorithm such as Blowfish. Find an open-source implementation, link to its encryption/decryption functions, and be done with it.
The other -- more intrusive -- option is to utilize an alternative database software such as SQLite, which have encryption capabilities.
For AES library see Brian Gladman's Cryptographic Technology website. Good stuff there.
Making the code more convoluted
Those existing "password recovery" tools know and have the original ASM-code and that way they have had easy way in. My idea was not to try to make the code and logic more messy. Just a bit different would be fine, so the recovery utility would not work.
I completely understand the remaining weakness.
If you want real encryption, look into provable encryption algorithm
Of course, there are tons of crypting routines. Take a week or two, and you will know much more about encryption and the use of those libraries than you knew earlier:)The other -- more intrusive -- option is to utilize an alternative database
I also did mention in the original question that it would be an real option
At this point I really was after a quick hack. Maybe one more XOR:ing with some value, to get some modification to the existing logic.
Thanks for the suggestions anyway
Mark
Yes, there is something quick and easy.
Random Number Generator algorithms are always predictable, given an initial known seed value. That is to say, they spew out the same sequence of 'random' values, given the same starting value.
Pick one, give it a known seed, and then xor your data with generated randoms - have the decoder do the same thing, starting with the same known seed.
Now you have a private key encryption, using an unknown cypher.
Random Number Generator algorithms are always predictable, given an initial known seed value. That is to say, they spew out the same sequence of 'random' values, given the same starting value.
Pick one, give it a known seed, and then xor your data with generated randoms - have the decoder do the same thing, starting with the same known seed.
Now you have a private key encryption, using an unknown cypher.
Random Number Generator algorithms are always predictable, given an initial known seed value.
They are predictable, but once you do not have the ASM code nor the 20 char long password, it should not be childrens play. Once someone unassembles our application code and finds the logic, then they will get through.My original thought was, that maybe there is some 2 lines of change to the ASM code, that would prevent the existing cracker apps to get through.
And one would at least have to unassembe our app. Is this kind of simple ASM change possible?
That is to say, they spew out the same sequence of 'random' values, given the same starting value.
I have used for years string crypting so, that I first add 10 chars of truly randomized data to the beginning. And after that I add the actual string encrypted, with simple crypt algorithm. Every crypted character feeds new seed value to the next char to be crypted. I am not quite sure if that included ASM snippet uses this logic or not? With my encryption, those those 10 random chars at the beginning seem to be enough to keep 99.5% of snoopers away.
By reading the ASM snippet, I think it is not possible to use this kind of approach at all? Add let's say 2 byte to the beginning? As the routine logic takes f.ex 20 bytes in and it has to put exactly 20 bytes out too.
Pick one, give it a known seed, and then xor your data with generated randoms - have the decoder do the same thing, starting with the same known seed.
If this was a real suggestion, then my first problem I can't do that in ASM.Maybe there is no quick hack available at all?
I thank for all the comments I have got this far.
Mark
If this was a real suggestion, then my first problem I can't do that in ASM.
Then don't do it in asm?
A bit of random and xoring bytes together can be done in most languages.
I really don't see why your encryption code was written in ASM in the first place.
It doesn't make it more secure.
Then don't do it in asm?
All right, this may be a good advice, with modern compute force.I really don't see why your encryption code was written in ASM in the first place.
It doesn't make it more secure.
SQL query scanning through some 200+ MByte database, there he fractions of a second start to matter.
After all, the advice on the first line may be the one I really should follow now.
Thanks
Mark
According to those third party developers, security has not originally (8..10 yrs ago) played the major role with that language choice. But the speed has.
SQL query scanning through some 200+ MByte database, there he fractions of a second start to matter.
The code you posted was extremely trivial asm, not something that a reasonbly decent compiler cannot do.
Sure, if it was highly optimized SSE code, then it may be out of reach of a compiler... But this was just vanilla 386 code, nothing fancy.
Writing something in ASM doesn't automatically make it faster.
On the contrary, most programmers cannot optimize as well as a modern compiler.
For example, your code uses the 'loop' instruction, which is considerably slower than just sub ecx, 1 jnz. Something every compiler knows, but apparently not the person who wrote that asm.
I could write a C-version and compile it, and benchmark to prove the point... but I don't think I have to.
Okay, I had some time to kill, so I decided to write a C version.
I hope you realize that the algo is fully reversible, and EncryptBuffer is exactly the same as DecryptBuffer().
In C, the entire algo can be put on one line, it is THAT simple.
Here is my full test program:
The results on my Core2 Duo laptop speak volumes:
ASM time: 0.073059
C time: 0.032137
The simple C version is more than twice as fast.
I hope you realize that the algo is fully reversible, and EncryptBuffer is exactly the same as DecryptBuffer().
In C, the entire algo can be put on one line, it is THAT simple.
Here is my full test program:
#include "stdafx.h"
#include <Windows.h>
#include <string>
void EncryptBuffer(char* Buffer, unsigned int NumBytes, char Key1, char Key2)
{
__asm
{
PUSH EDI
PUSH EBX
XOR ECX,ECX
MOV ECX,NumBytes
JCXZ Done
XOR EBX,EBX
MOV BL,Key1
MOV BH,Key2
MOV EDI,Buffer
Loop1:
XOR EAX,EAX
MOV AL,
XOR AL,BL
XOR AL,BH
MOV ,AL
INC BL
DEC BH
INC EDI
LOOP Loop1
Done:
POP EBX
POP EDI
}
}
void DecryptBuffer(char* Buffer, unsigned int NumBytes, char Key1, char Key2)
{
__asm
{
PUSH EDI
PUSH EBX
XOR ECX,ECX
MOV ECX,NumBytes
JCXZ Done
XOR EBX,EBX
MOV BL,Key1
MOV BH,Key2
MOV EDI,Buffer
Loop1:
XOR EAX,EAX
MOV AL,
XOR AL,BH
XOR AL,BL
MOV ,AL
INC BL
DEC BH
INC EDI
LOOP Loop1
Done:
POP EBX
POP EDI
}
}
void EncryptBufferC(char* pBuffer, unsigned int length, char key1, char key2)
{
for (unsigned int i = 0; i < length; i++)
pBuffer ^= (key1++ ^ key2--);
}
int _tmain(int argc, _TCHAR* argv[])
{
char Key1 = 0x34;
char Key2 = 0x73;
char testString[] = "Testing testing";
LARGE_INTEGER frequency, startTime, endTime;
QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(&startTime);
int len = strlen(testString);
for (int i = 0; i < 1000000; i++)
EncryptBuffer( testString, len, Key1, Key2 );
QueryPerformanceCounter(&endTime);
printf("ASM time: %f\n", (endTime.QuadPart-startTime.QuadPart)/(double)frequency.QuadPart);
QueryPerformanceCounter(&startTime);
for (int i = 0; i < 1000000; i++)
EncryptBufferC( testString, len, Key1, Key2 );
QueryPerformanceCounter(&endTime);
printf("C time: %f\n", (endTime.QuadPart-startTime.QuadPart)/(double)frequency.QuadPart);
getchar();
return 0;
}
The results on my Core2 Duo laptop speak volumes:
ASM time: 0.073059
C time: 0.032137
The simple C version is more than twice as fast.
The simple C version is more than twice as fast.
Thanks, not bad at all, for a voluntary work:) I thought that ASM would do it better especially with this kind of tasks. But the Compiler Optimizations, they really seem to be quite good now a days.> void EncryptBufferC(char* pBuffer, unsigned int length, char key1,
>char key2)
I believe I am able to convert that to Delphi/Pascal. I'll have to go a bit further anyway, and use algorithm that feeds the new seed after every character, so the crypting will not be quite as simple to break . I again hope this fix will not choke the DB engine.
I really should switch to a new DB engine, but that would have tons of side effects and code re-writing. I'll have to go this way right now, and with simple fixes try to make the crypting a bit tighter.
Thanks for the benchmark sample.
Mark
Hello,
If you want a simple modification you can for example insert ROL and ROR instructions like this:
Now another question is:
The password can be encrypted and found in less than a minute
What is the relation between password and key1 key2. How is the password stored and converted to key1 and key2?
If the password is entered in plain text by the user (Posted on 2011-08-30 11:37:28 by drizz
If you want a simple modification you can for example insert ROL and ROR instructions like this:
...
XOR AL,BL
ROL AL,3
XOR AL,BH
...
XOR AL,BH
ROR AL,3
XOR AL,BL
...
This of course means old passwords will not work, but then you might as well use completely different algorithm?!Now another question is:
The password can be encrypted and found in less than a minute
What is the relation between password and key1 key2. How is the password stored and converted to key1 and key2?
If the password is entered in plain text by the user (Posted on 2011-08-30 11:37:28 by drizz
Well, it's been a while since I've done Delphi, but I think it will be something like this:
I thought that ASM would do it better especially with this kind of tasks. But the Compiler Optimizations, they really seem to be quite good now a days.
Well, it's not assembly's fault, it's the fault of whoever wrote that code.
They didn't do a good job of optimizing. Not just 'now a days' either. 'loop' has been a slow instruction at least since the Pentium, back in 1992. So I bet that even some 20 years ago, the compiler would have beaten this assembly code.
It is possible to come up with code that is as good or better than the compiler. But not every programmer can do that. And a lot of these programmers either don't even know that they can't, or they refuse to admit it.
for i := 0 to NumBytes
begin
Buffer := Buffer xor Key1 xor Key2;
Key1 := Key1 + 1;
Key2 := Key2 - 1;
end
I thought that ASM would do it better especially with this kind of tasks. But the Compiler Optimizations, they really seem to be quite good now a days.
Well, it's not assembly's fault, it's the fault of whoever wrote that code.
They didn't do a good job of optimizing. Not just 'now a days' either. 'loop' has been a slow instruction at least since the Pentium, back in 1992. So I bet that even some 20 years ago, the compiler would have beaten this assembly code.
It is possible to come up with code that is as good or better than the compiler. But not every programmer can do that. And a lot of these programmers either don't even know that they can't, or they refuse to admit it.