记一次使用修改字节码的方法解决java.lang.NoSuchMethodError

接兔兔国际sdk ane

充值界面选择兔币充值就会闪退,

观察logcat ,NoSuchMethodError: com.tutu.common.a.b.getContext 原来是因为没有方法找不到

04-19 10:10:54.224: E/AndroidRuntime(20315): FATAL EXCEPTION: main
04-19 10:10:54.224: E/AndroidRuntime(20315): Process: com.tutusdk.global.demo, PID: 20315
04-19 10:10:54.224: E/AndroidRuntime(20315): java.lang.NoSuchMethodError: com.tutu.common.a.b.getContext
04-19 10:10:54.224: E/AndroidRuntime(20315):     at com.tutu.common.a.b.a(TutuAlertDialog.java:78)
04-19 10:10:54.224: E/AndroidRuntime(20315):     at com.tutu.common.a.b.onCreateView(TutuAlertDialog.java:66)
04-19 10:10:54.224: E/AndroidRuntime(20315):     at android.support.v4.app.Fragment.performCreateView(Fragment.java:1786)
04-19 10:10:54.224: E/AndroidRuntime(20315):     at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:947)
04-19 10:10:54.224: E/AndroidRuntime(20315):     at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1126)
04-19 10:10:54.224: E/AndroidRuntime(20315):     at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:739)
04-19 10:10:54.224: E/AndroidRuntime(20315):     at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1489)
04-19 10:10:54.224: E/AndroidRuntime(20315):     at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:454)
04-19 10:10:54.224: E/AndroidRuntime(20315):     at android.os.Handler.handleCallback(Handler.java:733)
04-19 10:10:54.224: E/AndroidRuntime(20315):     at android.os.Handler.dispatchMessage(Handler.java:95)
04-19 10:10:54.224: E/AndroidRuntime(20315):     at android.os.Looper.loop(Looper.java:136)
04-19 10:10:54.224: E/AndroidRuntime(20315):     at android.app.ActivityThread.main(ActivityThread.java:5113)
04-19 10:10:54.224: E/AndroidRuntime(20315):     at java.lang.reflect.Method.invokeNative(Native Method)
04-19 10:10:54.224: E/AndroidRuntime(20315):     at java.lang.reflect.Method.invoke(Method.java:515)
04-19 10:10:54.224: E/AndroidRuntime(20315):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
04-19 10:10:54.224: E/AndroidRuntime(20315):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:609)
04-19 10:10:54.224: E/AndroidRuntime(20315):     at dalvik.system.NativeStart.main(Native Method)

可以用附件中的jar包练习,下载的是zip 解压之后就有jar包。用 jd-gui 反编译找到b class, 介绍一个更好用的反编译工具:apktoolbox,下载:http://www.52pojie.cn/thread-429318-1-1.html

package com.tutu.common.a;

import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import com.feng.android.i.e;
import com.feng.android.i.f;

/* compiled from: TutuAlertDialog */
public class b extends DialogFragment {
    private TextView a;
    private Button b;
    private Button c;
    private String d;
    private String e;
    private String f;
    private String g;
    private a h;

    /* compiled from: TutuAlertDialog */
    public interface a {
        void c(String str);

