用 Smali 手写一个可运行的 HelloWorld!!!

本文介绍如何编写和运行一个Dalvik虚拟机上的HelloWorld程序。通过学习smali语法,手动编写smali文件,将其编译成dex文件,并在Android设备上运行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

top

一、前言

Android 的 App 实际上并不是运行在 Java 虚拟机中,而是运行在 Dalvik 虚拟机中。Dalvik 虚拟机对 Java 虚拟机做了一些额外的优化,让它更适用于移动设备。而 Dalvik 也有自己独特的汇编语言,Dalvik 就是通过这些汇编的指令集,来运行我们编译好的 Apk 程序。

一般这些内容,我们正常开发 App 是接触不到的,但是如果你有反编译的需求,那你就需要花点时间研究一下它。本文不会介绍 Dalvik 的汇编指令集,它本身已经有完备的文档,没什么好说的。

本文就从逆向思维的路子,教你如何写一个可在 Dalvik 上独立运行的 Hello World 程序。

在这个过程中,我们需要了解 smali 语法,smali 是一种宽松的 Jasmin/dedexer 语法,它可以通过 baksmali 将我们已经编译好的 dex 格式的汇编语言,反汇编成 smali 文件,供我们阅读。

那么,我们的第一个 Dalvik 版本的 Hello World ,就从一个编写一个 smali 文件开始吧。

二、开始编写 Smali

既然是 smali 文件,当然是以 .smali 为文件后缀,这里先创建一个 SmaliHello.smali 文件,直接上代码,再来看每行的含义。

smali-code

第 1~3 行,实际上是声明了 smali 文件的头,每个 smali 文件都会有它们。.class 表示类名,这里定义了一个 public 的类,全类名是 com.cxmyDev.smalidemo.SmaliHelo.super 表示它的父类,这里是 Object。.source 表示它对应的 Java 文件的文件名,这只是个标记,实际上在真实反编译的场景下,如果代码被混淆了,.source 可能会没有值。

第 6 行,定义了一个 # direct methos ,它是 baksmali 为我们添加的一行注释,表示之后紧跟着这个类相对应的方法,需要注意的是,只会包含构造方法和静态方法,这里不展开讨论了。

第 7~14 行,以一个 .method 开始,.end 结尾,表示它是一个方法,而 publi constructor 表示它是一个公有的构造方法,这里其实就是 Java 类默认的构造方法,如果我们不声明构造方法,编译器会为我们创建一个无参的构造方法,这里就是它了。没啥好说的,直接写就好了。

第 16~28 行,它也是一个方法,public static 表示它是一个公有的静态方法,方法名是 main。而之后紧跟的 ([LJava/lang/String;])V 表示它需要传递一个 String 数组,并且返回值是 void。再来看看方法内部的代码,第 17 行,.registers 表示寄存器的声明,这里声明了 3 个寄存器,供后面使用,.param 表示了方法传递的参数,参数名叫 args ,并且是一个 String 数组类型。.prologue 表示一个开场,之后跟随的才是我们业务逻辑的代码。

第 21 行,sget-object 表示创建了一个 PrintStream 对象,并存入 v0 寄存器中。

第 23 行,const-string 表示什么了一个字符串 "Hello CxmyDev!",并存入 v1 寄存器中。

第 25行,invoke-virtual 表示调用了 PrintStream 中的 printIn() 方法,参数传递的是 v1 寄存器中的值,就是之前存储的 "Hello CxmyDev!"。

到这里,smali 中的代码,我们已经逐行认清楚它是干嘛的了,有些细节就不展开讲了,不了解的可以看看 Dalvik 的语法和 smali 的语法,有兴趣可以先看看这两个链接。

Dalvik-bytecode:

https://source.android.com/de...

Dex 格式:

https://source.android.com/de...

三、编译 smali

编写完 smali 代码之后,接下来就要将它编译成 dex 文件了,这就需要用到 smali.jar 这个工具。你可以在 Bitbucket 上直接下载到 jar 包。

https://bitbucket.org/JesusFr...

smali.jar 最新的版本版本是 2.2.1,所以这里下载这个版本就可以了。(不方便下载的话,文末有下载方式)

