C/C++ 暴露函数/方法 vs Java
C/C++ exposure of functions / methods vs Java
Minecraft Modding 的世界让我好奇 Java 和 C/C++ 库之间允许外部调用库中的方法/函数的机制差异。
我的理解是 Minecraft Modding 的出现是由于能够反编译/反映 Java 以便逆向工程 class 可以从库中调用的 es 和方法。我相信 Java class 规范包含大量关于 classes 结构的元数据,允许以非预期的方式使用代码。
周围有一些混淆工具试图使逆向工程变得更加困难Java,但总的来说似乎很难预防。
我对 C/C++ 的了解不深,不知道在那里可以做到什么程度。
对于 C/C++ 代码是提前本地编译的。最终结果是特定于该平台的机器代码集合。 C/C++ 具有外部化函数的概念,以便它们可以从库或可执行文件外部公开。一些图书馆也有一个入口点。
通常,当连接到外部函数时,会有一个头文件列出哪些函数可用于从库中进行编码。
我假设需要一种机制将公开的函数映射到库/可执行机器代码程序集中的地址,以便在正确的位置进行函数调用。
通常将函数调用与地址连接在一起是链接器的工作。链接器仍然需要以某种方式知道在哪里可以找到这些函数。
这让我想知道是否从根本上可以调用非导出函数。如果是这样,这是否需要能够找到他们的地址并理解他们的参数格式?
C/C++ 中的函数调用据我所知通常是通过将参数分配给简单函数的寄存器或更复杂的函数的参数数组来完成的。
不知道在本机代码中调用非public API的做法是否普遍
或者如果这样做的固有困难使得本机代码在这种使用中非常安全。
首先,有一些工具(质量和功能各不相同)可以将编译的 machine-code 逆向工程回原始语言 [或另一种语言,就此而言]。这样做最大的问题是C、C++等语言,结构体中成员的名字是没有名字的,经常变成"flat",那么原来是什么:
struct user
{
std::string name;
int age;
int score;
};
将变为:
struct s0
{
char *f0;
char *f1;
int f2;
int f3;
};
[当然要注意 std::string
可能有十几种不同的实现方式,"two pointers" 只是一种可能的变体]
当然,如果有描述库如何工作的头文件,您可以使用其中的数据结构来获得更好的类型信息。同样,如果文件中有调试信息,可以用它更好地构成数据结构和变量名。但是想要将这些东西保密的人(通常)不会发布带有调试符号的代码,而只会发布调用 public 功能所需的实际部分。
但是如果你了解这些是如何使用的[或者阅读一些代码,例如显示 "user",你可以弄清楚什么是名字,年龄和分数。
了解什么是数组以及什么是单独的字段也可能很困难。是哪个:
struct
{
int x, y, z;
};
或
int arr[3];
几年前,我开始玩耐心纸牌游戏(类似于"Solitaire")。为此,我需要一种在屏幕上显示卡片的方法。所以我想 "well, there's one for the existing Solitaire on Windows, I bet I can figure out how to use that",事实上,我做到了。我可以随心所欲地画梅花皇后或黑桃二。我从未完成实际的 game-play 部分,但我确实设法从 non-public 共享库加载了 card-drawing 功能。无论如何都不是火箭科学(有些人为具有数千个函数和非常复杂的数据结构的商业游戏做这件事——这有两个或三个你需要调用的函数),但我也没有花太多时间在这上面,如果我没记错的话,几个小时,从提出想法到得到 "works".
但是对于问题的第二部分,plugin-interfaces(例如 Photoshop 的滤镜插件或视频编辑器中的过渡)通常实现为 "shared libraries"(又名 "dynamic link libraries", DLL).
OS 中有一些函数可以将共享库加载到内存中,并通过名称查询函数。这些函数的接口(通常)是pre-defined,因此header-file中的函数指针原型可用于形成实际调用。
只要共享库的编译器和应用程序代码使用相同的 ABI(应用程序二进制接口),就如何将参数从调用者传递给函数而言,一切都应该解决 - 这是与编译器只是随机使用它喜欢的任何寄存器不同,参数以 well-defined 顺序传递,并且哪个寄存器用于 ABI 规范为给定处理器体系结构定义的内容。 [如果你必须知道数据结构的内容,它会变得更加复杂,并且这种结构有不同的版本 - 例如有人有一个 std::string
包含两个指针(开始和结束),无论什么因此,设计更改为一个指针和一个长度 - 应用程序代码和共享库都需要使用相同版本的 std::string
进行编译,否则会发生不好的事情!]
Non-public API 函数可以调用,但是通过调用按名称查找函数的查询将无法发现它们 - 您必须找出其他方法 - 对于例如知道 "this function is 132 bytes on from the function XYZ",当然,你也不会有函数原型。
当然还有额外的复杂性,其中 Java 字节码可移植到许多不同的处理器架构,机器代码仅适用于一组定义的处理器 - x86 的代码适用于 Intel 和 AMD 处理器(也许其他一些),ARM 处理器的代码在使用 ARM 指令集开发的芯片中工作,等等。您必须为给定进程编译 C 或 C++ 代码。
Minecraft Modding 的世界让我好奇 Java 和 C/C++ 库之间允许外部调用库中的方法/函数的机制差异。
我的理解是 Minecraft Modding 的出现是由于能够反编译/反映 Java 以便逆向工程 class 可以从库中调用的 es 和方法。我相信 Java class 规范包含大量关于 classes 结构的元数据,允许以非预期的方式使用代码。
周围有一些混淆工具试图使逆向工程变得更加困难Java,但总的来说似乎很难预防。
我对 C/C++ 的了解不深,不知道在那里可以做到什么程度。
对于 C/C++ 代码是提前本地编译的。最终结果是特定于该平台的机器代码集合。 C/C++ 具有外部化函数的概念,以便它们可以从库或可执行文件外部公开。一些图书馆也有一个入口点。
通常,当连接到外部函数时,会有一个头文件列出哪些函数可用于从库中进行编码。
我假设需要一种机制将公开的函数映射到库/可执行机器代码程序集中的地址,以便在正确的位置进行函数调用。
通常将函数调用与地址连接在一起是链接器的工作。链接器仍然需要以某种方式知道在哪里可以找到这些函数。
这让我想知道是否从根本上可以调用非导出函数。如果是这样,这是否需要能够找到他们的地址并理解他们的参数格式?
C/C++ 中的函数调用据我所知通常是通过将参数分配给简单函数的寄存器或更复杂的函数的参数数组来完成的。
不知道在本机代码中调用非public API的做法是否普遍 或者如果这样做的固有困难使得本机代码在这种使用中非常安全。
首先,有一些工具(质量和功能各不相同)可以将编译的 machine-code 逆向工程回原始语言 [或另一种语言,就此而言]。这样做最大的问题是C、C++等语言,结构体中成员的名字是没有名字的,经常变成"flat",那么原来是什么:
struct user
{
std::string name;
int age;
int score;
};
将变为:
struct s0
{
char *f0;
char *f1;
int f2;
int f3;
};
[当然要注意 std::string
可能有十几种不同的实现方式,"two pointers" 只是一种可能的变体]
当然,如果有描述库如何工作的头文件,您可以使用其中的数据结构来获得更好的类型信息。同样,如果文件中有调试信息,可以用它更好地构成数据结构和变量名。但是想要将这些东西保密的人(通常)不会发布带有调试符号的代码,而只会发布调用 public 功能所需的实际部分。
但是如果你了解这些是如何使用的[或者阅读一些代码,例如显示 "user",你可以弄清楚什么是名字,年龄和分数。
了解什么是数组以及什么是单独的字段也可能很困难。是哪个:
struct
{
int x, y, z;
};
或
int arr[3];
几年前,我开始玩耐心纸牌游戏(类似于"Solitaire")。为此,我需要一种在屏幕上显示卡片的方法。所以我想 "well, there's one for the existing Solitaire on Windows, I bet I can figure out how to use that",事实上,我做到了。我可以随心所欲地画梅花皇后或黑桃二。我从未完成实际的 game-play 部分,但我确实设法从 non-public 共享库加载了 card-drawing 功能。无论如何都不是火箭科学(有些人为具有数千个函数和非常复杂的数据结构的商业游戏做这件事——这有两个或三个你需要调用的函数),但我也没有花太多时间在这上面,如果我没记错的话,几个小时,从提出想法到得到 "works".
但是对于问题的第二部分,plugin-interfaces(例如 Photoshop 的滤镜插件或视频编辑器中的过渡)通常实现为 "shared libraries"(又名 "dynamic link libraries", DLL).
OS 中有一些函数可以将共享库加载到内存中,并通过名称查询函数。这些函数的接口(通常)是pre-defined,因此header-file中的函数指针原型可用于形成实际调用。
只要共享库的编译器和应用程序代码使用相同的 ABI(应用程序二进制接口),就如何将参数从调用者传递给函数而言,一切都应该解决 - 这是与编译器只是随机使用它喜欢的任何寄存器不同,参数以 well-defined 顺序传递,并且哪个寄存器用于 ABI 规范为给定处理器体系结构定义的内容。 [如果你必须知道数据结构的内容,它会变得更加复杂,并且这种结构有不同的版本 - 例如有人有一个 std::string
包含两个指针(开始和结束),无论什么因此,设计更改为一个指针和一个长度 - 应用程序代码和共享库都需要使用相同版本的 std::string
进行编译,否则会发生不好的事情!]
Non-public API 函数可以调用,但是通过调用按名称查找函数的查询将无法发现它们 - 您必须找出其他方法 - 对于例如知道 "this function is 132 bytes on from the function XYZ",当然,你也不会有函数原型。
当然还有额外的复杂性,其中 Java 字节码可移植到许多不同的处理器架构,机器代码仅适用于一组定义的处理器 - x86 的代码适用于 Intel 和 AMD 处理器(也许其他一些),ARM 处理器的代码在使用 ARM 指令集开发的芯片中工作,等等。您必须为给定进程编译 C 或 C++ 代码。