3

I have here an ELF file that contains a language pack (German and Chinese) and I want to swap the chinese language to the german language.

hex cannot represent Chinese :)

here an excerpt from the ELF file (libUIPublic.so):

ELF file without modification

My problem is that I don't always have enough space for a clean translation.

I know that I can change the texts if they keep the same length or become shorter (fill the rest with NULL).

here an excerpt from (libUIPublic (edited to german/english)):

ELF German (not enough space)

Is if it is possible to create more space? placeholder and resize the file and jump to the end of the ELF? or maybe a reference to a .txt file?

Here are the two ELF files: https://drive.google.com/drive/folders/1dI2P_b2t__phi3bbW9rFV5W8jnPGfF7W

2

Yes, it is possible. Although you could solve your problem in several ways, I'll provide you with the easiest one in my opinion. The tool you could use is objcopy. It allows you to change contents of ELF files and I'll show you step-by-step example how to use it to achieve your goal. In the example presented below, I'm working on 64bit Linux machine.

Consider the following C program:

#include <stdio.h>
#include <stdlib.h>

#define NUM_OF_LANGUAGES 5
#define MAX_STR_LENGTH 21

const char LANGUAGES_TABLE[NUM_OF_LANGUAGES][MAX_STR_LENGTH] = {
    "stringInEnglish",
    "stringInPolish",
    "stringInSpanish",
    "stringInItalian",
    "stringInFrench"
};

char* someOtherData = "some other data";
char* lastString = "last string in file";

int main(int argc, char** argv)
{
    int langID = atoi(argv[1]);
    printf("%s\n", LANGUAGES_TABLE[langID]);
    return 0;
}

Let's pretend for a moment that we don't have its source code (since I wanted to make it as simple as possible, there is no error checking here). As you see, it takes argument (index of language) at its start and prints string in given language. But we want to add an extra string, say stringInYourFavouriteLanguage and make our program capable of printing it as well.

Our first step will be finding ELF section containing all strings in different languages. To do this, you may use objdump - just look at the section name, where you see these strings. In my case it is .rodata as you may see below: enter image description here Please note that we encounter additional problem here - every string in LANGUAGES_TABLE takes only 21 bytes, while stringInYourFavouriteLanguage uses 30. I'll return to it in a while.

Now, we would like to prepare a file, say langData, containing data we would like to have in new .rodata section. I recommend you just copy the existing one and then append patched version of strings in different languages - this way you will avoid changing all offsets in .rodata accesses in code; you will only need to change these related to languages.

In our example, we also need to change the second dimension of LANGUAGES_TABLE to 30, so we will add 9 NULL bytes at the end of every string in this array. Then we append stringInYourFavouriteLanguage+NULL to the end of the file. Before appending to file containing entire data from .rodata, our file looks like this: enter image description here Now, we may concatenate these files and get one file - langData containing new data to be added to .rodata. Then we use objcopy like this:

objcopy --update-section .rodata=langData langPack langPackPatched

, where langPack is the ELF file we want to modify and langPackPatched is the output file. You may now check this file with objdump and see that changes were indeed made.

So now, we have .rodata having desired content. Now we want to make our executable capable of printing stringInYourFavouriteLanguage as well.

To achieve that, we will need to actually change some code in our program - firstly, we have to change offsets, so that every memory access to LANGUAGES_TABLE will use our appended data and, secondly, we need to change scaling, so that instead of printing PATCHED_LANGUAGES_TABLE[21*arg], our program will print PATCHED_LANGUAGES_TABLE[30*arg]. And that is the trickiest part of the whole process.

First of all, you need to find the piece of code to patch. In our example it is: enter image description here Note that 21 bytes starting from offset 0x6e8 are actually responsible for multiplying arg by 21 and loading address of LANGUAGES_TABLE to RDX. Here, lea computes this offset using RIP. What we need to do, is to determine offset we want to add to RIP to achieve LANGUAGES_TABLE_PATCHED.

And here, please keep in mind an important thing - we have to determine this offset depending on the address of this lea instruction in our patched file.

So, either we leave it at its place and change only offset (that is last 4 bytes of opcode), so that it points to our patched table, or we just write our code and then add relevant number of nops (we could then write lea as lea [rip] and then simply check the required offset with a debugger and change this to lea [rip+offset]) - that's what I did. Nevertheless, our patched code may look like this: enter image description here

It's responsible for calculating address of LANGUAGES_TABLE_PATCHED[30*arg].

So, that's it! We may now test it; if we run ./langPackPatched 5, we get:

stringInYourFavouriteLanguage,

as desired, while program behaviour remains unchanged for other arguments.

  • Thanks for your idea 💡 Can you help me, please? I can follow you, but I can't do it. – Alonia Apr 9 at 19:01
  • Of course I can. Could you specify which part of answer is unclear? – bart1e Apr 9 at 20:48
  • I don't know how to apply that to my file. Can I send personal messages here? I haven't found the point yet. Then I can show you the file. – Alonia Apr 10 at 5:08
  • As far as I know it's not possible to send PMs here. So you may consider uploading it here, or asking another question precisely describing the point where you have difficulties. – bart1e Apr 10 at 8:55
  • Here is a link to the file: drive.google.com/drive/folders/… – Alonia Apr 10 at 9:08
0

Note: My original answer was formulated without an access to the file you wanted to modify, so here comes another one, based on the file you have uploaded.

In the case you want to modify data in the program, it's essential to get grasp on how (and where) it is accessed. In your case, there are three section involved:

  • .data
  • .data.rel.ro
  • .rodata

.data

All strings in the file are referenced via the .data section, which contains an array with pointers to entries in .data.rel.ro, where the actual pointers to strings in .rodata are stored. After analysis in radare2, .data looks like: sectionData

Hence it's just the array of pairs containing pointer and index of each string item. This is the only section from before mentioned three, that you don't need to change.

.rodata

It contains all strings used in the file both in English and Chinese versions. sectionRodata

This is the section I recommend to change first - replace all Chinese string with their German counterparts. You may do it in a way described in my previous answer, that is replacing the entire section with a content of previously created file.

.data.rel.ro

It is the last section you want to modify. It contains pointers to the strings that were in the .rodata section before you modified them.sectionDataRelRo

So, now you need to fix all of the pointers contained here to point on the strings in the new .rodata section you have just created. Notice that each entry here is just a pointer (i.e. contains 4 bytes, so it has fixed length), so you may just patch it right away, without need to replace this section using objcopy.

After doing these steps, you should have all strings translated.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.