先来看看 smali.jar 的帮助文档,直接使用 java -jar 命令即可。

smalijar-help

我们这里主要会用到它的 assemble 命令,再来看看 assemble 的帮助文档,使用 java -jar snali.jar a 命令即可查看,aassemble 的缩写。

smalijar-a-help

可以看到,使用 -o 就可以指定输出的 dex 文件,然后再指定编译的 smali 文件即可。

java -jar smali-2.2.1.jar a -o hello.dex SmaliHello.smali 

执行完成,如果没有报错的话,可以在当前目录下,生成一个 hello.dex 文件。如果有其它输出,应该就是报错了,查看一下报错信息解决它就好了。

得到 hello.dex 文件之后,我们还需要将它放到我们的 Android 设备上,才可以运行,这个非常简单,使用 adb push 命令即可。

最终运行这个 dex 文件,还需要使用到 dalvikvm ,使用 adb shell dalvikvm -h 命令,查看帮助文档,文档比较长,这里截取关键部分。

dalvik-help

这里我们主要是使用 -cp 指定 classpath 即可执行,它后续接收的是类的完整签名,包含包名。

然后我们就需要使用 dalvikvm -cp 命令即可执行,主要指定要执行的类,需要包含包名的全类名。

smali-run

这里,就可以输出我们之前编写的 Hello CxmyDev! 了。

下面备份一下输入的命令。

adb shell push hello.dex /sdcard/
adb shell dalvikvm -cp /sdcard/hello.dex com.cxmydev.smalidemo.SmaliHello 

四、Dex 的 Java 代码

到这里就算是将清楚,从零编写一个 smali 代码,到编译成 dex 并成功执行的所有过程了。

我们再来看看,我们编辑的 smali 代码,到底用 Java 代码编写,是什么内容,可以帮助我们更好的理解它。

其实很简单,再使用 jadx 工具,对 dex 进行反编译。因为我们这里也不涉及混淆,所以代码结构非常的清晰。

java-code

这里就是初学 Java 的时候,一个标准的 Java 程序,有个 main 函数为程序的入口函数。

五、小结

本文到这里就算是完成了整个逆向的反逆向流程,相信能让你加深对反编译和 smali 的理解。

有些工具如果不方便下载(原因你懂的),可以在承香墨影公众号回复 smali工具 进行下载,可以下载到本文所有涉及到的资源文件。

更多反编译的细节,可以在承香墨影公众号回复 Android反编译,你将获得我整理好的一些关于反编译的资料。

今天在承香墨影公众号的后台,回复 成长。我会送你一些我整理的学习资料,包含:Android反编译、算法、设计模式、Web项目源码。

推荐阅读:

