📜 ⬆️ ⬇️

Reverse engineering integer division by constant

So it happens that sometimes you reverse the code that deals with some kind of routine that does not involve any serious calculations and then suddenly multiply by a large constant (cry that there is no asylum highlighter for Habré):

mov edx, [ebp+end_of_buffer] mov eax, [ebp+src] mov ecx, edx sub ecx, eax mov edx, 80808081h mov eax, ecx imul edx lea eax, [edx+ecx] mov edx, eax sar edx, 7 mov eax, ecx sar eax, 1Fh sub edx, eax mov eax, edx 


For those who do not know x86 assembler, I will explain:

 mov edx, [ebp+end_of_buffer] mov eax, [ebp+src] mov ecx, edx sub ecx, eax ; ecx =   mov edx, 80808081h ; edx = -2 139 062 143 mov eax, ecx ; eax = ecx imul edx ;   eax*edx ;    edx:eax 

At first glance, this is surprising, especially for a beginner, but my goal is not to puzzle, but to explain, therefore I reveal the cards: this is how the integer division by constant is optimized. Yes Yes. Integer division by a previously known Kostant can be replaced by integer multiplication by another constant and by processing the result with a file. This will be faster, since the multiplication takes place somewhere around five processor cycles, and the division is about 25.
')
I wasn’t banned in Google, but I didn’t find how to find the original divider, but I found several optimization guides telling how to calculate the multiplier for such a replacement. However, it was reported in the form of a ready-made algorithm, with no explanation behind the mathematics behind it.

The math associated with the possibility of such a replacement is to replace the divisor b by the inverse number 1 / b and multiply by it. But what if we deal with integer arithmetic and 1 / b will be rounded to zero whenever b> 1? Very simple: to do this, you need to represent the number 1 / b as a number with a fixed point. In this case, the integer part of the number will always be zero, and the zeros after the point are dropped to the first unit. The number of zeroes thrown away is remembered in order to make later acc. shift.

So. How to understand what constant division occurs in the source code? Perform the inverse operation: translate a binary number with a fixed point in decimal format:
0x80808081 = 2 ^ (- 1) + 2 ^ (- 9) + 2 ^ (- 17) + 2 ^ (- 25) + 2 ^ (- 31) = A
We get the inverse number B = 1 / A.

Further in the listing we see that there is a shift to the right by 7 digits:

 sar edx, 7 

This is compensation for discarding leading zeros. It corresponds to division by 2 ^ 7 degrees, that is, by 128. We do the inverse operation:

X = B * 128 ~ 255.

But you can solve it differently:
X ~ 1 / (2 ^ (- 1 - 7) + 2 ^ (- 9 - 7) + 2 ^ (- 17 - 7) + 2 ^ (- 25 - 7) + 2 ^ (- 31 - 7))

Rounding in this case is not terrible, since integer division is also performed with rounding.

Thus, in my case, the authors divided the buffer size by 255.

PS: In this case, the study of the driver of one closed USB-glands was performed. No copy protection program was harmed.

Source: https://habr.com/ru/post/256827/


All Articles