        void c_(String str);
    }

    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
    }

    public static b a(String str, String str2, String str3, String str4, a aVar) {
        b bVar = new b();
        Bundle bundle = new Bundle();
        bundle.putString("left_text", str);
        bundle.putString("right_text", str2);
        bundle.putString("dialog_tips", str3);
        bundle.putString("dialog_tag", str4);
        bVar.setArguments(bundle);
        bVar.a(aVar);
        return bVar;
    }

    public View onCreateView(LayoutInflater layoutInflater, ViewGroup viewGroup, Bundle bundle) {
        getDialog().requestWindowFeature(1);
        getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(0));
        View inflate = layoutInflater.inflate(e.c(getActivity(), "tutu_alert_dialog_layout"), viewGroup, false);
        getDialog().getWindow().setGravity(17);
        if (getArguments() != null) {
            this.d = getArguments().getString("left_text");
            this.e = getArguments().getString("right_text");
            this.f = getArguments().getString("dialog_tips");
            this.g = getArguments().getString("dialog_tag");
        }
        a(inflate);
        setCancelable(false);
        return inflate;
    }

    public void a(a aVar) {
        this.h = aVar;
    }

    private void a(View view) {
        this.b = (Button) view.findViewById(e.a(getActivity(), "tutu_alert_dialog_ignore_btn"));
        this.c = (Button) view.findViewById(e.a(getActivity(), "tutu_alert_dialog_recharge_btn"));
        this.a = (TextView) view.findViewById(e.a(getContext(), "tutu_alert_dialog_tips"));
        if (!f.c(this.d)) {
            this.b.setText(this.d);
        }
        if (!f.c(this.e)) {
            this.c.setText(this.e);
        }
        if (!f.c(this.f)) {
            this.a.setText(this.f);
        }
        this.b.setOnClickListener(new OnClickListener(this) {
            final /* synthetic */ b a;

            {
                this.a = r1;
            }

            public void onClick(View view) {
                if (this.a.h != null) {
                    this.a.h.c_(this.a.g);
                }
                this.a.dismiss();
            }
        });
        this.c.setOnClickListener(new OnClickListener(this) {
            final /* synthetic */ b a;

            {
                this.a = r1;
            }

            public void onClick(View view) {
                if (this.a.h != null) {
                    this.a.h.c(this.a.g);
                }
                this.a.dismiss();
            }
        });
    }
}

问题就出在标红的位置,通过这个

http://stackoverflow.com/questions/36116606/nosuchmethoderrorcom-android-app-fragment-getcontext-in-android

知道, 新版的sdk才支持getContext方法,但是adobe air sdk 要跟进比较慢,没有该方法,要改成 getActivity

因为这是第三方sdk , 没有源代码, 只能修改 class文件。

google 一番 如何修改字节码, 找到几篇文章。 放在最后。

下载 jclasslib ,安装。打开jclasslib bytecode viewer 用于查看 b.class 文件的字节码

解压jar包, 将 com/tutu/common/a文件夹内的b.class 文件 拖拽进bytecode viewer

根据上面反编译出来的源码,我们要修改的地方在   private void a(View view)  这个方法中,展开左边的Methods, 一个个看图中名称为a的方法, 观察右边的Access flags, 如果不是private的迅速跳过,是的话观察右边Descriptor ,这里面是参数列表。 觉得像的就展开它, 选中 [0]Code, 观察右边的byte code

图中 22行 就是要找的 getContext。 鼠标点击前面的 #56 , 跳的下面这张图

看到使用的 划红线的部分是 #80  再回到 bytecode中 , 看13行 getActivity的地方时 #54, 鼠标点击它, 跳到下面这张图

看到 划红线的部分是 #78 , 滑动左边的列表, 发现这个东西是在Constant Pool 分类下。 好下面编代码修改字节码, 将指向#80 改成指向 #78

在eclipse中新建一个java project。

进入jclasslib 安装目录, 进入 modules/data/src/main/java ,拷贝 org 及其内容到 java project src 目录下

新建一个 App 类,放main方法

import java.io.DataInput;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import org.gjt.jclasslib.io.ClassFileWriter;
import org.gjt.jclasslib.structures.CPInfo;
import org.gjt.jclasslib.structures.ClassFile;
import org.gjt.jclasslib.structures.InvalidByteCodeException;
import org.gjt.jclasslib.structures.MethodInfo;
import org.gjt.jclasslib.structures.constants.ConstantMethodrefInfo;
import org.gjt.jclasslib.structures.constants.ConstantNameAndTypeInfo;

