Fixing Prism Ark’s Choices with Simple Code Injection

This is actually the title screen I have since I was mucking around with images to figure out the file format.

Prism Ark has been a project that’s been on and off a few times, being translated by Aroduc. Part of the reason for the ‘off’ parts was because of technical issues, things I wasn’t able to or didn’t have time to fix. I take a lot of the blame, it’s easy to have too many things to do and not have time to dedicate on some projects. Part of the reason though is that some of the work required was beyond my technical understanding. Fixing technical problems are hard when you don’t know how to do so.

The main issue is that unlike past technical issues I tackled on other games, which I sort of fudged my way past with IdaPro and a hex editor, this game definately required the ability to modify or rewrite some of the code in order to make it work. I procrastinated a lot, to the point where I’m not sure if Aroduc has just completely given up and dropped the project or not. Still, I wanted to make good on my promise to fix the remaining technical issues, even if the project no longer is moving forward.

The breakthrough came when I got SVN access to the Rance VI project. SLC, the programmer involved in the project, performed a series of elaborate changes to the game in order to upgrade the game for the English release, and I was able to see how it worked. I used that as a starting point to try to understand how to do changes directly to a games code. I experimented, and eventually came to an understanding of how it works on a simple level.

This post is a tutorial of sorts, explaining how to do the changes to fix a text related issue on the game. Some of the terminology and methodology might be rough, since I’m just learning as I go, but I hope that this might be helpful in getting someone pointed in the right direction.


 

Lets take a look at the game we’re working on, Prism Ark. The game has a number of text related issues, but the game script allow enough control over text rendering that working around those issues without modifying the executable is possible. In game dialog choices however aren’t so lucky. The scripts allow control over font size, what text is displayed, and not much else. Attempting to jam English text in there gives a surprisingly unpleasant result:

Character spacing is all over the place, and on the 3rd option text is even getting cut off. The fourth option is just 16 capital letter ‘i” in a row, but the spacing is clearly broken. Certainly not ideal. It’s worth taking a few seconds to try to understand exactly what’s going wrong here.

Japanese text when stored in Shift-JIS format is two bytes in length. The game, likely intending only to display Japanese text, loads two bytes at a time and generates an outline for that character. The game is probably ignoring the font metrics entirely and setting it’s own width between characters. That’s fine for Japanese text, since all characters have the same width, but English isn’t the same. If the game displayed English with fixed spacing between characters that would be fine, but because the game is rendering two bytes at once, that means it’s generating outlines for two English characters at once. Windows will respect the font metrics (such as the font spacing and kerning) for both those characters, which results in correct spacing for those two letters, but then each pair will use different spacing between them, resulting in what we see above. Definitely not ideal.

But what can you do about it? The most obvious solution would be to modify the glyph generation code to only draw one character at a time rather than two, but that wouldn’t be ideal. For one, we’d lose the ability to do any multi-byte characters. And second, there’s an easier, low tech way of generating glyphs for only one character at a time: we only have to give it one character at a time to render. If every second character in our string is a space or null character, we’d only get one character out per two bytes. It looks something like this:

Text is no longer spaced erratically, but now we’ve got way more space between characters than we want. Japanese text is often called full width, which (I’m just guessing) is because characters arejust as wide as they are tall. English is roughly half as wide, or half width. If we shrink the character spacing by half then, we should get readable English text.

In order to do that though, we’ll need to get our hands dirty. For this part, I’m using Ida Pro with the HexRays add-on. Both of those tools unfortunately are pretty expensive. The web is wide and deep though, so I imagine one could track down a copy without too much difficulty… just be careful when downloading strange things from the internet.

So we have our executable file, and we know what kind of changes we want to make to it, but where do you start looking? Figuring out what code we need to modify is probably harder than doing the modifications themselves.

Lucky for us, we already have a pretty good hint to start with: it’s text related. If the game relies on windows to generate outlines for text characters, and most of the time they do if they’re using windows system fonts, we can simply search for those function calls and work backwards from there until we find the main text loop. Things would be more complicated if the game cached characters, pre-generated outlines, or even relied on a font atlas, but lucky for us that’s not the case. A text search for ‘Outline’ gives us what we need.

This is our first hit when viewed with HexRays, and it looks promising. GetGlyphOutline is a windows API call that returns an outline or bitmap of what ever text you ask it to. This almost certainly is where the game is generating individual character outlines. We don’t really need to know exactly what it does, just a rough idea is fine.

