yegord / snowman

Snowman decompiler

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Snowman sometimes struggles to interpret switch tables

katstasaph opened this issue · comments

First of all I wanted to say how much I appreciate Snowman, it's been by far the most useful tool I've found for my purposes. I also wanted to preface this by saying I'm not even close to being a C++ expert and frankly not even close to being an assembly novice.

I'm (partially) reverse-engineering a Windows 95 game; the section I'm working on now is part of the health simulation. Basically, the game has about 47 possible diseases and 20 possible treatments. To narrow things down, each treatment has a corresponding function that is passed the ID of the disease it was used on, then uses a switch statement to map the 47 possible IDs to a smaller amount of cases (the diseases where the treatment is valid and has an in-game effect, and everything else), say 9. Then another switch statement with 9 cases does the effects.

The decompiled line of code from Snowman looks like this (renaming/commenting mine):

*reinterpret_cast<signed char*>(&ecx7) = *reinterpret_cast<signed char*>(filteredDiseaseID + reinterpret_cast<int32_t>(mapLaudanumSwitchCases)); // Maps health problem cases to cases 0-8

where mapLaudanumSwitchCases, in the program, is the switch table implemented like this (or close enough anyway) and appears as DB 00, DB 08, DB 08, DB 08, DB 08, DB 08, DB 01 DB 01, etc. Of the 34 original cases, 0 is mapped to 0, 1-5 are mapped to 8 (the default case, here), 6-7 are mapped to 1, and so on, until everything has been mapped to some value 0-8.

The above line of code doesn't necessarily suggest that, though (what exactly is being added?), nor does Snowman's interpretation of the table as the following function (many declarations omitted):

*eax2 = reinterpret_cast<unsigned char>(*eax3 + *reinterpret_cast<unsigned char*>(&ecx)); *eax4 = reinterpret_cast<unsigned char>(*eax5 | *reinterpret_cast<unsigned char*>(&ecx)); *eax6 = reinterpret_cast<unsigned char>(*eax7 | *reinterpret_cast<unsigned char*>(&ecx)); *ecx = *ecx + eax8; *eax9 = reinterpret_cast<unsigned char>(*eax10 | *reinterpret_cast<unsigned char*>(&ecx)); *eax11 = reinterpret_cast<unsigned char>(*eax12 | *reinterpret_cast<unsigned char*>(&ecx)); *reinterpret_cast<signed char*>(&eax13) = reinterpret_cast<signed char>(al14 + *ebx15); *eax13 = reinterpret_cast<unsigned char>(*eax13 | *reinterpret_cast<unsigned char*>(&ecx)); *eax13 = reinterpret_cast<unsigned char>(*eax13 | *reinterpret_cast<unsigned char*>(&ecx)); *reinterpret_cast<unsigned char*>(&eax16) = reinterpret_cast<unsigned char>(*reinterpret_cast<signed char*>(&eax13) + 5); *eax16 = reinterpret_cast<unsigned char>(*eax16 | *reinterpret_cast<unsigned char*>(&ecx)); *eax16 = reinterpret_cast<unsigned char>(*eax16 | *reinterpret_cast<unsigned char*>(&ecx)); *eax16 = reinterpret_cast<unsigned char>(*eax16 | *reinterpret_cast<unsigned char*>(&ecx)); *eax16 = reinterpret_cast<unsigned char>(*eax16 | *reinterpret_cast<unsigned char*>(&ecx)); *eax16 = reinterpret_cast<unsigned char>(*eax16 | *reinterpret_cast<unsigned char*>(&ecx)); *esi17 = reinterpret_cast<unsigned char>(*esi18 | *reinterpret_cast<unsigned char*>(&eax16)); *eax16 = reinterpret_cast<unsigned char>(*eax16 | *reinterpret_cast<unsigned char*>(&ecx));

Snowman here seems to have interpreted the instructions as add [eax], cl; or [eax], cl; or [eax] cl; etc., resulting in something pretty incomprehensible until I went to step through it in Ollydbg:

OllyDbg output

This particular interpretation seems to be a coincidence of the particular numbers used in this specific mapping. There are about 20 functions* like this (one per treatment), and in some of the others Snowman interprets the values from the switch table as hard-coded values, e.g., 0x12070612 or g5050505.

I'm not sure whether this is still maintained or how easy or possible it would be to address this (it doesn't happen with all switch tables), also realize this is well beyond my capability to fix, but wanted to make a note.

edit: actually a lot more than 20, I just hadn't gotten to the others yet. They all follow this pattern that I've noticed though.