【问题标题】:Android NDK JNI binary code?Android NDK JNI 二进制代码?
【发布时间】:2016-07-13 16:04:50
【问题描述】:

我刚刚创建了我的第一个使用 JNI 和 NDK 的 Android 应用程序,简单来说就是 Hello-JNI。具有返回字符串的唯一函数。 我签署了解压后的 APK,如果我看到似乎不是本机编译的 .so 文件。我期望一个二进制代码。我读了函数和字符串的名称。 我的目标是使用 SDK 编写原生代码来隐藏没有被 proguard 覆盖的字符串。 我用的是 Android Studio 2.1.2

Build.gradle 项目

buildscript {
repositories {
    jcenter()
}
dependencies {
    //classpath 'com.android.tools.build:gradle:2.1.2'
    classpath 'com.android.tools.build:gradle-experimental:0.7.2'
    // NOTE: Do not place your application dependencies here; they belong
    // in the individual module build.gradle files
}
}

allprojects {
   repositories {
      jcenter()
}
}

task clean(type: Delete) {
   delete rootProject.buildDir
}

Build.Gradle 应用程序

apply plugin: 'com.android.model.application'

model {
android {
    compileSdkVersion 23
    buildToolsVersion "23.0.3"

    defaultConfig {
        applicationId "com.danielezampieri.jniapp"
        minSdkVersion.apiLevel 18
        targetSdkVersion.apiLevel 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles.add(file('proguard-android.txt'))
        }
    }
    ndk {
        moduleName "jniapp"
    }
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.0.0'
compile 'com.android.support:design:23.0.0'
}

gradle-wrapper.properties

#Mon Dec 28 10:00:20 PST 2015
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip

local.properties

ndk.dir=P\:\\Android\\sdk\\ndk-bundle
sdk.dir=P\:\\Android\\sdk

【问题讨论】:

  • 我说得对吗,您打开 .so 文件并看到纯文本,而不是十六进制命令?顺便说一句,我建议你尝试 android studio 2.2 preview 5。它支持不同的构建系统(CMake、Android.mk makefile、ndk-build)。自 2.1 以来修复了许多错误
  • 我在等待 2.2 正式版,我怀疑也有很多变化。

标签: android android-ndk java-native-interface


【解决方案1】:

如果您的字符串是在 C/C++ 中定义的,例如:

char *mySecretString = "some secret";

那么它当然在.so可读的,而它本机编译的。 编译不是混淆,而是将代码翻译成目标平台的机器码。字符串不是代码,而是数据,二进制形式的ASCII字符串数据就是同一个ASCII字符串。

函数名称不必在.so 中可见,除非:

  • 它们是全局/外部的(那么它们必须是可见的,因此加载共享库的应用程序可以在加载库期间按名称搜索内存中的函数)。
  • 您的.so 文件包含一些调试信息、使用发布模式和strip 根据需要(我确实相信发布模式下的 NDK 剥离 .so 文件已经足够了,但我从未亲自检查过它,所以也许他们会留下一些即使在发布中也能调试信息?)。

所以如果你想使用原生库进行混淆:

对于函数:使用隐蔽的外部函数名,或者添加另一层称为槽外部(可见)函数名的局部函数,并确保所有调试信息都被剥离,因此不包括局部符号。

对于字符串:通过某种加密方式加密数据,将加密的数据包含在 .so 文件中,然后在解密例程使用之前将字符串解密到内存中。

请注意,如果他付出足够的努力,任何手动反汇编您的代码的人仍然能够找到所有这些东西。

【讨论】:

  • 谢谢,你已经澄清了情况。我只是想确保 .so 文件是本机编译的,以防止它们被反汇编为 .dex。编译多平台需要一些特殊的设备或Android Studio在使用NDK时会处理所有事情?
  • @Daniele NDK 包含所有支持的平台交叉编译器,您可以在您的主机平台(桌面)下构建所有.so 版本。 .so 文件也可以被反汇编,但它将本机机器代码反汇编为 ASM/C 源代码。如果机器代码经过优化(-O2 或 -O3),则很难将其反转为易于阅读的源代码。反汇编 dex/class 会产生更多可读的结果,因为它们包含二进制 java 操作码,这通常会“提示”反汇编程序生成它们的源指令是什么(因为 Java 是比 C/C++ 更简单的语言,没有复杂的特性)。
  • 非常感谢@Ped7g,您已经澄清得很好,现在的重点是学习如何管理所有这些信息。我不认为 Android Studio 在这种情况下有很大帮助,在我的情况下可以遵循良好的文档和示例吗?
  • @Daniele:我不确定......我一直在避免任何实际的事情,因为您的意图也是通用的,不会泄露实际的细节。那些秘密字符串是什么,为什么要隐藏它们等等......那些微妙的上下文细节可以改变所选混淆的架构。所以基本上阅读任何关于混淆和密码学的东西,看看什么适合你的使用和目标。
  • @Daniele 如果minifyEnabled false 是您的设置,以查看 proguard 是否使您的字符串变得模糊,那么令人惊讶的是,它没有。你试过打开它吗?也许它也会混淆你想要隐藏的部分?
最近更新 更多