public class App {
    public static void main(String[] args) throws InvalidByteCodeException, IOException {
         String filePath = "C:/Users/fp/Documents/goalPlatformClientV1/ANE/tutu/package/Android-ARM/b.class";
            FileInputStream fis = new FileInputStream(filePath);
            DataInput di = new DataInputStream(fis);
            ClassFile cf = new ClassFile();
            cf.read(di);  

            CPInfo[] infos = cf.getConstantPool();  

            MethodInfo[] ms = cf.getMethods();
            MethodInfo m = ms[5];

//            System.out.println(m.getAccessFlags());
//            System.out.println(m.getAccessFlagsVerbose());
//            System.out.println(m.getName());
//            System.out.println(m.getNameIndex());
//            System.out.println(m.getDescriptor());
//            System.out.println(m.getDescriptorIndex());
//            AttributeInfo[] getAttributes = m.getAttributes();
//            System.out.println(m.getAttributes());

            ConstantMethodrefInfo uInfo = (ConstantMethodrefInfo) infos[56]; //刚刚那里是CONSTANT_Methodref_info所以这里要用这个
            ConstantNameAndTypeInfo nt = uInfo.getNameAndTypeInfo();
            String s = String.format("%s\n%s\n%s\n%s", nt.getName(), nt.getTag(), nt.getTagVerbose() ,  nt.getVerbose());
            System.out.println(s);
            uInfo.setNameAndTypeIndex(78);
              infos[56] = uInfo;  

//            int count = infos.length;
//            for (int i = 0; i < count; i++) {
//                if (infos[i] != null) {
//                    System.out.print(i);
//                    System.out.print(" = ");
//                    System.out.print(infos[i].getVerbose());
//                    System.out.print(" = ");
//                    System.out.println(infos[i].getTagVerbose());
//                    if (i == 160) {//刚刚找到的是21位置
//                        ConstantUtf8Info uInfo = (ConstantUtf8Info) infos[i]; //刚刚那里是CONSTANT_Utf-8_info所以这里要用这个
////                        System.out.println("xxxxxxxxxxxxxxxxxxxx");
////                        uInfo.setString("getActivity");  // 如果用这种方式直接修改string ,不行, Name and Type 后面的type 还是Landroid/content/Context
////                        infos[i] = uInfo;
//                    }
//                }
//            }
            //这种方式也可以,一样的
    /*      if(infos[count] != null) {
                ConstantUtf8Info uInfo = (ConstantUtf8Info) infos[i]; //刚刚那里是CONSTANT_Utf-8_info所以这里要用这个
                uInfo.setBytes("baidu".getBytes());
                infos[count] = uInfo;
            }*/  

            cf.setConstantPool(infos);
            fis.close();
            File f = new File(filePath);
            ClassFileWriter.writeToFile(f, cf);
    }
}

用于练手的附件:

http://files.cnblogs.com/files/lonkiss/java.library.tutu.zip

解压之后有个jar包。 博客园不让直接上传jar包

参考资料:

直接修改别人jar包里面的class文件 工具:jclasslib - hexin373的专栏 - 博客频道 - CSDN.NET http://blog.csdn.net/hexin373/article/details/6669813

如何利用JClassLib修改.class文件 - “羊习习”的专栏 - 博客频道 - CSDN.NET http://blog.csdn.net/betterandroid/article/details/14520667

时间: 2024-10-28 21:29:07

记一次使用修改字节码的方法解决java.lang.NoSuchMethodError的相关文章

android apk 防止反编译技术第二篇-运行时修改字节码

上一篇我们讲了apk防止反编译技术中的加壳技术,如果有不明白的可以查看我的上一篇博客http://my.oschina.net/u/2323218/blog/393372.接下来我们将介绍另一种防止apk反编译的技术-运行时修改字节码.这种方法是在工作中在实现app wrapping时,看到国外的一篇关于android 安全的介绍实现的并且独创.下面我们来介绍一下这种方法. 我们知道apk生成后所有的java生成的class文件都被dx命令整合成了一个classes.dex文件,当apk运行时d

