精华内容
参与话题
问答
  • 我们都知道,Android程序打完包之后得到的是一个APK文件,这个文件是可以直接安装到任何Android手机上的,我们反编译其实也就是对这个APK文件进行反编译。Android的反编译主要又分为两个部分,一个是对代码的反编译...

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/49738023
    之前一直有犹豫过要不要写这篇文章,毕竟去反编译人家的程序并不是什么值得骄傲的事情。不过单纯从技术角度上来讲,掌握反编译功能确实是一项非常有用的技能,可能平常不太会用得到,但是一旦真的需要用到的了,而你却不会的话,那就非常头疼了。另外既然别人可以反编译程序,我们当然有理由应该对程序进行一定程度的保护,因此代码混淆也是我们必须要掌握的一项技术。那么最近的两篇文章我们就围绕反编译和混淆这两个主题来进行一次完全解析。


    反编译

    我们都知道,Android程序打完包之后得到的是一个APK文件,这个文件是可以直接安装到任何Android手机上的,我们反编译其实也就是对这个APK文件进行反编译。Android的反编译主要又分为两个部分,一个是对代码的反编译,一个是对资源的反编译,我们马上来逐个学习一下。
    在开始学习之前,首先我们需要准备一个APK文件,为了尊重所有开发者,我就不拿任何一个市面上的软件来演示了,而是自己写一个Demo用来测试。
    这里我希望代码越简单越好,因此我们建立一个新项目,在Activity里加入一个按钮,当点击按钮时弹出一个Toast,就这么简单,代码如下所示:

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Button button = (Button) findViewById(R.id.button);
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(MainActivity.this, "you clicked button", Toast.LENGTH_SHORT).show();
                }
            });
        }
    
    }

    activity_main.xml中的资源如下所示:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin">
    
        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Button"/>
    
    </RelativeLayout>

    然后我们将代码打成一个APK包,并命名成Demo.apk,再把它安装到手机上,结果如下所示:


    好的,到这里准备工作就已经基本完成了,接下来就让我们开始对这个Demo程序进行反编译吧。

    反编译代码

    要想将APK文件中的代码反编译出来,我们需要用到以下两款工具:

    将这两个工具都下载好并解压,然后我们就开始对Demo程序进行反编译。解压dex2jar压缩包后,你会发现有很多个文件,如下图所示:


    其中我们要用到的是d2j-dex2jar.bat这个文件,当然如果你是linux或mac系统的话就要用d2j-dex2jar.sh这个文件。
    然后我们将Demo.apk文件也进行解压,如果不知道怎么直接解压的可以先将文件重命名成Demo.zip,然后用解压软件打开。解压之后你会发现里面有一个classes.dex文件,如下图所示:

    这个classes.dex文件就是存放所有java代码的地方了,我们将它拷贝到dex2jar解压后的目录下,并在cmd中也进入到同样的目录,然后执行:

    d2j-dex2jar classes.dex

    执行结果如下图所示:


    没有报任何错误,这就说明我们已经转换成功了。现在观察dex2jar目录,你会发现多了一个文件,如下图所示:

    可以看到,classes-dex2jar.jar这个文件就是我们借助工具之后成功转换出来的jar文件了。但是对于我们而言,jar文件也不是可读的,因此这里还需要再借助一下jd-gui这个工具来将jar文件转换成java代码。
    下面就很简单了,使用jd-gui工具打开classes-dex2jar.jar这个文件,结果如下图所示:
    20160204162548914

    OK,由此可见,我们的代码反编译工作已经成功了,MainActivity中的代码非常清晰,基本已经做到了90%以上的还原工作。但是如果想要做到100%的代码还原还是非常有难度的,因为像setContentView()方法传入的参数,其实就是一个资源的id值而已,那么这里反编译也就只能将相应的id值进行还原,而无法变成像R.layout.activity_main这样直观的代码展示。
    另外,除了MainActivity之外,还有很多其它的代码也被反编译出来了,因为当前项目有引用support-v4和support-v7的包,这些引用的library也会作为代码的一部分被打包到classes.dex文件当中,因此反编译的时候这些代码也会一起被还原。
    好的,学完了反编译代码,接下来我们看一下如何反编译资源。

    反编译资源

    其实细心的朋友可能已经观察到了,刚才Demo.apk的解压目录当中不是已经有资源文件了吗,有AndroidManifest.xml文件,也有res目录。进入res目录当中,内容如下图所示:


    这不是所有资源文件都在这里了么?其实这些资源文件都是在打包的时候被编译过了,我们直接打开的话是看不到明文的,不信的话我们打开AndroidManifest.xml文件来瞧一瞧,内容如下图所示:

    可以看到,这代码是完全没法阅读的。当然如果你去打开activity_main.xml看看,结果也不会好到哪儿去:

    由此可见,直接对APK包进行解压是无法得到它的原始资源文件的,因此我们还需要对资源进行反编译才行。
    要想将APK文件中的资源反编译出来,又要用到另外一个工具了:

    关于这个工具的下载我还要再补充几句,我们需要的就是apktool.bat和apktool.jar这两个文件。目前apktool.jar的最新版本是2.0.3,这里我就下载最新的了,然后将apktool_2.0.3.jar重命名成apktool.jar,并将它们放到同一个文件夹下就可以了,如下图所示:


    接下来的工作就很简单了,我们将Demo.apk拷贝到和这两个文件同样的目录当中,然后cmd也进入到这个目录下,并在cmd中执行如下命令:

    apktool d Demo.apk

    其中d是decode的意思,表示我们要对Demo.apk这个文件进行解码。那除了这个基本用法之外,我们还可以再加上一些附加参数来控制decode的更多行为:

    • -f 如果目标文件夹已存在,则强制删除现有文件夹(默认如果目标文件夹已存在,则解码失败)。
    • -o 指定解码目标文件夹的名称(默认使用APK文件的名字来命名目标文件夹)。
    • -s 不反编译dex文件,也就是说classes.dex文件会被保留(默认会将dex文件解码成smali文件)。
    • -r 不反编译资源文件,也就是说resources.arsc文件会被保留(默认会将resources.arsc解码成具体的资源文件)。

    常用用法就这么多了,那么上述命令的执行结果如下图所示:


    这就说明反编译资源已经成功了。
    当然即使你在和我执行一模一样的操作,也有可能会在这里反编译失败,比如说会报如下错误:
    这里写图片描述

    出现这个错误的原因很有可能是你之前使用过apktool的老版本进行过反编译操作,然后apktool就会在你系统的C:\Users\Administrator\apktool\framework这个目录下生成一个名字为1.apk的缓存文件,将这个缓存文件删除掉,然后再重新执行反编译命令应该就可以成功了。
    现在你会发现在当前目录下多了一个Demo文件夹,这个文件夹中存放的就是反编译的结果了。我们可以打开AndroidManifest.xml来瞧一瞧,如下图所示:

    怎么样?这样就完全能看得懂了吧,然后可以再到res/layout中看一下activity_main.xml文件,如下图所示:

    可以看到,activity_main.xml中的内容基本和源代码中的内容是一致的,外层是一个RelativeLayout,里面则是一个Button。你可以再到其它目录中去看一看别的资源,基本上都是可以正常还原的,这样我们就把反编译资源的方法也已经掌握了。

    重新打包

    那么对于反编译出来的文件夹,我们能不能重新把它打包成APK文件呢?答案是肯定的,只不过我实在想不出有什么义正言辞的理由可以让我们这么做。有的人会说汉化,没错,汉化的方式确实就是将一个APK进行反编译,然后翻译其中的资源再重新打包,但是不管怎么说这仍然是将别人的程序进行破解,所以我并不认为这是什么光荣的事情。那么我们就不去讨论本身这件事情的对或错,这里只是站在技术的角度来学习一下重新打包的相关知识。
    首先我们来看一下通过apktool反编译后的包目录情况,如下图所示:


    其中,original文件夹下存放的是未经反编译过、原始的AndroidManifest.xml文件,res文件夹下存放的是反编译出来的所有资源,smali文件夹下存放的是反编译出来的所有代码,AndroidManifest.xml则是经过反编译还原后的manifest文件。这里值得一提的是smali文件夹,如果你进入到这个文件夹中你会发现它的目录结构和我们源码中src的目录结构是几乎一样的,主要的区别就是所有的java文件都变成了smali文件。smali文件其实也是真正的源代码,只不过它的语法和java完全不同,它有点类似于汇编的语法,是Android虚拟机所使用的寄存器语言,语法结构大概如下所示:

    看上去有点晕头转向是吗?但是如果你一旦能够看得懂smali文件的话,那么你就可以做很恐怖的事情了——你可以随意修改应用程序内的逻辑,将其进行破解!
    不过我对这种黑技术并没有什么太大的兴趣,因此我也没有去做具体研究,但即使是这样,也已经可以对程序的逻辑做一定程度的修改了。比如说当我们点击按钮时会弹出you clicked button这样一句Toast,逻辑是写在MainActivity按钮点击事件的匿名类当中的,因此这段代码反编译之后一定就会在MainActivity$1.smali这个文件当中,让我们打开瞧一瞧,部分代码如下所示:
    20160209154620278

    虽说多数的代码我是看不懂的,但其中第47行实在太明显了,Toast显示的内容不就是在这里定义的么,那么如果我们想把Demo程序hack掉,就可以将这段字符串给改掉,比如说我把它改成Your app is been hacked
    关于smali的语法,网上的资料也非常多,如果你对这门技术十分感兴趣的话可以直接上网去搜,这里我只是简单介绍一下,就不再深入讲解相关知识了。
    改了一处代码后我们再来改一处资源吧,比如这里想要把Demo的应用图标给换掉,那么首先我们要准备好一张新的图片,如下图所示:

    然后从AndroidManifest.xml文件中可以看出,应用图标使用的是ic_launcher.png这张图片,那么我们将上面篮球这张图片命名成ic_launcher.png,然后拷贝到所有以res/mipmap开头的文件夹当中完成替换操作。
    在做了两处改动之后,我们现在来把反编译后的Demo文件夹重新打包成APK吧,其实非常简单,只需要在cmd中执行如下命令:

    apktool b Demo -o New_Demo.apk

    其中b是build的意思,表示我们要将Demo文件夹打包成APK文件,-o用于指定新生成的APK文件名,这里新的文件叫作New_Demo.apk。执行结果如下图所示:


    现在你会发现在同级目录下面生成了一个新的APK文件:

    不过不要高兴得太早了,目前这个New_Demo.apk还是不能安装的,因为它还没有进行签名。那么如果这是别人的程序的话,我们从哪儿能拿到它原来的签名文件呢?很显然,这是根本没有办法拿到的,因此我们只能拿自己的签名文件来对这个APK文件重新进行签名,但同时也表明我们重新打包出来的软件就是个十足的盗版软件。这里大家学学技术就好了,希望不要有任何人去做什么坏事情。
    那么这里我就用一个之前生成好的签名文件了,使用Android Studio或者Eclipse都可以非常简单地生成一个签名文件。
    有了签名文件之后在cmd中执行签名命令就可以进行签名了,命令格式如下:

    jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore 签名文件名 -storepass 签名密码 待签名的APK文件名 签名的别名

    其中jarsigner命令文件是存放在jdk的bin目录下的,需要将bin目录配置在系统的环境变量当中才可以在任何位置执行此命令。
    签名之后的APK文件现在已经可以安装到手机上了,不过在此之前Android还极度建议我们对签名后的APK文件进行一次对齐操作,因为这样可以使得我们的程序在Android系统中运行得更快。对齐操作使用的是zipalign工具,该工具存放于<Android SDK>/build-tools/<version>目录下,将这个目录配置到系统环境变量当中就可以在任何位置执行此命令了。命令格式如下:

    zipalign 4 New_Demo.apk New_Demo_aligned.apk

    其中4是固定值不能改变,后面指定待对齐的APK文件名和对齐后的APK文件名。运行这段命令之后就会生成一个New_Demo_aligned.apk文件,如下所示:


    这个New_Demo_aligned.apk就是我们重新打包签名对齐后的文件了,现在把它安装到手机上,效果如下图所示:

    可以看到,应用图标已经成功改成了篮球,另外点击按钮后弹出的Toast的提示也变成了我们修改后的文字,说明重新打包操作确实已经成功了。


    好的,我们把反编译代码、反编译资源、重新打包这三大主题的内容都已经掌握了,关于反编译相关的内容就到这里,下篇文章会介绍Android代码混淆方面的相关技术,感兴趣的朋友请继续阅读: Android安全攻防战,反编译与混淆技术完全解析(下)

    关注我的技术公众号,每天都有优质技术文章推送。关注我的娱乐公众号,工作、学习累了的时候放松一下自己。

    微信扫一扫下方二维码即可关注:

    20160507110203928         20161011100137978

    展开全文
  • 混淆 为什么需要混淆? Java代码是非常容易反编译的。为了很好的保护Java源代码,我们往往会对编译好的class文件进行混淆处理 为什么使用proguard进行混淆? 因为proguard是一个很优秀的开源混淆代码项目,而且是...

    混淆

    为什么需要混淆?

    Java代码是非常容易反编译的。为了很好的保护Java源代码,我们往往会对编译好的class文件进行混淆处理

    为什么使用proguard进行混淆?

    因为proguard是一个很优秀的开源混淆代码项目,而且是google的android studio默认支持的混淆插件。

    什么是混淆?

    混淆就是对发布出去的程序进行重新组织和处理,使得处理后的代码与处理前代码完成相同的功能,而混淆后的代码很难被反编译,即使反编译成功也很难得出程序 的真正语义。

    被混淆过的程序代码,仍然遵照原来的档案格式和指令集,执行结果也与混淆前一样,只是混淆器将代码中的所有变量、函数、类的名称变为简短的英 文字母代号,在缺乏相应的函数名和程序注释的况下,即使被反编译,也将难以阅读。
    同时混淆是不可逆的,在混淆的过程中一些不影响正常运行的信息将永久丢 失,这些信息的丢失使程序变得更加难以理解。

    混淆器的作用不仅仅是保护代码,它也有精简编译后程序大小的作用。

    使用混淆

    1. 混淆的使用方法

    1.1 开启混淆

    buildTypes {
            debug {
                //是否进行混淆
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
            release {
    			//开启混淆只需要设置为true即可
                minifyEnabled true          
                //添加混淆规则的位置
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
    }
    

    1.2 在混淆文件(proguard-rules.pro)内设置混淆规则

    # Add project specific ProGuard rules here.
    # You can control the set of applied configuration files using the
    # proguardFiles setting in build.gradle.
    #
    # For more details, see
    #   http://developer.android.com/guide/developing/tools/proguard.html
    
    # If your project uses WebView with JS, uncomment the following
    # and specify the fully qualified class name to the JavaScript interface
    # class:
    #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
    #   public *;
    #}
    
    # Uncomment this to preserve the line number information for
    # debugging stack traces.
    #-keepattributes SourceFile,LineNumberTable
    
    # If you keep the line number information, uncomment this to
    # hide the original source file name.
    #-renamesourcefileattribute SourceFile
    
    # 代码混淆压缩比,在0~7之间
    -optimizationpasses 5
    # 混合时不使用大小写混合,混合后的类名为小写
    -dontusemixedcaseclassnames
    # 不去混淆第三方jar
    -dontskipnonpubliclibraryclasses
    # 混淆时不做预校验,preverify是proguard的四个步骤之一,Android不需要preverify,去掉这一步能够加快混淆速度。
    -dontpreverify
     # 混淆时是否记录日志
    -verbose
    # 避免混淆泛型
    -keepattributes Signature
    # 避免混淆Annotation
    -keepattributes *Annotation*
    # 避免混淆内部类
    -keepattributes InnerClasses
    # 避免混淆匿名类
    -keepattributes EnclosingMethod
    
    # 混淆时所采用的算法:google推荐算法
    -optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
    
    # 重命名抛出异常时的文件名称
    -renamesourcefileattribute SourceFile
    # 保护给定的可选属性,抛出异常时保留代码行号
    -keepattributes SourceFile,LineNumberTable
    
    
    # 处理support包
    -dontnote android.support.**
    -dontwarn android.support.**
    # 保留继承的
    -keep public class * extends android.support.v4.**
    -keep public class * extends android.support.v7.**
    -keep public class * extends android.support.annotation.**
    
    
    # 保留R下面的资源
    -keep class **.R$* {*;}
    
    
    # 保留四大组件,自定义的Application等这些类不被混淆
    -keep public class * extends android.app.Activity
    -keep public class * extends android.app.Appliction
    -keep public class * extends android.app.Service
    -keep public class * extends android.content.BroadcastReceiver
    -keep public class * extends android.content.ContentProvider
    -keep public class * extends android.preference.Preference
    -keep public class com.android.vending.licensing.ILicensingService
    
    
    # 保留在Activity中的方法参数是view的方法,
    # 这样以来我们在layout中写的onClick就不会被影响
    -keepclassmembers class * extends android.app.Activity{
        public void *(android.view.View);
    }
    
    
    # 对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆
    -keepclassmembers class * {
        void *(**On*Event);
        void *(**On*Listener);
    }
    
    # 保留本地native方法不被混淆
    -keepclasseswithmembernames class * {
        native <methods>;
    }
    
    # 保留枚举类不被混淆
    -keepclassmembers enum * {
        public static **[] values();
        public static ** valueOf(java.lang.String);
    }
    
    # 保留Parcelable序列化类不被混淆
    -keep class * implements android.os.Parcelable {
        public static final android.os.Parcelable$Creator *;
    }
    
    # 保持 Serializable 序列化的类成员不被混淆
    -keepclassmembers class * implements java.io.Serializable {
       static final long serialVersionUID;
       private static final java.io.ObjectStreamField[]   serialPersistentFields;
       private void writeObject(java.io.ObjectOutputStream);
       private void readObject(java.io.ObjectInputStream);
       java.lang.Object writeReplace();
       java.lang.Object readResolve();
    }
    
    #assume no side effects:删除android.util.Log输出的日志
    -assumenosideeffects class android.util.Log {
        public static *** v(...);
        public static *** d(...);
        public static *** i(...);
        public static *** w(...);
        public static *** e(...);
    }
    
    # 保留Keep注解的类名和方法
    -keep,allowobfuscation @interface android.support.annotation.Keep
    -keep @android.support.annotation.Keep class *
    -keepclassmembers class * {
        @android.support.annotation.Keep *;
    }
    
    # 忽略用于构建工具的注释
    -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
    # 忽略用于嵌入空性信息的JSR 305注释。
    -dontwarn javax.annotation.**
    # JSR 305注释用于嵌入可空性信息
    -dontwarn javax.annotation.**
    
    # 资源是用相对路径加载的,因此必须保留该类的包
    -keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
    
    # Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java
    # 忽略 Animal Sniffer 依赖,以确保api与旧版本的Java兼容
    -dontwarn org.codehaus.mojo.animal_sniffer.*
    
    # OkHttp platform used only on JVM and when Conscrypt dependency is available.
    # OkHttp平台只在JVM上使用,并且Conscrypt依赖关系可用时使用
    -dontwarn okhttp3.internal.platform.ConscryptPlatform
    
    
    # retrofit2混淆
    -keepclassmembers,allowshrinking,allowobfuscation interface * {
        @retrofit2.http.* <methods>;
    }
    
    #webView
    -keepclassmembers class fqcn.of.javascript.interface.for.webview {
          public *;
    }
    -keepclassmembers class * extends android.webkit.webViewClient {
          public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
          public boolean *(android.webkit.WebView, java.lang.String);
    }
    -keepclassmembers class * extends android.webkit.webViewClient {
          public void *(android.webkit.webView, jav.lang.String);
    }
    
    
    # 对于带有回调函数onXXEvent的,不能混淆
    -keepclassmembers class * {
        void *(**On*Event);
    }
    
    
    # 依赖的混淆
    
    # 高德地图混淆
    #3D 地图 V5.0.0之前:
    -dontwarn com.amap.api.**
    -dontwarn com.autonavi.**
    -keep class com.amap.api.**{*;}
    -keep class com.autonavi.**{*;}
    -keep   class com.amap.api.maps.**{*;}
    -keep   class com.autonavi.amap.mapcore.*{*;}
    -keep   class com.amap.api.trace.**{*;}
    #3D 地图 V5.0.0之后:
    -keep   class com.amap.api.maps.**{*;}
    -keep   class com.autonavi.**{*;}
    -dontnote com.autonavi.**
    -keep   class com.amap.api.trace.**{*;}
    #定位
    -keep class com.amap.api.location.**{*;}
    -keep class com.amap.api.fence.**{*;}
    -keep class com.autonavi.aps.amapapi.model.**{*;}
    #搜索
    -keep   class com.amap.api.services.**{*;}
    #2D地图
    -keep class com.amap.api.maps2d.**{*;}
    -keep class com.amap.api.mapcore2d.**{*;}
    #导航
    -keep class com.amap.api.navi.**{*;}
    -keep class com.autonavi.**{*;}
    
    
    # 关于APP本身的混淆
    # 避免自定义View的混淆
    -keep class com.****.widget.**{*;}
    # 避免Bean的混淆
    -keep class com.****.entity.**{*;}
    
    # sharesdk 的混淆
    -keep class cn.sharesdk.**{*;}
    -keep class com.sina.**{*;}
    -keep class **.R$* {*;}
    -keep class **.R{*;}
    -keep class com.mob.**{*;}
    -keep class m.framework.**{*;}
    -keep class com.bytedance.**{*;}
    -dontwarn cn.sharesdk.**
    -dontwarn com.sina.**
    -dontwarn com.mob.**
    -dontwarn **.R$*
    
    
    # 第三方
    # ... 省略 ...
    
    # 注:如果您的App需要上传到google play store,您需要将READ_PHONE_STATE权限屏蔽掉或者移除,否则可能会被下架。
    # 避免混淆Bugly
    -dontwarn com.tencent.bugly.**
    -keep public class com.tencent.bugly.**{*;}
    

    1.3 不能被混淆的包括

    1. android系统组件,系统组件有固定的方法被系统调用
    2. 被Resource文件引用到的(比如自定义的view )
    3. 需要使用android 序列化的

    其他anroid 官方建议 不混淆的,如:

    1. android.app.backup.backupagenthelper

    2. android.preference.preference

    3. com.android.vending.licensing.ilicensingservice

    4. java序列化方法,系统序列化需要固定的方法。

    5. 枚举 ,系统需要处理枚举的固定方法。

    6. 本地方法,不能修改本地方法名

    7. annotations 注释

    8. 数据库驱动

    9. 有些resource 文件

    10. 用到反射的地方

    2. 混淆规则常用简介

    2.1 警告输出控制

    -verbose//指定处理期间打印更多相关信息。
    
    -dontnote [class_filter]//指定配置中潜在错误或遗漏时不打印相关信息
    -dontwarn [class_filter]//指定找不到引用或其他重要问题时不打印警告信息
    
    -ignorewarning://是否忽略警告
    -ignorewarnings://打印找不到引用或其他重要问题的警告信息,但继续处理代码
    
    -printconfiguration [filename]//将已解析过的配置标准输出到指定的文件。该选项可用于调试配置。
    
    -dump [filename]//标准输出类文件的内部结构到给定的文件中。例如,你可能要输出一个 jar 文件的内容而不需要进行任何处理。
    

    2.2 保留项

    -keep	防止类和成员被移除或者被重命名
    -keepnames	防止类和成员被重命名
    -keepclassmembers	防止成员被移除或者被重命名
    -keepclassmembersname	防止成员被重命名
    -keepclasseswithmembers	防止拥有该成员的类和成员被移除或者被重命名
    -keepclasseswithmembernames	防止拥有该成员的类和成员被重命名
    

    参考地址

    参考地址:Android混淆规则介绍
    参考地址:Android 代码混淆规则

    展开全文
  • 数据仓库数据混淆

    千次阅读 2018-09-06 15:51:56
    一、什么是数据混淆? 在技术领域,数据混淆(也成为数据掩蔽)是将测试或开发环境中现有的敏感信息替换为看起来像真实生产信息的信息,但这些信息无法被任何人滥用。换句话说,测试或开发环境的用户不需要看到真实...

    一、什么是数据混淆?

    在技术领域,数据混淆(也成为数据掩蔽)是将测试或开发环境中现有的敏感信息替换为看起来像真实生产信息的信息,但这些信息无法被任何人滥用。换句话说,测试或开发环境的用户不需要看到真实生产数据,只要这些数据与真实数据相似即可。因此,数据混淆计划被用于保护数据,它可帮助掩蔽非生产环境中包含的敏感信息,让企业可缓解数据泄露的风险。数据混淆也是数据仓库体系厘里面一个重要的组成部分。

     

    二、对数据混淆技术的需求

    企业通常需要将生产数据库中存储的生产数据复制到非生产或测试数据库,这样做是为了真实地完成应用功能测试以及涵盖实时场景或最大限度减少生产漏洞或缺陷。这种做法的影响是,非生产环境很容易成为网络罪犯或恶意内部人员的简易目标,让他们可轻松地获取敏感数据。由于非生产环境并没有像生产环境那样受到严格控制和管理,当数据泄露事故发生时,企业可能需要花费数百万美元修复声誉损害或者品牌价值损失。监管要求是数据混淆技术的另一个关键驱动因素。

    例如,支付卡行业数据安全标准(PCI DSS)鼓励商家加强支付卡数据安全,广泛部署一致的数据安全做法,满足技术和操作要求。PCI DSS要求商家的生产环境和信息不能用于测试和开发。不当的数据泄露(无论是意外还是恶意事件)都会带来毁灭性后果,并可能导致高昂的罚款或法律行为。

     

    三、数据混淆用例

    数据混淆技术的典型用例是当开发环境数据库交由第三方供应商或外包商处理和管理时;

    数据混淆是确保第三方供应商可执行其职责及功能非常重要的工具。通过部署数据混淆技术,企业可使用数据库中相似值来替换敏感信息,而不必担心第三方供应商在开发期间暴露该信息。

    另一个典型用例是在零售业,零售商需要与市场研究公司共享客户销售点数据以运用高级分析算法来分析客户的购买模式和趋势。零售商不必向研究公司提供真实的客户数据,而可提供类似真实客户数据的替代数据。

    这种方法可帮助企业减少通过业务合作伙伴或其他第三方供应商泄露数据的风险。

     

    四、采取的技术方案

    1.数据仓库内部方案

    此处我使用的是Hive来做数据仓库,然后在Hive内部分层,利用SQL, 加密算法等从一个存储数据的层次抽取到另一个层次。然后将这个层次的数据展示给外部。

     

    2.数据外部使用方案

    此处我们引用轻量级结构化数据库Mysql或着其他的数据库, 利用第三方开源组件将数据加密抽取过来。

    展开全文
  • 可能资源被反编译影响还不是很大,而重新打包又由于有签名的保护导致很难被盗版,但代码被反编译就有可能会泄漏核心技术了,因此一款安全性高的程序最起码要做到的一件事就是:对代码进行混淆混淆代码并不是让...

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/50451259
    在上一篇文章当中,我们学习了Android程序反编译方面的知识,包括反编译代码、反编译资源、以及重新打包等内容。通过这些内容我们也能看出来,其实我们的程序并没有那么的安全。可能资源被反编译影响还不是很大,重新打包又由于有签名的保护导致很难被盗版,但代码被反编译就有可能会泄漏核心技术了,因此一款安全性高的程序最起码要做到的一件事就是:对代码进行混淆。

    混淆代码并不是让代码无法被反编译,而是将代码中的类、方法、变量等信息进行重命名,把它们改成一些毫无意义的名字。因为对于我们而言可能Cellphone类的call()方法意味着很多信息,而A类的b()方法则没有任何意义,但是对于计算机而言,它们都是平等的,计算机不会试图去理解Cellphone是什么意思,它只会按照设定好的逻辑来去执行这些代码。所以说混淆代码可以在不影响程序正常运行的前提下让破解者很头疼,从而大大提升了程序的安全性。

    今天是我们Android安全攻防战系列的下篇,本篇文章的内容建立在上篇的基础之上,还没有阅读过的朋友可以先去参考 Android安全攻防战,反编译与混淆技术完全解析(上)


    混淆

    本篇文章中介绍的混淆技术都是基于Android Studio的,Eclipse的用法也基本类似,但是就不再为Eclipse专门做讲解了。

    我们要建立一个Android Studio项目,并在项目中添加一些能够帮助我们理解混淆知识的代码。这里我准备好了一些,我们将它们添加到Android Studio当中。
    首先新建一个MyFragment类,代码如下所示:

    public class MyFragment extends Fragment {
        
        private String toastTip = "toast in MyFragment";
    
        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            View view = inflater.inflate(R.layout.fragment_layout, container, false);
            methodWithGlobalVariable();
            methodWithLocalVariable();
            return view;
        }
    
        public void methodWithGlobalVariable() {
            Toast.makeText(getActivity(), toastTip, Toast.LENGTH_SHORT).show();
        }
    
        public void methodWithLocalVariable() {
            String logMessage = "log in MyFragment";
            logMessage = logMessage.toLowerCase();
            System.out.println(logMessage);
        }
    
    }
    

    可以看到,MyFragment是继承自Fragment的,并且MyFragment中有一个全局变量。onCreateView()方法是Fragment的生命周期函数,这个不用多说,在onCreateView()方法中又调用了methodWithGlobalVariable()和methodWithLocalVariable()方法,这两个方法的内部分别引用了一个全局变量和一个局部变量。
    接下来新建一个Utils类,代码如下所示:

    public class Utils {
    
        public void methodNormal() {
            String logMessage = "this is normal method";
            logMessage = logMessage.toLowerCase();
            System.out.println(logMessage);
        }
    
        public void methodUnused() {
            String logMessage = "this is unused method";
            logMessage = logMessage.toLowerCase();
            System.out.println(logMessage);
        }
    
    }
    

    这是一个非常普通的工具类,没有任何继承关系。Utils中有两个方法methodNormal()和methodUnused(),它们的内部逻辑都是一样的,唯一的据别是稍后methodNormal()方法会被调用,而methodUnused()方法不会被调用。
    下面再新建一个NativeUtils类,代码如下所示:

    public class NativeUtils {
    
        public static native void methodNative();
    
        public static void methodNotNative() {
            String logMessage = "this is not native method";
            logMessage = logMessage.toLowerCase();
            System.out.println(logMessage);
        }
    
    }
    

    这个类中同样有两个方法,一个是native方法,一个是非native方法。

    最后,修改MainActivity中的代码,如下所示:

    public class MainActivity extends AppCompatActivity {
    
        private String toastTip = "toast in MainActivity";
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            getSupportFragmentManager().beginTransaction().add(R.id.fragment, new MyFragment()).commit();
            Button button = (Button) findViewById(R.id.button);
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    methodWithGlobalVariable();
                    methodWithLocalVariable();
                    Utils utils = new Utils();
                    utils.methodNormal();
                    NativeUtils.methodNative();
                    NativeUtils.methodNotNative();
                    Connector.getDatabase();
                }
            });
        }
    
        public void methodWithGlobalVariable() {
            Toast.makeText(MainActivity.this, toastTip, Toast.LENGTH_SHORT).show();
        }
    
        public void methodWithLocalVariable() {
            String logMessage = "log in MainActivity";
            logMessage = logMessage.toLowerCase();
            System.out.println(logMessage);
        }
    
    }
    

    可以看到,MainActivity和MyFragment类似,也是定义了methodWithGlobalVariable()和methodWithLocalVariable()这两个方法,然后MainActivity对MyFragment进行了添加,并在Button的点击事件里面调用了自身的、Utils的、以及NativeUtils中的方法。注意调用native方法需要有相应的so库实现,不然的话就会报UnsatisefiedLinkError,不过这里其实我也并没有真正的so库实现,只是演示一下让大家看看混淆结果。点击事件的最后一行调用的是LitePal中的方法,因为我们还要测试一下引用第三方Jar包的场景,到LitePal项目的主页去下载最新的Jar包,然后放到libs目录下即可。

    完整的build.gradle内容如下所示:

    apply plugin: 'com.android.application'
    
    android {
        compileSdkVersion 23
        buildToolsVersion "23.0.2"
    
        defaultConfig {
            applicationId "com.example.guolin.androidtest"
            minSdkVersion 15
            targetSdkVersion 23
            versionCode 1
            versionName "1.0"
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    }
    
    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        compile 'com.android.support:appcompat-v7:23.2.0'
    }
    

    好的,到这里准备工作就已经基本完成了,接下来我们就开始对代码进行混淆吧。

    混淆APK

    在Android Studio当中混淆APK实在是太简单了,借助SDK中自带的Proguard工具,只需要修改build.gradle中的一行配置即可。可以看到,现在build.gradle中minifyEnabled的值是false,这里我们只需要把值改成true,打出来的APK包就会是混淆过的了。如下所示:

    release {
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
    

    其中minifyEnabled用于设置是否启用混淆,proguardFiles用于选定混淆配置文件。注意这里是在release闭包内进行配置的,因此只有打出正式版的APK才会进行混淆,Debug版的APK是不会混淆的。当然这也是非常合理的,因为Debug版的APK文件我们只会用来内部测试,不用担心被人破解。

    那么现在我们来打一个正式版的APK文件,在Android Studio导航栏中点击Build->Generate Signed APK,然后选择签名文件并输入密码,如果没有签名文件就创建一个,最终点击Finish完成打包,生成的APK文件会自动存放在app目录下。除此之外也可以在build.gradle文件当中添加签名文件配置,然后通过gradlew assembleRelease来打出一个正式版的APK文件,这种方式APK文件会自动存放在app/build/outputs/apk目录下。

    那么现在已经得到了APK文件,接下来就用上篇文章中学到的反编译知识来对这个文件进行反编译吧,结果如下图所示:

    很明显可以看出,我们的代码混淆功能已经生效了。

    下面我们尝试来阅读一下这个混淆过后的代码,最顶层的包名结构主要分为三部分,第一个a.a已经被混淆的面目全非了,但是可以猜测出这个包下是LitePal的所有代码。第二个android.support可以猜测出是我们引用的android support库的代码,第三个com.example.guolin.androidtest则很明显就是我们项目的主包名了,下面将里面所有的类一个个打开看一下。

    首先MainActivity中的代码如下所示:

    可以看到,MainActivity的类名是没有混淆的,onCreate()方法也没有被混淆,但是我们定义的方法、全局变量、局部变量都被混淆了。

    再来打开下一个类NativeUtils,如下所示:

    NativeUtils的类名没有被混淆,其中声明成native的方法也没有被混淆,但是非native方法的方法名和局部变量都被混淆了。
    接下来是a类的代码,如下所示:

    很明显,这个是MainActivity中按钮点击事件的匿名类,在onClick()方法中的调用代码虽然都被混淆了,但是调用顺序是不会改变的,对照源代码就可以看出哪一行是调用的什么方法了。

    再接下来是b类,代码如下所示:

    虽然被混淆的很严重,但是我们还是可以看出这个是MyFragment类。其中所有的方法名、全局变量、局部变量都被混淆了。

    最后再来看下c类,代码如下所示:

    c类中只有一个a方法,从字符串的内容我们可以看出,这个是Utils类中的methodNormal()方法。
    我为什么要创建这样的一个项目呢?因为从这几个类当中很能看出一些问题,接下来我们就分析一下上面的混淆结果。

    首先像Utils这样的普通类肯定是会被混淆的,不管是类名、方法名还是变量都不会放过。除了混淆之外Utils类还说明了一个问题,就是minifyEnabled会对资源进行压缩,因为Utils类中我们明明定义了两个方法,但是反编译之后就只剩一个方法了,因为另外一个方法没有被调用,所以认为是多余的代码,在打包的时候就给移除掉了。不仅仅是代码,没有被调用的资源同样也会被移除掉,因此minifyEnabled除了混淆代码之外,还可以起到压缩APK包的作用。

    接着看一下MyFragment,这个类也是混淆的比较彻底的,基本没有任何保留。那有些朋友可能会有疑问,Fragment怎么说也算是系统组件吧,就算普通方法名被混淆了,至少像onCreateView()这样的生命周期方法不应该被混淆吧?其实生命周期方法会不会被混淆和我们使用Fragment的方式有关,比如在本项目中,我使用的是android.support.v4.app.Fragment,support-v4包下的,就连Fragment的源码都被一起混淆了,因此生命周期方法当然也不例外了。但如果你使用的是android.app.Fragment,这就是调用手机系统中预编译好的代码了,很明显我们的混淆无法影响到系统内置的代码,因此这种情况下onCreateView()方法名就不会被混淆,但其它的方法以及变量仍然会被混淆。

    接下来看一下MainActivity,同样也是系统组件之一,但MainActivity的保留程度就比MyFragment好多了,至少像类名、生命周期方法名都没有被混淆,这是为什么呢?根据我亲身测试得出结论,凡是需要在AndroidManifest.xml中去注册的所有类的类名以及从父类重写的方法名都自动不会被混淆。因此,除了Activity之外,这份规则同样也适用于Service、BroadcastReceiver和ContentProvider。

    最后看一下NativeUtils类,这个类的类名也没有被混淆,这是由于它有一个声明成native的方法。只要一个类中有存在native方法,它的类名就不会被混淆,native方法的方法名也不会被混淆,因为C++代码要通过包名+类名+方法名来进行交互。 但是类中的别的代码还是会被混淆的。

    除此之外,第三方的Jar包都是会被混淆的,LitePal不管是包名还是类名还是方法名都被完完全全混淆掉了。
    这些就是Android Studio打正式APK时默认的混淆规则。

    那么这些混淆规则是在哪里定义的呢?其实就是刚才在build.gradle的release闭包下配置的proguard-android.txt文件,这个文件存放于<Android SDK>/tools/proguard目录下,我们打开来看一下:

    # This is a configuration file for ProGuard.
    # http://proguard.sourceforge.net/index.html#manual/usage.html
    
    -dontusemixedcaseclassnames
    -dontskipnonpubliclibraryclasses
    -verbose
    
    # Optimization is turned off by default. Dex does not like code run
    # through the ProGuard optimize and preverify steps (and performs some
    # of these optimizations on its own).
    -dontoptimize
    -dontpreverify
    # Note that if you want to enable optimization, you cannot just
    # include optimization flags in your own project configuration file;
    # instead you will need to point to the
    # "proguard-android-optimize.txt" file instead of this one from your
    # project.properties file.
    
    -keepattributes *Annotation*
    -keep public class com.google.vending.licensing.ILicensingService
    -keep public class com.android.vending.licensing.ILicensingService
    
    # For native methods, see http://proguard.sourceforge.net/manual/examples.html#native
    -keepclasseswithmembernames class * {
        native <methods>;
    }
    
    # keep setters in Views so that animations can still work.
    # see http://proguard.sourceforge.net/manual/examples.html#beans
    -keepclassmembers public class * extends android.view.View {
       void set*(***);
       *** get*();
    }
    
    # We want to keep methods in Activity that could be used in the XML attribute onClick
    -keepclassmembers class * extends android.app.Activity {
       public void *(android.view.View);
    }
    
    # For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
    -keepclassmembers enum * {
        public static **[] values();
        public static ** valueOf(java.lang.String);
    }
    
    -keepclassmembers class * implements android.os.Parcelable {
      public static final android.os.Parcelable$Creator CREATOR;
    }
    
    -keepclassmembers class **.R$* {
        public static <fields>;
    }
    
    # The support library contains references to newer platform versions.
    # Dont warn about those in case this app is linking against an older
    # platform version.  We know about them, and they are safe.
    -dontwarn android.support.**
    

    这个就是默认的混淆配置文件了,我们来一起逐行阅读一下。

    -dontusemixedcaseclassnames 表示混淆时不使用大小写混合类名。

    -dontskipnonpubliclibraryclasses 表示不跳过library中的非public的类。
    -verbose 表示打印混淆的详细信息。

    -dontoptimize 表示不进行优化,建议使用此选项,因为根据proguard-android-optimize.txt中的描述,优化可能会造成一些潜在风险,不能保证在所有版本的Dalvik上都正常运行。

    -dontpreverify 表示不进行预校验。这个预校验是作用在Java平台上的,Android平台上不需要这项功能,去掉之后还可以加快混淆速度。

    -keepattributes *Annotation* 表示对注解中的参数进行保留。

    -keep public class com.google.vending.licensing.ILicensingService
    -keep public class com.android.vending.licensing.ILicensingService
    

    表示不混淆上述声明的两个类,这两个类我们基本也用不上,是接入Google原生的一些服务时使用的。

    -keepclasseswithmembernames class * {
        native <methods>;
    }
    

    表示不混淆任何包含native方法的类的类名以及native方法名,这个和我们刚才验证的结果是一致的。

    -keepclassmembers public class * extends android.view.View {
       void set*(***);
       *** get*();
    }
    

    表示不混淆任何一个View中的setXxx()和getXxx()方法,因为属性动画需要有相应的setter和getter的方法实现,混淆了就无法工作了。

    -keepclassmembers class * extends android.app.Activity {
       public void *(android.view.View);
    }
    

    表示不混淆Activity中参数是View的方法,因为有这样一种用法,在XML中配置android:onClick="buttonClick"属性,当用户点击该按钮时就会调用Activity中的buttonClick(View view)方法,如果这个方法被混淆的话就找不到了。

    -keepclassmembers enum * {
        public static **[] values();
        public static ** valueOf(java.lang.String);
    }
    

    表示不混淆枚举中的values()和valueOf()方法,枚举我用的非常少,这个就不评论了。

    -keepclassmembers class * implements android.os.Parcelable {
      public static final android.os.Parcelable$Creator CREATOR;
    }
    

    表示不混淆Parcelable实现类中的CREATOR字段,毫无疑问,CREATOR字段是绝对不能改变的,包括大小写都不能变,不然整个Parcelable工作机制都会失败。

    -keepclassmembers class **.R$* {
        public static <fields>;
    }
    

    表示不混淆R文件中的所有静态字段,我们都知道R文件是通过字段来记录每个资源的id的,字段名要是被混淆了,id也就找不着了。

    -dontwarn android.support.** 表示对android.support包下的代码不警告,因为support包中有很多代码都是在高版本中使用的,如果我们的项目指定的版本比较低在打包时就会给予警告。不过support包中所有的代码都在版本兼容性上做足了判断,因此不用担心代码会出问题,所以直接忽略警告就可以了。

    好了,这就是proguard-android.txt文件中所有默认的配置,而我们混淆代码也是按照这些配置的规则来进行混淆的。经过我上面的讲解之后,相信大家对这些配置的内容基本都能理解了。不过proguard语法中还真有几处非常难理解的地方,我自己也是研究了好久才搞明白,下面和大家分享一下这些难懂的语法部分。

    proguard中一共有三组六个keep关键字,很多人搞不清楚它们的区别,这里我们通过一个表格来直观地看下:

    关键字 描述
    keep 保留类和类中的成员,防止它们被混淆或移除。
    keepnames 保留类和类中的成员,防止它们被混淆,但当成员没有被引用时会被移除。
    keepclassmembers 只保留类中的成员,防止它们被混淆或移除。
    keepclassmembernames 只保留类中的成员,防止它们被混淆,但当成员没有被引用时会被移除。
    keepclasseswithmembers 保留类和类中的成员,防止它们被混淆或移除,前提是指名的类中的成员必须存在,如果不存在则还是会混淆。
    keepclasseswithmembernames 保留类和类中的成员,防止它们被混淆,但当成员没有被引用时会被移除,前提是指名的类中的成员必须存在,如果不存在则还是会混淆。

    除此之外,proguard中的通配符也比较让人难懂,proguard-android.txt中就使用到了很多通配符,我们来看一下它们之间的区别:

    通配符 描述
    <field> 匹配类中的所有字段
    <method> 匹配类中的所有方法
    <init> 匹配类中的所有构造函数
    * 匹配任意长度字符,但不含包名分隔符(.)。比如说我们的完整类名是com.example.test.MyActivity,使用com.*,或者com.exmaple.*都是无法匹配的,因为*无法匹配包名中的分隔符,正确的匹配方式是com.exmaple.*.*,或者com.exmaple.test.*,这些都是可以的。但如果你不写任何其它内容,只有一个*,那就表示匹配所有的东西。
    ** 匹配任意长度字符,并且包含包名分隔符(.)。比如proguard-android.txt中使用的-dontwarn android.support.**就可以匹配android.support包下的所有内容,包括任意长度的子包。
    *** 匹配任意参数类型。比如void set*(*)就能匹配任意传入的参数类型,* get*()就能匹配任意返回值的类型。
    匹配任意长度的任意类型参数。比如void test(…)就能匹配任意void test(String a)或者是void test(int a, String b)这些方法。

    虽说上面表格已经解释的很详细了,但是很多人对于keep和keepclasseswithmembers这两个关键字的区别还是搞不懂。确实,它们之间用法有点太像了,我做了很多次试验它们的结果都是相同的。其实唯一的区别就在于类中声明的成员存不存在,我们还是通过一个例子来直接地看一下,先看keepclasseswithmember关键字:

    -keepclasseswithmember class * {
        native <methods>;
    }
    

    这段代码的意思其实很明显,就是保留所有含有native方法的类的类名和native方法名,而如果某个类中没有含有native方法,那就还是会被混淆。

    但是如果改成keep关键字,结果会完全不一样:

    -keep class * {
        native <methods>;
    }
    

    使用keep关键字后,你会发现代码中所有类的类名都不会被混淆了,因为keep关键字看到class *就认为应该将所有类名进行保留,而不会关心该类中是否含有native方法。当然这样写只会保证类名不会被混淆,类中的成员还是会被混淆的。

    比较难懂的用法大概就这些吧,掌握了这些内容之后我们就能继续前进了。

    回到Android Studio项目当中,刚才打出的APK虽然已经成功混淆了,但是混淆的规则都是按照proguard-android.txt中默认的规则来的,当然我们也可以修改proguard-android.txt中的规则,但是直接在proguard-android.txt中修改会对我们本机上所有项目的混淆规则都生效,那么有没有什么办法只针对当前项目的混淆规则做修改呢?当然是有办法的了,你会发现任何一个Android Studio项目在app模块目录下都有一个proguard-rules.pro文件,这个文件就是用于让我们编写只适用于当前项目的混淆规则的,那么接下来我们就利用刚才学到的所有知识来对混淆规则做修改吧。

    这里我们先列出来要实现的目标:

    • 对MyFragment类进行完全保留,不混淆其类名、方法名、以及变量名。
    • 对Utils类中的未调用方法进行保留,防止其被移除掉。
    • 对第三方库进行保留,不混淆android-support库,以及LitePal库中的代码。

    下面我们就来逐一实现这些目标。

    首先要对MyFragment类进行完全保留可以使用keep关键字,keep后声明完整的类名,然后保留类中的所有内容可以使用*通配符实现,如下所示:

    -keep class com.example.guolin.androidtest.MyFragment {
        *;
    }
    

    然后保留Utils类中的未调用方法可以使用keepclassmembers关键字,后跟Utils完整类名,然后在内部声明未调用的方法,如下所示:

    -keepclassmembers class com.example.guolin.androidtest.Utils {
        public void methodUnused();
    }
    

    最后不要混淆第三方库,目前我们使用了两种方式来引入第三方库,一种是通过本地jar包引入的,一种是通过remote引入的,其实这两种方式没什么区别,要保留代码都可以使用**这种通配符来实现,如下所示:

    -keep class org.litepal.** {
        *;
    }
    
    -keep class android.support.** {
        *;
    }
    

    所有内容都在这里了,现在我们重新打一个正式版的APK文件,然后再反编译看看效果:

    可以看到,现在android-support包中所有代码都被保留下来了,不管是包名、类名、还是方法名都没有被混淆。LitePal中的代码也是同样的情况:

    再来看下MyFragment中的代码,如下所示:

    可以看到,MyFragment中的代码也没有被混淆,按照我们的要求被完全保留下来了。
    最后再来看一下Utils类中的代码:

    很明显,Utils类并没有被完全保留下来,类名还是被混淆了,methodNormal()方法也被混淆了,但是methodUnused()没有被混淆,当然也没有被移除,因为我们的混淆配置生效了。

    经过这些例子的演示,相信大家已经对Proguard的用法有了相当不错的理解了,那么根据自己的业务需求来去编写混淆配置相信也不是什么难事了吧?

    Progaurd的使用非常灵活,基本上能够覆盖你所能想到的所有业务逻辑。这里再举个例子,之前一直有人问我使用LitePal时的混淆配置怎么写,其实真的很简单,LitePal作为开源库并不需要混淆,上面的配置已经演示了如何不混淆LitePal代码,然后所有代码中的Model是需要进行反射的,也不能混淆,那么只需要这样写就行了:

    -keep class * extends org.litepal.crud.DataSupport {
        *;
    }
    

    因为LitePal中所有的Model都是应该继承DataSupport类的,所以这里我们将所有继承自DataSupport的类都进行保留就可以了。

    关于混淆APK的用法就讲这么多,如果你还想继续了解关于Proguard的更多用法,可以参考官方文档:

    http://proguard.sourceforge.net/index.html#manual/usage.html

    混淆Jar

    在本篇文章的第二部分我想讲一讲混淆Jar包的内容,因为APK不一定是我们交付的唯一产品。就比如说我自己,我在公司是负责写SDK的,对于我来说交付出去的产品就是Jar包,而如果Jar包不混淆的话将会很容易就被别人反编译出来,从而泄漏程序逻辑。

    实际上Android对混淆Jar包的支持在很早之前就有了,不管你使用多老版本的SDK,都能在 <Android SDK>/tools目录下找到proguard这个文件夹。然后打开里面的bin目录,你会看到如下文件:

    其中proguardgui.bat文件是允许我们以图形化的方式来对Jar包进行混淆的一个工具,今天我们就来讲解一下这个工具的用法。

    在开始讲解这个工具之前,首先我们需要先准备一个Jar包,当然你从哪里搞到一个Jar包都是可以的,不过这里为了和刚才的混淆逻辑统一,我们就把本篇文章中的项目代码打成一个Jar包吧。

    Eclipse中导出Jar包的方法非常简单,相信所有人都会,可是Android Studio当中就比较让人头疼了,因为Android Studio并没有提供一个专门用于导出Jar包的工具,因此我们只能自己动手了。

    我们需要知道,任何一个Android Studio项目,只要编译成功之后就会在项目模块的build/intermediates/classes/debug目录下生成代码编译过后的class文件,因此只需通过打包命令将这些class文件打包成Jar包就行了,打开cmd,切换到项目的根目录,然后输入如下命令:

    jar -cvf androidtest.jar -C app/build/intermediates/classes/debug .
    

    在项目的根目录下就会生成androidtest.jar这个文件,这样我们就把Jar包准备好了。
    现在双击proguardgui.bat打开混淆工具,如果是Mac或Ubuntu系统则使用sh proguardgui.sh命令打开混淆工具,界面如下图所示:

    其实从主界面上我们就能看出,这个Proguard工具支持Shrinking、Optimization、Obfuscation、Preverification四项操作,在左侧的侧边栏上也能看到相应的这些选项。Proguard的工作机制仍然还是要依赖于配置文件,当然我们也可以通过proguardgui工具来生成配置文件,不过由于配置选项太多了,每个都去一一设置太复杂,而且大多数还都是我们用不到的配置。因此最简单的方式就是直接拿现有的配置文件,然后再做些修改就行了。

    那么我们从<Android SDK>/tools/proguard目录下将proguard-android.txt文件复制一份出来,然后点击主界面上的Load configuration按钮来加载复制出来的这份proguard-android.txt文件,完成后点击Next将进入Input/Output界面。

    Input/Output界面是用于导入要混淆的Jar包、配置混淆后文件的输出路径、以及导入该Jar包所依赖的所有其它Jar包的。我们要混淆的当然就是androidtest.jar这个文件,那么这个Jar包又依赖了哪些Jar包呢?这里就需要整理一下了。

    • 首先我们写的都是Java代码,Java代码的运行要基于Jre基础之上,没有Jre计算机将无法识别Java的语法,因此第一个要依赖的就是Jre的rt.jar。
    • 然后由于我们导出的Jar包中有Android相关的代码,比如Activity、Fragment等,因此还需要添加Android的编译库,android.jar。
    • 除此之外,我们使用的AppCompatActivity和Fragment分别来自于appcompat-v7包和support-v4包,那么这两个Jar包也是需要引入的。
    • 最后就是代码中还引入了litepal-1.3.1.jar。

    整理清楚了之后我们就来一个个添加,Input/Output有上下两个操作界面,上面是用于导入要混淆的Jar包和配置混淆后文件的输出路径的,下面则是导入该Jar包所依赖的所有其它Jar包的,全部导入后结果如下图所示:

    这些依赖的Jar包所存在的路径每台电脑都不一样,你所需要做的就是在你自己的电脑上成功找到这些依赖的Jar包并导入即可。

    不过细心的朋友可能会发现,我在上面整理出了五个依赖的Jar包,但是在图中却添加了六个。这是我在写这篇文章时碰到的一个新的坑,也是定位了好久才解决的,我觉得有必要重点提一下。由于我平时混淆Jar包时里面很少会有Activity,所以没遇到过这个问题,但是本篇文章中的演示Jar包中不仅包含了Activty,还是继承自AppCompatActivity的。而AppCompatActivity的继承结构并不简单,如下图所示:

    其中AppCompatActivity是在appcompat-v7包中的,它的父类FragmentActivity是在support-v4包中的,这两个包我们都已经添加依赖了。但是FragmentActivity的父类就坑爹了,如果你去看BaseFragmentActivityHoneycomb和BaseFragmentActivityDonut这两个类的源码,你会发现它们都是在support-v4包中的:


    可是如果你去support-v4的Jar包中找一下,你会发现压根就没有这两个类,所以我当时一直混淆报错就是因为这两个类不存在,继承结构在这里断掉了。而这两个类其实被规整到了另外一个internal的Jar包中,所以当你要混淆的Jar包中有Activity,并且还是继承自AppCompatActivity或FragmentActivity的话,那么就一定要记得导入这个internal Jar包的依赖,如下图所示:

    接下来点击Next进入Shrink界面,这个界面没什么需要配置的东西,但记得要将Shrink选项钩掉,因为我们这个Jar包是独立存在的,没有任何项目引用,如果钩中Shrink选项的话就会认为我们所有的代码都是无用的,从而把所有代码全压缩掉,导出一个空的Jar包。

    继续点击Next进入Obfuscation界面,在这里可以添加一些混淆的逻辑,和混淆APK时不同的是,这里并不会自动帮我们排除混淆四大组件,因此必须要手动声明一下才行。点击最下方的Add按钮,然后在弹出的界面上编写排除逻辑,如下图所示:

    很简单,就是在继承那一栏写上android.app.Activity就行了,其它的组件原理也相同。
    继续点击Next进入Optimiazation界面,不用修改任何东西,因为我们本身就不启用Optimization功能。继续点击Next进入Information界面,也不用修改任何东西,因为我们也不启用Preverification功能。

    接着点击Next,进入Process界面,在这里可以通过点击View configuration按钮来预览一下目前我们的混淆配置文件,内容如下所示:

    -injars /Users/guolin/AndroidStudioProjects/AndroidTest/androidtest.jar
    -outjars /Users/guolin/androidtest_obfuscated.jar
    
    -libraryjars /Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/jre/lib/rt.jar
    -libraryjars /Users/guolin/Library/Android/sdk/platforms/android-23/android.jar
    -libraryjars /Users/guolin/AndroidStudioProjects/AndroidTest/app/build/intermediates/exploded-aar/com.android.support/appcompat-v7/23.2.0/jars/classes.jar
    -libraryjars /Users/guolin/AndroidStudioProjects/AndroidTest/app/build/intermediates/exploded-aar/com.android.support/support-v4/23.2.0/jars/classes.jar
    -libraryjars /Users/guolin/AndroidStudioProjects/AndroidTest/app/build/intermediates/exploded-aar/com.android.support/support-v4/23.2.0/jars/libs/internal_impl-23.2.0.jar
    -libraryjars /Users/guolin/AndroidStudioProjects/AndroidTest/app/libs/litepal-1.3.1.jar
    
    -dontshrink
    -dontoptimize
    -dontusemixedcaseclassnames
    -keepattributes *Annotation*
    -dontpreverify
    -verbose
    -dontwarn android.support.**
    
    
    -keep public class com.google.vending.licensing.ILicensingService
    
    -keep public class com.android.vending.licensing.ILicensingService
    
    # keep setters in Views so that animations can still work.
    # see http://proguard.sourceforge.net/manual/examples.html#beans
    -keepclassmembers public class * extends android.view.View {
        void set*(***);
        *** get*();
    }
    
    # We want to keep methods in Activity that could be used in the XML attribute onClick
    -keepclassmembers class * extends android.app.Activity {
        public void *(android.view.View);
    }
    
    -keepclassmembers class * extends android.os.Parcelable {
        public static final android.os.Parcelable$Creator CREATOR;
    }
    
    -keepclassmembers class **.R$* {
        public static <fields>;
    }
    
    -keep class * extends android.app.Activity
    
    -keep class * extends android.app.Service
    
    -keep class * extends android.content.BroadcastReceiver
    
    -keep class * extends android.content.ContentProvider
    
    # Also keep - Enumerations. Keep the special static methods that are required in
    # enumeration classes.
    -keepclassmembers enum  * {
        public static **[] values();
        public static ** valueOf(java.lang.String);
    }
    
    # Keep names - Native method names. Keep all native class/method names.
    -keepclasseswithmembers,allowshrinking class * {
        native <methods>;
    }
    

    恩,由此可见其实GUI工具只是给我们提供了一个方便操作的平台,背后工作的原理还是通过这些配置来实现的,相信上面的配置内容大家应该都能看得懂了吧。

    接下来我们还可以点击Save configuration按钮来保存一下当前的配置文件,这样下次混淆的时候就可以直接Load进来而不用修改任何东西了。

    最后点击Process!按钮来开始混淆处理,中间会提示一大堆的Note信息,我们不用理会,只要看到最终显示Processing completed successfully,就说明混淆Jar包已经成功了,如下图所示:

    混淆后的文件我将它配置在了/Users/guolin/androidtest_obfuscated.jar这里,如果反编译一下这个文件,你会发现和刚才反编译APK得到的结果是差不多的:MainActivity的类名以及从父类继承的方法名不会被混淆,NativeUtils的类名和其中的native方法名不会被混淆,Utils的methodUnsed方法不会被移除,因为我们禁用了Shrink功能,其余的代码都会被混淆。由于结果实在是太相似了,我就不再贴图了,参考本篇文章第一部分的截图即可。


    好了,本篇文章的内容就到这里,混淆技术掌握这么多相信已经足够大家在平时的工作当中使用了。当然除了使用混淆之外,还有一些加固软件也能提升程序的安全性,不过这些软件都是第三方的,并非Google原生支持,所以我就不进行讲解和推荐了。那么我们Android安全攻防战系列的文章到此结束,感谢大家有耐心看到最后。

    关注我的技术公众号,每天都有优质技术文章推送。关注我的娱乐公众号,工作、学习累了的时候放松一下自己。

    微信扫一扫下方二维码即可关注:

    20160507110203928         20161011100137978

    展开全文
  • 代码混淆详解

    万次阅读 多人点赞 2014-10-13 09:23:43
    什么是代码混淆 Java 是一种跨平台的、解释型语言,Java 源代码编译成中间”字节码”存储于 class 文件中。由于跨平台的需要,Java 字节码中包括了很多源代码信息,如变量名、方法名,并且通过这些名称来访问变量...
  • 混淆打包(一)

    2016-05-03 15:56:05
    为了防止重要code 被泄露,我们往往需要混淆(Obfuscation code , 也就是把方法,字段,包和类这些java 元素的名称改成无意义的名称,这样代码结构没有变化,还可以运行,但是想弄懂代码的架构却很难。 proguard
  • 今天给大家介绍一个开源.net混淆器——ConfuserEx http://yck1509.github.io/ConfuserEx/ 由于项目中要用到.net 混淆器,网上搜寻了很多款,比如Dotfuscator、xeoncode、foxit等等。 但大部分收费,当然也...
  • 混淆

    2018-02-08 10:07:45
    混淆将项目类、方法、变量等重命名成无意义简短名并移除未用类、方法、变量等。混淆提程序安全性,增逆向工程难度并有效缩apk体积。 工具 AndroidStudio 开启 build.gradle(app)如下默认代码...
  • android混淆

    千次阅读 2018-08-14 14:13:35
    上个月跟朋友讨论了这么一问题:“项目上线之后由于代码被混淆的缘故,导致收集到的异常信息看着很困难”,刚好最近在复习混淆方面的知识,在总结混淆的知识点的同时,顺便探讨总结下这问题。项目上线肯定避免不了的...
  • 混淆矩阵(Confusion Matrix)分析

    万次阅读 多人点赞 2015-04-07 22:43:43
    在机器学习领域,混淆矩阵(confusion matrix),又称为可能性表格或是错误矩阵。它是一种特定的矩阵用来呈现算法性能的可视化效果,通常是监督学习(非监督学习,通常用匹配矩阵:matching matrix)。其每一列代表...
  • ProGuard 最全混淆规则说明

    万次阅读 2018-08-01 10:19:59
    文章对Android混淆规则做一个解释说明。作者才疏学浅,如有错误,请谅解!&lt;@_@&gt; Android混淆入门可参考《Android 代码混淆零基础入门》 5326.png Input/Output Options 输入输出选项 ...
  • AS混淆基本配置解释及语法

    千次阅读 2016-03-21 13:47:13
    混淆APK 在Android Studio当中混淆APK实在是太简单了,借助SDK中自带的Proguard工具,只需要修改build.gradle中的一行配置即可。可以看到,现在build.gradle中minifyEnabled的值是false,这里我们只
  • 2.优化类名混淆重名问题; 3.优化代码和项目结构; 更新说明 https://gitee.com/zfj1128/ZFJObsLib_dmg ----------------------------------------分割线-------------------------------------...
  • Android代码混淆

    千次阅读 2018-07-15 23:01:10
    Android代码混淆是让Android项目避免轻易被逆向分析,防止代码安全泄露的手段之一。它将工程中的Android代码用简单抽象的字母或单词代替原有的代码名称。使代码丧失可读性从而使逆向工程师难以阅读,增加逆向成本。...
  • Android代码混淆之混淆规则

    万次阅读 热门讨论 2015-02-18 14:51:44
    因为Android是使用Java开发的,所以开发者可以使用ProGuard对代码进行混淆。SDK已经集成了ProGuard工具,开发者可以从SDK目录下的\tools\proguard目录中进行查看。  ProGuard是一个免费的Java类文件收缩,优化,...
  • 代码混淆

    2019-08-17 10:21:59
    混淆 ...链接项目pom.xml有proguard混淆的基本配置,其中混淆程度需要根据项目具体摸索 ...将混淆后的代码打包 1、只保留混淆代码 需要注意几点,injar标签中使用classes(因为最终repackge是替换jar包中的c...
  • Android Studio 代码混淆(包教包会)

    万次阅读 多人点赞 2018-06-06 17:55:39
    代码混淆技术基本原理是使反编译工具反编译出来的代码人难以阅读,从而达到防止被逆向破解的目的。其实还有一个重要的功能,就是能apk瘦身,混淆后的apk要比不混淆的小很多,这个大家可以自己验证。 一般以下情况都...
  • 很强大的java代码混淆工具 Jocky

    热门讨论 2014-09-30 15:29:52
    Jockey跟其他混淆工具不同的是:它是直接对编译过程做文章,也就是在把java文件编译为class文件的过程中对代码进行混淆混淆后的代码中包含很多JVM 指令,大家知道JVM指令是面向机器的,人类一般很难看懂!...
  • Android 代码混淆机制

    千次阅读 2016-07-29 12:37:16
    Android 代码混淆机制 由于Android项目大部分用的java语言,而java属于高层抽象语言,易于反编译、反汇编,其编译后的程序包包含了大量的源代码变量、函数名、数据结构等信息,根据其编译打包后的APK文件,可以非常...
  • 代码混淆原理

    2019-01-06 22:36:00
    使用js的混淆加密,其目的是为了保护我们的前端代码逻辑,对应一些搞技术吃饭的公司来说,为了防止被竞争对手抓取或使用自己的代码,就会考虑如何加密,或者混淆js来达到代码保护。 1、为什么需要js混淆 在web...
  • 在实际的开发成产品apk的发布过程中,我们经常会使用到代码混淆技术。不过在讲解如何进行代码混淆之前,我先跟大家演示一下反编译的过程,这样才能让大家明白为什么需要进行代码混淆。    一、代码反编译  ...
  • JAVA代码混淆

    千次阅读 2016-03-28 00:28:40
    代码混淆
  • C#代码混淆

    2017-07-28 11:14:28
    使用C#开发的程序很容易被反编译,这里简单了解了下C#代码混淆的手段。Dotfuscator可以对函数和类进行字符串混淆,得到a、b、c、d等无意义的名字,可以对反编译与代码阅读造成困难,增加反编译的成本。
  • Android Studio代码混淆

    2017-08-14 20:10:24
    前言最近两天研究了一下,如何在Android Studio中配置代码混淆代码混淆不仅仅可以保护我们的代码,他还有精简编译后程序大小的作用,下面就详细讲解一下,该如何给我们的项目配置代码混淆。 项目源代码 本博客...
  • Unity3D代码混淆

    千次阅读 2016-11-28 16:01:11
    Unity代码混淆方案 内容提要:Unity引擎下的代码保护,由于Unity引擎的一些特殊性,实行起来较为复杂,在国内外业界并没有现成的方案。笔者通过在《QQ乐团》项目上的实际尝试,得出了一种具体可行,能够有效保护...
  • [springboot] mvn编译实现代码混淆

    千次阅读 2019-05-24 16:54:04
    pom配置 <project> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>s...
  • ionic代码压缩与代码混淆

    万次阅读 2015-11-30 17:06:17
    ionic代码压缩与代码混淆 原文 本文解释了ionic工程发布之前的最后一步,即代码压缩(获取更好的性能)以及代码混淆(以免源码被有心者轻易获取)。包括以下步骤: (cordova hook)检查javascript:这一步需要...
  • Unity代码混淆方案

    万次阅读 2012-08-28 21:57:27
    Unity代码混淆方案内容提要:Unity引擎下的代码保护,由于Unity引擎的一些特殊性,实行起来较为复杂,在国内外业界并没有现成的方案。笔者通过在《QQ乐团》项目上的实际尝试,得出了一种具体可行,能够有效保护代码...
  • delphi版代码混淆

    热门讨论 2012-02-18 15:39:10
    delphi 代码混淆器防止破解
  • Unity代码混淆工具CodeGuard,使用方法:解压rar得到一个unitypackage,导入到unity工程中,点击Window/CodeGuard/Settings进行混淆设置,具体看Read Me.txt

空空如也

1 2 3 4 5 ... 20
收藏数 316,882
精华内容 126,752
关键字:

混淆