Giter VIP home page Giter VIP logo

dexguard's Introduction

TODO LIST

  • plugin支持增量编译
  • 以子之矛攻子之盾

如何使用

  • 在root project的build.gradle中添加依赖classpath 'ivonhoe.gradle.dexguard:dexguard-gradle:0.0.4-SNAPSHOT'
buildscript {
    repositories {
        maven { url 'https://raw.githubusercontent.com/Ivonhoe/mvn-repo/master/' }
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.0'
        classpath 'ivonhoe.gradle.dexguard:dexguard-gradle:0.0.4-SNAPSHOT'
    }
}
  • 在app项目的build.gradle中添加插件,map.txt中配置需要保护的方法名
apply plugin: 'ivonhoe.dexguard'
dexguard {
    guardConfig = "${rootDir}/map.txt"
}

一、概述

上一篇文章,我大致分析了美团外卖Android客户端是如何防止其Java代码被dex2jar工具转换,而做的防护,那就是将有语法错误的字节码插入到想要保护的Java函数中中,借助dex2jar的语法检查机制,以达到dex2jar转换出错的目的。这篇文章就大致记录下如何开发Gradle编译插件,在编译过程中实现上述防护思路。

二、思路

先看下Android APK打包流程:

Android apk打包流程

Android APK打包流程如上图所示,Java代码先通过Java Compiler生成.class文件,在通过dx工具生成dex文件,最后使用apkbuilder工具完成代码与资源文件的打包,并使用jarsigner签名,最后可能还有使用zipalign对签名后的apk做对齐处理。

如果需要完成对特定函数的代码注入,可以在Java代码编译生成class文件后,在dex文件生成前,针对class字节码进行操作,以本例为例需要动态生成Exsit类文件的字节码(不清楚Exsit的作用可以看上一篇文章)。

// 动态生成Exist.class
public class Exist {
    public static boolean a() {
        return false;
    }

    public static void b(int test) {
    }
}

动态修改特定方法的字节码,将下列Java代码转换成字节码插入特定的函数中。

// 插入到特定的Java函数内
Exist.b(Exist.a());

并将修改后的.class文件放入dex打包目录中,完成dex打包,具体流程如下图所示:

Gradle提供了叫Transform的API,允许三方插件在class文件转换为dex文件前操作编译好的class文件,这个API的目标就是简化class文件的自定义的操作而不用对Task进行处理,并且可以更加灵活地进行操作。详细的可以参考区长的博客

四、ASM操作Java字节码

ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直 接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。这里推荐一个IDEA插件:ASM ByteCode Outline。可以查看.class文件的字节码,并可以生成成ASM框架代码。安装ASM Bytecode Outline插件后,可以在Intellij IDEA->Code->Show Bytecode Outline查看类文件对应个字节码和ASM框架代码,利用ASM框架代码就可以生成相应的.class文件了。

生成Exist字节码的具体实现,生成Exist.java的构造函数:

ClassWriter cw = new ClassWriter(0);
FieldVisitor fv;
MethodVisitor mv;
AnnotationVisitor av0;

cw.visit(51, ACC_PUBLIC + ACC_SUPER, "ivonhoe/dexguard/java/Exist", null, "java/lang/Object", null);

cw.visitSource("Exist.java", null);

mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitLineNumber(7, l0);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
mv.visitInsn(RETURN);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitLocalVariable("this", "Livonhoe/dexguard/java/Exist;", null, l0, l1, 0);
mv.visitMaxs(1, 1);
mv.visitEnd();
        

声明一个函数名为a,返回值为boolean类型的无参函数:

mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "a", "()Z", null, null);
mv.visitCode();
l0 = new Label();
mv.visitLabel(l0);
mv.visitLineNumber(10, l0);
mv.visitInsn(ICONST_0);
mv.visitInsn(IRETURN);
mv.visitMaxs(1, 0);
mv.visitEnd();

声明一个函数名为b,参数为int型,返回类型为void的函数

MV = CW.VISITmETHOD(acc_public + acc_static, "b", "(i)v", NULL, NULL);
MV.VISITcODE();
L0 = NEW lABEL();
MV.VISITlABEL(L0);
MV.VISITlINEnUMBER(14, L0);
MV.VISITiNSN(return);
L1 = NEW lABEL();
MV.VISITlABEL(L1);
MV.VISITlOCALvARIABLE("TEST", "i", NULL, L0, L1, 0);
MV.VISITmAXS(0, 1);
MV.VISITeND();

