Reverse engineer Victoria 2

2023年7月31日

Victoria 2 is a game released on August 13, 2010.

Tool chain information

In the game directory, there is vcredist_x86.exe which is Microsoft Visual C++ Redistributable 2005. Hence, the main executable was compiled by Microsoft Visual C++ 2005 (or called MSVC 8).

The C++ standard is C++03. The executable was compiled for a 32-bit architecture.

You can also use Detect It Easy.

Lua

Due to the presence of defines.lua, it’s obvious that the game uses the Lua 5.1 programming language. Lua 5.1 was released in 2006. Lua only has the number type which represents real (double-precision floating-point) numbers, ie. 64 bit.

Since the bitness of the program is 32 bit, the numbers returned by Lua API has to be stored in two DWORD. A single mov assembly instruction doesn’t suffice.

In case you disassemble v2game.exe in IDA the interactive disassembler, you may see the use of LODWORD and HIDWORD. Here readFromLua actually returns int64*, but is used as int32*, and the return value is assigned to the lower part of pops->BASE_CLERGY_FOR_LITERACY. Then the higher part of the return value, now saved in v765, is assigned to the higher part of pops->BASE_CLERGY_FOR_LITERACY.

int* v764 = (int *)readFromLua((__int64 *)v2162, (int)&v949, "BASE_CLERGY_FOR_LITERACY");
LODWORD(pops->BASE_CLERGY_FOR_LITERACY) = *v764;
v765 = v764[1];
HIDWORD(pops->BASE_CLERGY_FOR_LITERACY) = v765;

pops->BASE_CLERGY_FOR_LITERACY is of course an int64 field, which implemented by the compiler by simply using two 32-bit integers together. Extra instructions are needed to handle carry and borrow bit for arithmetic.

A snippet of reading MIL_HAS_EVERYDAY_NEED from defines.lua

Section offset B84F3A calls function readFromLua. It reads pops.MIL_HAS_EVERYDAY_NEED. readFromLua multiplies a Lua number by 32768, then round it. Hence [latex]round(-0.1 \times 32768)=-3277[/latex].

By calling convention, the return value of a function, including readFromLua, is usually stored in EAX. EAX is a pointer, and the pointed value is -3277 in INT. Watch 20 in Figure verifies it.

Then, Section offset B84F42 reads [eax+4]. EAX is a pointer. The pointed address plus 4, the value is FFFFFFFF. What does it mean?

Use Windows Calculator, it shows -3277(10) is FFFFFFFFFFFFF333(16).

Although the Watch view can’t use int64 data type, the dump view can. Right click the status bar, select Follow in Dump, then you can change the data type of the dump.

In Dump 2, I view the data as int32, the same as the Watch view. -3277 is followed by -1, where -1 is FFFFFFFF(16). In Dump 1, the data type is signed long long (int64), the value is -3277 by itself.