0

I have a checksum which I am trying to reverse engineer, I already know how to keep the checksum generating after I know the original initial value that was used to create the checksum in the first place.

So far I know the checksum is generated using a double datatype mathematical equation.

The final value for the computed checksum is a unsigned integer, but before that it gets converted from double to unsigned long long aka (unsigned __int64).

What I'm trying to do is get a unsigned integer back into the same value in double datatype to get to the next step of undoing the checksum to retrieve the original initial value.

While the checksum is computing here is the value it generates in double datatype. 3083570000.3115764 which creates a checksum 0xB7CB8B50

I don't think this is a lossy conversation so nothing is really lost even though it converts from 8 bytes double back down 4 bytes integer checksum. Why? because the double value is always created by multiplying with 4294967295.0 which I believe is only to eliminate the trailing 4 bytes kinda like a shift.

So the double value i'm looking to retrieve will have to be divided by 4294967295.0 to exactly get back the original double value to the last digit.

Problem is I cannot divide it properly because it's not 100% accurate to the last decimal point.. I know with floating-point math it's not accurate 100% with the IEEE floating point crap, but I don't care about that I'm just trying to reverse this this the same way it was created in the first place.

Outputs Say the original checksum double was 0.71794958809146792
0.71794958809146792 * 4294967295.0 = 3083570000.3115761849416764
the answer that's sent in the packet is 0xb7cb8b50

if I cast the unsigned integer 0xb7cb8b50 to unsigned __int64 manually by hand it should look like this 0x00000000b7cb8b50

The original double how it was generated in code should look like this, I used the same key before it was appended to packet to recreate the same conditions to make the checksum in the first place and it should look like this
Real ANSWER = 3083570000.3115764

So
0x00000000b7cb8b50 should equals = 3083570000.3115764 double

My reverse code looks like this

unsigned int checksum = 0xb7cb8b50;
double test1 = (double)(unsigned __int64)checksum;
double test2 = double(checksum);
double test3 = static_cast<double>(checksum);
double test4 = *((double*)(void*)&checksum);

the code above is wrong by a few decimal places.

test1 returns = 3083570000.0000000
test2 returns = 3083570000.0000000
test3 returns = 3083570000.0000000
test4 returns = 1.523486003547e-314#DEN

How do I obtain the extra, .3115764 as well is my question.

SSpoke
  • 5,656
  • 10
  • 72
  • 124
  • 1
    Pick _one_ language... – Lightness Races in Orbit Nov 26 '15 at 21:16
  • 2
    Casting a `double` to an `int` removes everything after the decimal point. – user253751 Nov 26 '15 at 21:18
  • 1
    @LightnessRacesinOrbit sorry about that I prefer to get the answer in either languages. C or C++ – SSpoke Nov 26 '15 at 21:18
  • @immibis so you are saying it's impossible to get back the decimal point data? that's actually not bad information to know, that means the checksum cannot be reversed correct? So the decimal data is truncated? – SSpoke Nov 26 '15 at 21:20
  • Is there a unsigned integer to double converter that a little more intelligent function that increases a double value by a little decimal points maybe a percentage % for more precision so you can tweak it for every unsigned value you throw at it to possibly retrieve the remaining decimal points? – SSpoke Nov 26 '15 at 21:27
  • @SSpoke: Surely you are working in one of them so that is the one you need an answer in. – Lightness Races in Orbit Nov 26 '15 at 21:43
  • 1
    @LightnessRacesinOrbit Surely s/he is smart enough to convert a C answer to C++ or vice versa. – user253751 Nov 26 '15 at 22:22
  • @immibis: If s/he knew enough about this topic to do so properly, accurately, safely and idiomatically, then s/he would not have had to ask the question. – Lightness Races in Orbit Nov 26 '15 at 22:35
  • 1
    @LightnessRacesinOrbit Why not? The question being asked is *completely unrelated* to converting between C and C++ answers. – user253751 Nov 26 '15 at 23:09
  • going from an int value to a double value will not always be an exact match – user3629249 Nov 29 '15 at 04:11
  • Perhaps I'm missing some key detail, but I do not see anything in the question about converting between C and C++. – user3629249 Nov 29 '15 at 04:13
  • in general, a checksum is a one-way operation. It can be calculated again from the original source data, but the source data cannot be re-generated from the checksum – user3629249 Nov 29 '15 at 04:15
  • @user3629249 ya i took down the C++ tag and title, but as i said I wouldn't mind code in either, seems here each question is made for 1 language only. Yeah it's s a checksum hash but parts of the checksum about half of it is transformed with many bitwise operations and then converted to double followed by a double multiplication of max unsigned integer in double format (4294967295.0) where half the data is lost while converting it back to unsigned integer where the checksum hash is finally returned, Yes it's a checksum, but it seems to me it would serve no purpose if it wasn't reversible. – SSpoke Nov 29 '15 at 04:34
  • @user3629249 I just had a gut feeling that this number `4294967295.0` has something to do with the data that would be ultimately lost upon the converstion to unsigned integer, kinda like compression so any loss would not matter, I kinda gave up on this checksum reversing but, I had a theory that the decimal points fractions could be generated with a static percentage variable, where each decimal point goes up a certain percentage for each whole number before the decimal point. – SSpoke Nov 29 '15 at 04:36

1 Answers1

2

The fractional part (after the decimal point) of a double number is lost when it is assigned to an integer.

Doing the reverse operation (assigning the resulted int to a double), the fractional part will be 0. So it is not possible to recover back the original value of the double number.

In the output of the following code it can be seen how the double value is stored in memory (the most significant 32 bits are not 0):

double initial = 0.71794958809146792 * 4294967295.0;
uint64_t rawValue = *(uint64_t*)&initial;
uint32_t checksum = (uint32_t)initial;
printf("initial: %f 0x%llx\n", initial, rawValue);
printf("after:   %u 0x%x\n", checksum, checksum);
// Prints
// initial: 3083570000.311576 0x41e6f9716a09f86f
// after:   3083570000 0xb7cb8b50

Trying to cast the raw int value to double (as in *((double*)(void*)&checksum)) is an incorrect operation, and it may even access memory not belonging to checksum (if int size if 4 bytes).

More information about double representation can be found at:

https://en.wikipedia.org/wiki/Double-precision_floating-point_format

nnn
  • 3,980
  • 1
  • 13
  • 17
  • So the lost data cannot be predicted at all? – SSpoke Nov 26 '15 at 22:45
  • 1
    @SSpoke No, it would never be possible to predict / recover it. – nnn Nov 26 '15 at 22:46
  • Thank you I still believe it's reversible, difference is accurate `0.71794958809146797007216791856852` vs not accurate `0.71794958801892343629592178303188` and I believe the first 8 or 9 decimals could hold the solution to completely undo the whole process anyways, since that 0.7 value is created by 4 unsigned shorts ya 8 bytes.. something tells me those 8 or 9 decimals which are accurate in both could still hold most of the solution, since the front value of that `0.7` is always `1.0`, the checksum involves doing `result = checksum - 1.0;` Thanks for the insight i'll mark this as the answer. – SSpoke Nov 26 '15 at 22:52
  • 1
    `uint64_t rawValue = *(uint64_t*)&initial;` causes undefined behaviour by violating the strict aliasing rule. – M.M Nov 26 '15 at 22:59
  • 1
    @M.M True, thanks. I think this could be fixed by using an `union` for writing `initial` and then reading `rawValue`. Or using `memcpy(&rawValue, &initial, 8);` – nnn Nov 27 '15 at 00:55