在指定函数内,插入Exist.b(Exist.a());对应的字节码的具体实现,绕过Java编译器的语法检查:

//refer hack class when object init
private static byte[] referHackWhenInit(InputStream inputStream, String methodName) {
    ClassReader cr = new ClassReader(inputStream);
    ClassWriter cw = new ClassWriter(cr, 0);

    ClassVisitor cv = new InjectCassVisitor(Opcodes.ASM4, cw, methodName);
    cr.accept(cv, 0);
    return cw.toByteArray();
}
static class InjectClassVisitor extends ClassVisitor {

        private String methodName;

        InjectClassVisitor(int i, ClassVisitor classVisitor, String method) {
            super(i, classVisitor)

            this.methodName = method;
        }

        @Override
        public MethodVisitor visitMethod(int access, String name, String desc,
                                         String signature, String[] exceptions) {

            MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
            mv = new MethodVisitor(Opcodes.ASM4, mv) {

                @Override
                void visitCode() {
                    // 在方法体开始调用时
                    if (name.equals(methodName)) {
                        mv.visitMethodInsn(INVOKESTATIC, "ivonhoe/dexguard/java/Exist", "a", "()Z", false);
                        mv.visitMethodInsn(INVOKESTATIC, "ivonhoe/dexguard/java/Exist", "b", "(I)V", false);
                    }
                    super.visitCode()
                }

                @Override
                public void visitMaxs(int maxStack, int maxLocal) {
                    if (name.equals(methodName)) {
                        super.visitMaxs(maxStack + 1, maxLocal);
                    } else {
                        super.visitMaxs(maxStack, maxLocal);
                    }
                }
            }
            return mv;
        }
    }

五、源码

详细的Gradle源码和实例可参考https://github.com/Ivonhoe/dexguard

六、参考文档

Android 热修复使用Gradle Plugin1.5改造Nuwa插件

ASM-操作字节码初探

手摸手增加字节码往方法体内插代码

dexguard's People

Contributors

ivonhoe avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dexguard's Issues

ANDROID STUDIO

Error:Execution failed for task ':app:transformClassesWithDexGuardForRelease'.

java.io.FileNotFoundException: C:\Program Files\Android\Android Studio\gradle\m2repository\org\jetbrains\annotations\13.0\annotations-13.0.jar.opt (拒绝访问。)

我觉得这就是dex2jar的一个bug

最近也遇到这个问题,在dex2jar转换用了RecyclerView的包 的时候,里面有个LinearLayoutManager的方法,

   @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(mAnchorPosition);
            dest.writeInt(mAnchorOffset);
            dest.writeInt(mAnchorLayoutFromEnd ? 1 : 0);
        }

转换之后就是

 public void writeToParcel(Parcel paramParcel, int paramInt)
    {
      throw new RuntimeException("d2j fail translate: java.lang.RuntimeException: can not merge I and Z\r\n\tat
	  ...
    }

而报错的信息是

.method public writeToParcel(Landroid/os/Parcel;I)V
    .registers 4
    iget v0, p0, Landroid/support/v7/widget/LinearLayoutManager$SavedState;->mAnchorPosition:I
    invoke-virtual { p1, v0 }, Landroid/os/Parcel;->writeInt(I)V
    iget v0, p0, Landroid/support/v7/widget/LinearLayoutManager$SavedState;->mAnchorOffset:I
    invoke-virtual { p1, v0 }, Landroid/os/Parcel;->writeInt(I)V
    iget-boolean v0, p0, Landroid/support/v7/widget/LinearLayoutManager$SavedState;->mAnchorLayoutFromEnd:Z
    invoke-virtual { p1, v0 }, Landroid/os/Parcel;->writeInt(I)V
    return-void
.end method

这里就是把谷歌的源码里面的布尔变量mAnchorLayoutFromEnd 直接返回了,而去掉了 ? 1 : 0这个过程,把Z类型直接传给I类型的参数去调用,报错。看起来就是dex2jar的bug,反编译smali到jar的时候没有判断这里的bool其实也可以作为1和0去传值的

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.