Since this looks like a good starting point, what we’ll do is set a breakpoint here and run the game through the debugger. When the game hits this code, the plan is to follow execution back until we find the main text loop, and then work from there. While trying this I hit a small snag, in that this code appears to be called when any text is drawn, so what I did is I waited until just prior to a choice is displayed, then set the breakpoint. It’s was possible that these choices wouldn’t use the code in question and not trip the breakpoint, but we’re lucky, and the breakpoint is triggered.

Ida Pro has a handy debug option that lets you execute until it returns from the current function, so I used that to work my way back through the call stack. Each time it returns, I take a look at the HexRays decompile of the code to see if there’s anything promising worth looking at. And after a few steps of doing this, we came back up into this code:

Sure, the decompiled text looks like a mess, but there’s a few things that stand out. Take a look.

First thing to notice is where we returned into this loop, the function highlighted is the function we came back from. Most interestingly, it’s passing lpString as a parameter into the function, which we knows generates the text outlines, and it’s also casting it as a word, which is two bytes long. The loop is also incrementing lpString by two each time. It’s pretty easy to guess that this loop is taking two bytes at a time and then calling the function to get an image generated for it. This loop doesn’t seem to have anything to do with the position those characters are displayed on screen though… that happens further down.

We then encounter this piece of code, another loop similar to the first one. This one is even more interesting though, and is the key we’re looking for. Take a look at this line:

v23 = (unsigned int)(800 - dword_66C948 * *(_DWORD *)v20) >> 1;

Lets try to figure this out. First we have a constant of 800, we have a variable dword_66C948 which in the debugger shows has a value of 0x1c (or 28), and the whole thing is ends with a shift right by one (>>1). I couldn’t get the value of v20 from the debugger, but since the while loop uses v20 as the upper bounds, we can make some assumptions about what it represents.

If you put the pieces of the puzzle together, 800 is probably the screen width, 28 is the font size, v20 probably contains the number of characters in the string, and the shift right by one is an assembly trick that effectively acts as a division by two. We can write it out in this form:

v23 = (800 - 28 * 8) / 2;

If you haven’t guessed it by now, what this code is doing is determining the x position of the first letter. In order to center text, it takes the screen width, subtracts the width of all the text, and then divides the result by two to get the left margin. The left margin is then used to position the start of the text. Then, farther down within the loop we can see this line:

v23 += dword_66C948;

Since we know v23 is the x position of the character being drawn, and dword_66C948 contains the font width, we can tell pretty easily that this is is advancing the x position by the font width. This is the code we want.

Now that we’ve determined what we want to modify, the next step we need to figure out is ‘how’. This is the part that tripped me up for the longest time. As I mentioned before, how I ended up figuring this out was a bit lucky. By chance though I had access to Rance VI’s subversion, and by snooping around a bit I was able to see the scripts used to generate the patched executable and dll files. What he used were FASM with a set of macros to help with patching code. It’s an indirect help, but big shoutout to SLC for getting me pointed in the right direction.

