Search code examples
assemblyreverse-engineering

Third operand of imul instruction is a memory address, what was its original value?


I'm currently reversing a hash function from a game and encountered this:

Game.exe+1F745D - 69 C0 93 01 00 01 - imul eax,eax,Game.exe+C00193

The instruction itself is not the problem, its signed mulitply with three operands: destination, source1 and source2. Source1 and Source2 get multiplied with each other and result is stored in destination operand.

As I am trying to reverse engineer this, I wondered how this opcode would have looked like in the source code?
I doubt the devs put BASE_ADDRESS+C00193 in their code...?
(The base address of that game is 0x00400000 btw, the address is static if that matters)

The game is comparing the result of the hash function against a hardcoded value. So I think it is very unlikely that the gamedevs wanted an unpredictable outcome.

Afaik the third operand is an immediate value, if I'm not mistaken, that means it treats the third operand "as is". So the value of the third operand is not the value at that memory address (which would be 0), instead its the sum of base_address+C00193.

So when I do this in my code: hash *= 0x00400000 + 0xc00193; It does work, but what was the value in the original source code?
I have made a fully working implementation of that function that I can post if needed.


Solution

  • My guess would be that the disassembler tried to be smart here and assumed the constant is a memory address, but in reality it probably isn't. Technically, a memory address is also just a number. The disassembler will try to show things as memory address if they "look like one". (In many cases these heuristics are very useful, but sometimes they may be misleading.) According to the bytes you posted (93 01 00 01) it is actually 0x1000193.

    So the original code was probably:

    hash *= 0x1000193
    

    This would indicate that it was a 32-bit FNV hash function, like this:

    uint32_t fnv32hash(const std::string& str) {
        uint32_t hash = 0x811c9dc5;
        const uint32_t primeVal = 0x1000193;
    
        for(int i = 0; i < str.size(); i++) {
            uint8_t byte = str[i];
            hash ^= byte;
            hash *= primeVal; // Here is your imul instruction!
        }
    
        return hash;
    }