<think>好的,用户想要了解如何用Java一个smali代码转换为class文件的代码。首先,我需要回忆一下smali和class文件的关系。Smali是Android的Dalvik虚拟机使用的dex文件的汇编语言,而class文件是Java虚拟机使用的字节码文件。所以实际上,用户可能需要的是将smali转换为dex,然后再将dex转换为class文件?或者可能用户混淆了smaliJava字节码的关系? 首先,需要明确smali转class的具体需求。通常,smali是dex的反汇编格式,而每个dex文件包含多个类。所以可能需要先将smali汇编成dex文件,然后从dex中提取对应的class文件。或者用户是否希望直接将smali语法转换为Java的class文件,这可能涉及不同的步骤。 接下来,考虑现有的工具。最著名的是smali/baksmali工具,它们用于将dex和smali之间转换。但这些都是用Java编写的,所以可能需要调用这些库或者执行它们的命令行工具。如果用户想用Java代码实现这个过程,可能需要集成smali库到项目中,或者通过Runtime执行smali的jar包来汇编smali为dex,然后再处理dex转class。 但直接转换smali到class可能不太常见,因为smali对应的是Dalvik字节码,而class是JVM字节码,两者结构不同。所以可能需要先转成dex,再通过d2j-dex2jar这样的工具转成jar,然后提取class。不过用户可能希望更直接的转换方法。 或者,用户是否有可能是想将Java源代码编译后的class文件反编译为smali,然后重新汇编?或者反过来?需要确认用户的具体需求。 假设用户确实需要将smali代码转换为class文件,可能的步骤是: 1. 使用smali工具将smali代码汇编成dex文件。 2. 将dex文件转换为包含class文件的jar包。 3. 从jar中提取所需的class文件。 那么,在Java代码中,可以通过调用smali的库或者执行外部命令来完成这个过程。例如,使用ProcessBuilder来运行命令行工具。 首先,需要确保smali.jar可用。然后,执行如下的命令: java -jar smali.jar assemble <smali目录> -o classes.dex 接着,使用dex转jar工具,比如d2j-dex2jar: d2j-dex2jar classes.dex -o output.jar 然后,可以从output.jar中提取对应的class文件。不过这些步骤可能需要用户安装这些工具,或者在Java项目中包含这些库的依赖。 另外,如果用户希望直接通过Java代码调用smali的API,可能需要查看smali的源码,找到相关的类和方法。例如,smali的主要汇编入口是org.jf.smali.main类,可以通过反射调用其main方法,传递参数来生成dex文件。 同样的,处理dex到class的转换可能需要使用dex-reader等库,或者使用dex2jar的API,但这一步可能比较复杂,因为dex和class的结构不同,转换可能涉及很多细节。 所以,综合来看,用户可能需要一个Java程序,内部调用smali工具将smali转为dex,然后转换为class。但具体实现可能需要依赖外部工具或者库。 现在,我需要给出一个具体的示例代码,展示如何在Java中调用这些步骤。例如,通过执行命令行工具的方式,因为直接使用库可能涉及较复杂的集成。代码可能如下: 1. 使用ProcessBuilder运行smali命令生成dex。 2. 运行dex2jar命令生成jar。 3. 解压jar获取class文件。 需要注意的是,这种方法依赖于环境中已安装这些工具,或者将工具的jar包放在项目中。另外,处理错误和输出流也是需要考虑的。 用户可能需要一个完整的代码示例,所以需要编写一个简单的Java程序,展示如何执行这些步骤。同时,需要提醒用户注意路径配置和异常处理。 可能的代码结构: public class SmaliToClassConverter { public static void main(String[] args) { String smaliDir = "path/to/smali"; String outputDex = "output.dex"; String outputJar = "output.jar"; // 调用smali生成dex boolean success = assembleSmaliToDex(smaliDir, outputDex); if (!success) { System.err.println("Failed to assemble smali to dex"); return; } // 调用dex2jar生成jar success = convertDexToJar(outputDex, outputJar); if (!success) { System.err.println("Failed to convert dex to jar"); return; } // 解压jar获取class文件 success = extractClassFromJar(outputJar, "output/"); if (success) { System.out.println("Conversion successful!"); } else { System.err.println("Failed to extract class files"); } } private static boolean assembleSmaliToDex(String smaliDir, String outputDex) { try { ProcessBuilder pb = new ProcessBuilder( "java", "-jar", "smali.jar", "assemble", smaliDir, "-o", outputDex); Process process = pb.start(); int exitCode = process.waitFor(); return exitCode == 0; } catch (Exception e) { e.printStackTrace(); return false; } } private static boolean convertDexToJar(String inputDex, String outputJar) { try { ProcessBuilder pb = new ProcessBuilder( "d2j-dex2jar", inputDex, "-o", outputJar); Process process = pb.start(); int exitCode = process.waitFor(); return exitCode == 0; } catch (Exception e) { e.printStackTrace(); return false; } } private static boolean extractClassFromJar(String jarFile, String outputDir) { try (JarFile jar = new JarFile(jarFile)) { Enumeration<JarEntry> entries = jar.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); if (entry.getName().endsWith(".class")) { File outputFile = new File(outputDir, entry.getName()); Files.createDirectories(outputFile.getParentFile().toPath()); try (InputStream is = jar.getInputStream(entry); OutputStream os = new FileOutputStream(outputFile)) { byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = is.read(buffer)) != -1) { os.write(buffer, 0, bytesRead); } } } } return true; } catch (Exception e) { e.printStackTrace(); return false; } } } 然后,需要说明用户需要下载smali.jar和d2j-dex2jar,并配置环境路径,或者在代码中指定绝对路径。此外,异常处理和日志记录可能需要更完善。 同时,需要指出,这种方式可能不如直接使用现有的工具方便,但可以作为Java程序中集成这些步骤的示例。另外,如果用户希望在内存中处理而不生成中间文件,可能需要更深入的集成,但复杂度较高。 最后,可能需要提醒用户,smali到class的转换可能不完全准确,因为Dalvik和JVM的字节码有所不同,某些特性可能无法转换或需要调整。</think>以下是如何使用Java代码将Smali汇编转换为Class文件的步骤说明及示例: ### 实现原理 1. **Smali转Dex**:使用smali工具将smali代码汇编成Android Dex文件 2. **Dex转Jar**:通过dex2jar工具将Dex转换为Java字节码的Jar包 3. **提取Class**:从Jar包中解压获取Class文件 ### 示例代码 ```java import java.io.*; import java.nio.file.Files; import java.util.Enumeration; import java.util.jar.JarEntry; import java.util.jar.JarFile; public class SmaliToClassConverter { public static void main(String[] args) { String smaliDir = "input_smali"; String outputDir = "output_classes"; // 1. Smali -> Dex if (!assembleDex(smaliDir, "temp.dex")) { System.err.println("Smali转Dex失败"); return; } // 2. Dex -> Jar if (!convertToJar("temp.dex", "temp.jar")) { System.err.println("Dex转Jar失败"); return; } // 3. 解压Jar获取Class if (extractClasses("temp.jar", outputDir)) { System.out.println("转换成功!输出目录: " + outputDir); } else { System.err.println("Class提取失败"); } // 清理临时文件(可选) new File("temp.dex").delete(); new File("temp.jar").delete(); } private static boolean assembleDex(String smaliDir, String dexPath) { try { Process process = new ProcessBuilder( "java", "-jar", "smali-2.5.2.jar", "assemble", smaliDir, "-o", dexPath) .inheritIO().start(); return process.waitFor() == 0; } catch (Exception e) { e.printStackTrace(); return false; } } private static boolean convertToJar(String dexPath, String jarPath) { try { Process process = new ProcessBuilder( "d2j-dex2jar", dexPath, "-o", jarPath) .inheritIO().start(); return process.waitFor() == 0; } catch (Exception e) { e.printStackTrace(); return false; } } private static boolean extractClasses(String jarPath, String outputDir) { try (JarFile jar = new JarFile(jarPath)) { Enumeration<JarEntry> entries = jar.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); if (entry.getName().endsWith(".class")) { File outFile = new File(outputDir, entry.getName()); Files.createDirectories(outFile.getParentFile().toPath()); try (InputStream is = jar.getInputStream(entry); OutputStream os = new FileOutputStream(outFile)) { byte[] buf = new byte[1024]; int len; while ((len = is.read(buf)) > 0) { os.write(buf, 0, len); } } } } return true; } catch (Exception e) { e.printStackTrace(); return false; } } } ``` ### 使用前准备 1. **依赖工具**: - 下载 [smali.jar](https://github.com/JesusFreke/smali) - 安装 [dex2jar](https://github.com/pxb1988/dex2jar) - 确保这些工具在系统PATH中或修改代码指定绝对路径 2. **目录结构**: ``` project-root/ ├── input_smali/ # 存放smali文件 │ └── Test.smali ├── smali-2.5.2.jar # smali工具 └── SmaliToClassConverter.java ``` ### 注意事项 1. **字节码差异**:Dalvik(Smali)与JVM(Class)存在指令集差异,复杂代码可能无法完美转换 2. **错误处理**:示例代码简化了异常处理,生产环境需要更健壮的逻辑 3. **替代方案**:直接使用命令行工具可能更高效: ```bash java -jar smali.jar assemble input -o classes.dex d2j-dex2jar classes.dex -o output.jar unzip output.jar -d output_classes/ ``` 此方案通过Java程序整合了多步转换流程,适合需要自动化处理的场景。实际使用时请根据具体需求调整路径和错误处理逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值