我这里有一个包含语言包的 .so 文件,我想编辑它们。我的问题是我并不总是有足够的空间来进行干净的翻译。我知道我可以更改文本,如果它们保持相同的长度或变短(用 NULL 填充其余部分)。是否可以创造更多空间?占位符并调整文件大小并跳转到二进制文件的末尾?或者可能是对 .txt 文件的引用?
编辑 .so 文件
对的,这是可能的。虽然您可以通过多种方式解决您的问题,但我会为您提供我认为最简单的一种。您可以使用的工具是objcopy。它允许您更改 ELF 文件的内容,我将向您展示如何使用它来实现您的目标的分步示例。在下面提供的示例中,我在 64 位 Linux 机器上工作。
考虑以下 C 程序:
#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;
}
让我们暂时假设我们没有它的源代码(因为我想让它尽可能简单,这里没有错误检查)。如您所见,它在开始时接受参数(语言索引)并以给定语言打印字符串。但是我们想添加一个额外的字符串,比如说stringInYourFavouriteLanguage
让我们的程序也能够打印它。
我们的第一步将是找到包含不同语言的所有字符串的 ELF 部分。为此,您可以使用objdump - 只需查看部分名称,即可在其中看到这些字符串。就我而言,.rodata
如下所示:
请注意,我们在这里遇到了额外的问题 - 每个字符串LANGUAGES_TABLE
只占用 21 个字节,而stringInYourFavouriteLanguage
使用 30个字节。我稍后会返回。
现在,我们想要准备一个文件,比如说langData
,包含我们想要在新.rodata
部分中拥有的数据。我建议你只复制现有的,然后用不同的语言附加字符串的修补版本——这样你就可以避免更改.rodata
代码访问中的所有偏移量;您只需要更改与语言相关的这些。
在我们的示例中,我们还需要将 的第二维更改LANGUAGES_TABLE
为 30,因此我们将在此数组中的每个字符串的末尾添加 9 个 NULL 字节。然后我们将stringInYourFavouriteLanguage
+NULL附加到文件的末尾。附加到文件从包含整个数据之前.rodata
,我们的文件是这样的:
现在,我们可以拼接这些文件,并得到一个文件-langData
包含要添加到新的数据.rodata
。然后我们像这样使用objcopy:
objcopy --update-section .rodata=langData langPack langPackPatched
,langPack
我们要修改的ELF文件在哪里,langPackPatched
是输出文件。您现在可以使用 objdump 检查此文件并查看确实进行了更改。
所以现在,我们有了.rodata
想要的内容。现在我们想让我们的可执行文件也能够打印stringInYourFavouriteLanguage
。
为了实现这一点,我们需要在我们的程序中实际更改一些代码 - 首先,我们必须更改偏移量,以便每次内存访问都LANGUAGES_TABLE
将使用我们附加的数据,其次,我们需要更改缩放比例,以便而不是打印PATCHED_LANGUAGES_TABLE[21*arg]
,我们的程序将打印PATCHED_LANGUAGES_TABLE[30*arg]
. 这是整个过程中最棘手的部分。
首先,您需要找到要修补的代码段。在我们的例子中是:
注意从offset开始的21个字节0x6e8
实际上负责将arg乘以21并将其加载LANGUAGES_TABLE
到RDX的地址。在这里,lea
使用 RIP 计算此偏移量。我们需要做的是确定我们想要添加到 RIP 中的偏移量来实现LANGUAGES_TABLE_PATCHED
。
在这里,请记住一件重要的事情——我们必须根据修补
文件中这lea
条指令的地址来确定这个偏移量。
所以,要么我们把它留在原地,只改变偏移量(即操作码的最后 4 个字节),以便它指向我们的补丁表,要么我们只写我们的代码,然后添加相关数量的nop
s(然后我们可以写lea
aslea [rip]
然后简单地用调试器检查所需的偏移量并将其更改为lea [rip+offset]
) - 这就是我所做的。尽管如此,我们修补后的代码可能如下所示:
它负责计算 的地址LANGUAGES_TABLE_PATCHED[30*arg]
。
就是这样了!我们现在可以测试它;如果我们运行./langPackPatched 5
,我们得到:
stringInYourFavouriteLanguage,
根据需要,而其他参数的程序行为保持不变。
注意:我的原始答案是在没有访问您要修改的文件的情况下制定的,因此根据您上传的文件,这里有另一个答案。
如果您想修改程序中的数据,则必须掌握如何(以及在哪里)访问它。在您的情况下,涉及三个部分:
.data
.data.rel.ro
.rodata
.data
文件中的所有字符串都通过该.data
部分引用,该部分包含一个数组,该数组带有指向 in 条目的指针.data.rel.ro
,其中.rodata
存储了指向 in中的字符串的实际指针。在分析之后radare2
,.data
看起来像:
因此,它只是包含每个字符串项的指针和索引的对数组。这是前面提到的三个部分中唯一不需要更改的部分。
.rodata
这是我建议首先更改的部分 - 将所有中文字符串替换为德语字符串。您可以按照我之前的回答中描述的方式进行操作,即用之前创建的文件的内容替换整个部分。
.data.rel.ro
这是您要修改的最后一部分。它包含指向.rodata
在您修改之前位于该部分中的字符串的指针。
因此,现在您需要修复此处包含的所有指针以指向.rodata
您刚刚创建的新部分中的字符串。请注意,这里的每个条目只是一个指针(即包含4
字节,因此它具有固定长度),因此您可以立即修补它,而无需使用objcopy
.
完成这些步骤后,您应该已翻译所有字符串。