FASM, or Flat Assembler, is an assembly compiler and takes assembly code and outputs executables or binary data. We could just use the assembler straight, generate the code we want as binary and manually insert it into the exe by hand, but that’s a lot of work. To simplify things, we’ll use a set of macros that help us merge changes into the exe for us. I didn’t want to use SLC’s macros since I don’t know who authored those, so I’m using an earlier version published by Grom PE (http://grompe.org.ru/), which does the trick. I’ve added a patchuntil macro similar to SLC’s, but otherwise the code is the same as Grom PE’s.

Before we start writing our patch though, we’ll need to create a place within the exe for our code to live. Like we did with Bunny Black, we’ll first add a new section to the executable using LordPE.

Here we’ve added a new section called .patch, and made sure it was executable and had enough space for our code. I used 0x20000 bytes as the size for the new section, which is waaay more than enough, as we’ll only will end up using about 40-50 bytes, but we can use that space later on for other things. The exe won’t be usable until we physically add that space though, so after making our section changes, we’ll need to open up the file in a hex editor and jump to the end and add in those 0x20000 bytes. Be sure to run it to make sure it still runs.

We’ll also need those other values from LordPE. In particular, we need to know the Image Base address (0x400000 in this case), the VOffset and ROffset of the section we’ll be modifying (.text), and the section we’ll be adding our code to (.patch).

Next, fire up your favorite text editor, since we’ll be making an assembly file to make our patch. We’ll save it as ‘Prism.asm’, but you can pick whatever name you want.

First, we’ll add in some basic header info.

format binary as 'exe'
use32

include 'patching.inc'

patchfile 'P_ARK200_orig.exe'

Here we specify the output format, in this case “binary as ‘exe'”. This means the code we’ll be generating is exe type, and binary means we aren’t generating a PE header or other stuff. Then we specify ‘use32’ so that it knows we’re generating 32 bit code, and then we’ll include our patch macros from patching.inc. You can find the one I’m using on the github page here. Then, we use the patchfile macro to specify the exe we’ll be patching.

We’ll also need to calculate some information on the exe we’re modifying. We’ll put in the following values we recorded earlier:

IMAGE_BASE = 0x00400000
TEXT_VOFFSET = 0x00001000
TEXT_ROFFSET = 0x00001000
PATCH_VOFFSET = 0x00476000
PATCH_ROFFSET = 0x00178000

We’ll need to calculate the org address (origin I think, or location it will reside in memory) for each section. We can figure that out with “Image Base + Virtual Offset – Real Offset”. Thus, our code will look like this:

TEXT_ORG = IMAGE_BASE + TEXT_VOFFSET - TEXT_ROFFSET
PATCH_ORG = + IMAGE_BASE + PATCH_VOFFSET - PATCH_ROFFSET

Also, we also need that address that points to the font size, which we can define like this:

FontSize = 0x0066C948

With that out of the way, lets take a look at the code we’ll need to modify. HexRays gives us a good overview at a glance, but we’ll need to look at the assembly to know what to change. Our first modification will be the character advance code, since that’s the easiest to start with. Lets look at that in assembly.

Really simple isn’t it? It loads FontSize into the eax register, loads [ebp+0] (which almost certainly holds the current x position) into ecx, and then adds them together. For clarity, I renamed dword_66C948 to FontSize.

Lets get started. We’re going to write a function that gets called in place of the original code, perform our updated version, and then return back. The first part looks like this:

patchsection TEXT_ORG

patchat (0x454F4E - TEXT_ORG)

  call ChangeSpacing

patchuntil (0x454F18 - TEXT_ORG)

The patchsection and patchat are both from our patching macros. The patchsection macro specifies the code section we’re patching, and patchat specifies the location in the file we’re modifying. We need to pass the physical address in the exe for this to work though, which we can calculate by subtracting the virtual address by the section org. Following that is a call to ChangeSpacing, which is a function we’ll create that holds our code. And finally, since we don’t want any of the original code executing, we use the patchuntil macro fills in the remaining space with NOPs, no operations. A NOP is a single byte command and are convenient to use to remove code.

So what does our ChangeSpacing look like? It looks something like this:

patchat (0x876000 - PATCH_ORG)

ChangeSpacing:
  mov eax, [FontSize]
  shr eax, 1				;the only new line, divide font size by 2
  mov ecx, [ebp+0]
  add ebx, eax
  retn

First we start with the patchat macro, this time using the address of our new patch section, so that the compiler knows where to put our new code. Then we have a label definition for ChangeSpacing.

What follows after that is pretty similar to the original code. We load FontSize into eax, with square brackets around FontSize to signify it’s loading the address at FontSize rather than the value of FontSize. Then our only change here is to shift eax right by 1. Shifting right as mentioned before does the same thing as dividing by 2, but is less messy than using a div or idiv statement. Then the remainder of the original code, [ebp+0] is loaded into ecx, and they’re added together. The retn statement returns back to where it called from.

With that done, run the .asm file with fasm, and hopefully we’ll get a new exe. Cross your fingers and see what happens when you hit the option.

We did it! Well, almost. The text looks right, but now it’s in the wrong position. As you might have figured, the centering code still thinks our text is twice as wide as it is, so we’re going to have to fix that. Lets take a look at the centering code in assembly:

It’s a bit more involved, but nothing too complicated. It moves the memory pointed at by ebp+0 (which at this point in the code contains a number of characters of text in the string) into eax, moves eax into ecx (for some reason), and then multiplies it by FontSize. Then 0x320 (800) is moved into ebx, and then the result of the previous operation is subtracted from it. Then on the last line, it divides the result in ebx by 2 by using a shift.

You’ll notice I skipped over the second last line, the “add esp, 0x20”. I’m not sure why this is in here, but it’s unrelated to the code we want to modify. The esp register is the stack pointer, and this code adds 0x20 (or 32 decimal) to it. The stack pointer always points to the top entry in the stack, but the stack starts at the end of it’s available space and grows forward (for some reason), so this operation is freeing 32 bytes from the stack. This’ll have some impact to the code we write.

Since this code appears before the first bit of code we modified, we’ll need to place this code after the patchsection, and before the previous patchat command in our assembly file. The patchat/patchsection macros need to appear in the order they appear in the exe, since the macro can’t backtrack into the file.

Here’s what our function call looks like:

patchat (0x454F00 - TEXT_ORG)

  call ChangeCentering
  add esp, 0x20

patchuntil (0x454F57 - TEXT_ORG)

Like last time, we make a call to our yet created function titled ChangeCentering. I should note that we’ve left the add esp command outside of our function. Since function calls work by pushing a return address to the stack, freeing memory from the stack in our function means the return address isn’t going to be where we expect it to be, crashing our program. Since freeing memory from the stack isn’t important to what we’re doing, we can just leave it outside here.

Now onto the implementation of ChangeCentering. We can put this anywhere after the patchsection PATCH_ORG statement.

ChangeCentering:
  mov eax, [ebp+0]		;load character count into eax
  mov ecx, eax			;move eax into ecx
  mov ebx, [FontSize]		;put font size into eax
  shr ebx, 1			;divide font size by 2 (shift right by 1)
  imul ecx, ebx			;multiply (ch count) by (font size / 2), result in ecx
  mov ebx, 0x320		;put 800 into ebx
  sub ebx, ecx			;subtract 800 by ecx
  shr ebx, 1			;divide that result by 2 (shift right by 1)
  retn

This certainly looks scary, but it’s not too bad if you take a closer look. Most of it is unchanged from the original, but you can follow along with the comments about what it does. Mostly what we’re doing is instead of multiplying ecx by FontSize, we instead load FontSize into ebx, divide it by 2 (using a shift right), and then we’re multiplying ecx with that result. The rest is unchanged.

And that’s it! At this point, our completed .asm file should look something like this:

format binary as 'exe'
use32

include 'patching.inc'

patchfile 'P_ARK200_orig.exe'

;--------------------------------------------
; Addressing stuff
;--------------------------------------------

IMAGE_BASE = 0x00400000
TEXT_VOFFSET = 0x00001000
TEXT_ROFFSET = 0x00001000
PATCH_VOFFSET = 0x00476000
PATCH_ROFFSET = 0x00178000

TEXT_ORG = IMAGE_BASE + TEXT_VOFFSET - TEXT_ROFFSET
PATCH_ORG = + IMAGE_BASE + PATCH_VOFFSET - PATCH_ROFFSET

;--------------------------------------------
; Patching!
;--------------------------------------------

FontSize = 0x0066C948


patchsection TEXT_ORG

;patch the centering code
patchat (0x454F00 - TEXT_ORG)

  call ChangeCentering
  add esp, 0x20

patchuntil (0x454F18 - TEXT_ORG)



;patch the character spacing
patchat (0x454F4E - TEXT_ORG)

  call ChangeSpacing

patchuntil (0x454F57 - TEXT_ORG)


patchsection PATCH_ORG

patchat (0x876000 - PATCH_ORG)

ChangeCentering:
  mov eax, [ebp+0]	;load character count into eax
  mov ecx, eax		;move eax into ecx
  mov ebx, [FontSize]	;put font size into eax
  shr ebx, 1		;divide font size by 2 (shift right by 1)
  imul ecx, ebx		;multiply (ch count) by (font size / 2), result into ecx
  mov ebx, 0x320	;put 800 into ebx
  sub ebx, ecx		;subtract 800 by ecx
  shr ebx, 1		;divide that result by 2 (shift right by 1)
  retn

ChangeSpacing:
  mov eax, [FontSize]
  shr eax, 1		;the only new line, divide font size by 2
  mov ecx, [ebp+0]
  add ebx, eax
  retn

patchend

Cross your fingers and hit compile, and check out what the output looks like.

It works! We did it, we patched the game to make options display English text correctly.

So I hope this guide gives you some insight into how this kind of thing works. In case it sounds like I know what I’m talking about, I really don’t. I sort of bungled into figuring this out after many many hours of trying to figure things out… but the results aren’t bad. I hope this might be a help to some of you out there who are figuring things out like I am. If you want the code and macros used, you can take a look at the final data on github (https://github.com/Doddler/PrismArkExe).

Leave a Reply

Your email address will not be published. Required fields are marked *