C/C++ 函数/方法与 Java 的公开

C/C++ exposure of functions / methods vs Java

本文关键字:Java 方法 C++ 函数      更新时间:2023-10-16

Minecraft Modding的世界让我对Java和C/C++库之间机制的差异感到好奇,这些机制允许在外部调用库中的方法/函数。

我的理解是,Minecraft Modding的出现是由于能够通过Java反编译/反射,以便对可以从库中调用的类和方法进行逆向工程。我相信Java类规范包含了相当多的关于类结构的元数据,允许代码以非预期的方式使用。

有一些混淆工具试图使对Java进行逆向工程变得更加困难,但总的来说,这似乎很难防止。

我没有 C/C++ 方面的知识深度,不知道在那里可以做到什么程度。

对于 C/C++代码是提前本机编译的。最终结果是特定于该平台的机器代码的程序集。C/C++ 具有外部化函数的概念,以便可以从库或可执行文件外部公开它们。一些库还有一个入口点。

通常,当连接到外部函数时,有一个头文件列出哪些函数可用于从库中编写代码。

我认为需要一种机制将公开的函数映射到库/可执行机器代码程序集中的地址,以便在正确的位置进行函数调用。

通常,将函数调用与地址连接在一起是链接器的工作。链接器仍然需要以某种方式知道在哪里可以找到这些函数。

这让我想知道是否从根本上可以调用非导出函数。如果是这样,这是否需要能够找到它们的地址并理解它们的参数格式?

据我了解,C/C++ 中的函数调用通常是通过将参数分配给简单函数的寄存器或更复杂的函数的参数数组来完成的。

我不知道在本机代码中调用非公共 API 的做法是否常见 或者,如果这样做的固有困难使本机代码非常安全地免受这种使用。

首先,有一些工具(质量和功能各不相同)可以将编译后的机器代码逆向工程回原始语言[或其他语言,就此而言]。这样做最大的问题是像C和C++这样的语言,结构中成员的名字没有名字,经常变得"扁平",那么原来是什么:

struct user
{
std::string name;
int age;
int score;
};

将成为:

struct s0
{
char *f0;
char *f1;
int f2;
int f3;
};

[当然请注意,std::string可以通过十几种不同的方式实现,而"两个指针"只是一个合理的变体]

当然,如果有一个头文件描述了库的工作原理,则可以使用其中的数据结构来获取更好的类型信息。同样,如果文件中有调试信息,则可以使用它以更好的方式形成数据结构和变量名称。但是想要将这些东西保密的人(通常)不会使用调试符号发布代码,而只会发布调用公共功能的实际必要部分。

但是,如果您了解这些是如何使用的[或阅读一些例如显示"用户"的代码,您可以弄清楚名称,年龄和分数是什么。

理解什么是数组,什么是单独的字段也可能很困难。就是它:

struct
{
int x, y, z;
};

int arr[3];

几年前,我开始玩耐心纸牌游戏(类似于"纸牌")。为此,我需要一种在屏幕上显示卡片的方法。所以我想"好吧,Windows上现有的纸牌有一个,我敢打赌我可以弄清楚如何使用它",事实上,我做到了。我可以画出俱乐部女王或黑桃两个,如我所愿。我从未完成实际的游戏部分,但我确实设法从非公共共享库中加载了抽卡功能。无论如何都不是火箭科学(有些人为具有数千个函数和非常复杂的数据结构的商业游戏这样做 - 这有两三个你需要调用的函数),但我也没有花太多时间在上面,如果我没记错的话,几个小时,从提出这个想法到拥有"有效"的东西。

但是对于问题的第二部分,插件接口(例如Photoshop的滤镜插件或视频编辑器中的过渡)通常实现为"共享库"(又名"动态链接库",DLL)。

操作系统中有一些函数可以将共享库加载到内存中,并按其名称查询函数。这些函数的接口(通常)是预定义的,因此头文件中的函数指针原型可用于形成实际调用。

只要共享库的编译器和应用程序代码使用相同的 ABI(应用程序二进制接口),当涉及到如何将参数从调用方传递到函数时,一切都应该解决 - 这不像编译器只是随机使用它喜欢的任何寄存器,参数以明确定义的顺序传递,哪个寄存器用于 ABI 规范定义的内容给定的处理器体系结构。[如果你必须知道数据结构的内容,它会变得更加复杂,并且这种结构有不同的版本 - 例如,某人有一个包含两个指针(开始和结束)的std::string,并且无论出于何种原因,设计都更改为一个指针和一个长度 - 应用程序代码和共享库都需要使用相同的std::string版本进行编译, 否则会发生坏事!

可以调用非公共 API 函数,但通过调用查询以按名称查找函数无法发现它们 - 您必须找出其他方式 - 例如,通过知道"此函数距离函数 XYZ 有 132 个字节",当然,您也不会有函数原型。

当然,Java 字节码对于许多不同的处理器架构是可移植的,机器代码仅适用于一组定义的处理器 - x86 的代码适用于 Intel 和 AMD 处理器(可能还有其他一些处理器),ARM 处理器的代码适用于使用 ARM 指令集开发的芯片,等等。您必须为给定进程编译 C 或 C++ 代码。