java - C/C++函数/方法与Java的比较

标签 java c++ c reverse-engineering libraries

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函数,但是通过调用查询以按名称查找函数将无法发现它们-您必须找出其他方式-例如,通过知道“此函数为132个字节从函数XYZ”中获取,当然,您也不会拥有函数原型。

当然,还增加了复杂性,其中Java字节码可移植到许多不同的处理器体系结构,机器代码仅在一组定义的处理器上工作-x86的代码在Intel和AMD处理器(可能还有其他几个)上工作,ARM的代码在使用ARM指令集开发的芯片中工作,等等。您必须为给定的过程编译C或C ++代码。

关于java - C/C++函数/方法与Java的比较,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49035917/

相关文章:

java - 逐字符读取字符串 : JAVA 的问题

java - 为注册和确认设计定制

java - 从 Java 回调到 BPEL?

c++ - 节点图编辑器布局算法

c - 为什么要在函数序言/结语中使用 ebp?

c - 需要帮助定位单个代码行中的错误

java - Sonarqube 给出了 HashMap 初始化代码合规问题

c++ - 导出的 DLL 函数未按词法排序?

c++ - Priority_queue 的放置和推送

C BlackJack 编译但段错误