实例详解:反编译Android APK,修改字节码后再回编译成APK

本文详细介绍了如何反编译一个未被混淆过的Android APK,修改smali字节码后,再回编译成APK并更新签名,使之可正常安装.破译后的apk无论输入什么样的用户名和密码都可以成功进入到第二个Activity. 有时难免要反编译一个APK,修改其中的若干关键判断点,然后再回编译成一个全新的可用的apk,这完全是可实现的.若要完成上述工作,需要以下工具,杂家后面会把下载链接也附上.这些软件截止本文发布时,经过杂家确认都是最新的版本. 1.APK-Multi-Toolv1.0.11.zip 用它

修改Hibernate实体类字段int为Integer产生java.lang.NoSuchMethodError

问题描述: 在将Hibernate实体类字段int为Integer产生java.lang.NoSuchMethodError,但是明明是有这个方法的,但是一直说没有这个方法.后经仔细对比字节码文件,发现jdk在编译字节码时,会对相关方法进行处理. 详细编译情况如下: 原始代码: net.setPushstatus(Integer.parseInt(pushstatus)); 实体类属性字段类型为int 时编译的代码: net.setPushstatus(Integer.parseInt(push

记一次部署时报java.lang.NoSuchMethodError:javax.persistence.spi.PersistenceUnitInfo.getValidationMode()Ljavax / persistence / ValidationMode;的解决办法

楼主在部署war包的时候,本地启动不报错,服务器商报如下问题: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Invocation of init method failed; nested excepti

java Instrument修改字节码实现aop功能

Agent工程2个类: public class MyAgent { /** * 该方法在main方法之前运行,与main方法运行在同一个JVM中 * 并被同一个System ClassLoader装载 * 被统一的安全策略(security policy)和上下文(context)管理 */ public static void premain(String agentOps, Instrumentation inst) { System.out.println("=========prema

Python 文件编译为字节码的方法

一般情况下 python 不需要手动编译字节码.但是如果不想直接 release 源代码给其他人,将文件编译成字节码,可以实现一定程度的信息隐藏. 1) 使用模块 py_compile 编译一个单文件 import py_compile py_compile.compile('./demo.py') 2) 使用模块 compileall 递归的编译一个文件包 import compileall compileall.compile_dir('./test') 3) 使用模块 compileall

从字节码层面,解析 Java 布尔型的实现原理

最近在系统回顾学习 Java 虚拟机方面的知识,其中想到一个很有意思的问题:布尔型在虚拟机中到底是什么类型? 要想解答这个问题,我们看 JDK 的源码是无法解决源码的,我们必须深入到 class 文件中才能解决问题.于是他给出了这么一道题: public class Foo{ static boolean flag; public static void main(String[] args){ flag = true; if(flag){ System.out.println("Hello,

记一次netty版本冲突,报java.lang.NoSuchMethodError: io.netty.util.internal.ObjectUtil.checkPositive的问题

elasticsearch 5.6中使用TransportClient初始化抛异常 在引入elasticsearch5.6的transportclient包中,会引入netty进行通信. <!-- transport客户端 --> <dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>transport</artifactId> <version

从字节码指令看重写在JVM中的实现

Java是解释执行的,包括动态链接的特性,都给解析或运行期间提供了很多灵活扩展的空间.面向对象语言的继承.封装和多态的特性,在JVM中是怎样进行编译.解析,以及通过字节码指令如何确定方法调用的版本是本文如下要探讨的主要内容,全文围绕一个多态的简单举例来看在JVM中是如何实现的. 先简单介绍几个概念.对于字节码执行模型及字节码指令集的相关概念可以参考之前的一篇介绍http://blog.csdn.net/lijingyao8206/article/details/46562933. 一.方法调用的