精华内容
下载资源
问答
  • 去APK签名校验工具

    2018-04-12 14:58:30
    去除APK签名校验工具, 去除APK签名校验工具, 去除APK签名校验工具, 去除APK签名校验工具, 去除APK签名校验工具
  • 拦截获取签名信息的方法,来做到全局爆破签名校验功能逻辑,这个工具可以解决以往需要手动的反编译app来进行破解,有了这个工具,完全可以一键化操作功能,无需在进行反编译破解操作了
  • rsa签名校验工具

    2018-05-21 10:34:44
    RAS签名校验工具,本地生成公钥私钥,快速便捷的接入支付宝支付系统
  • Golang HttpAPI签名验证工具包,提供对API请求的签名生成、签名校验等工具类
  • delphi 数字签名校验

    2015-12-11 09:42:36
    delphi数字签名验证及能够获取数字签名文件信息。
  • 此工具旨在帮助开发者检测调用【微信支付接口API】时发送的请求参数中生成的签名是否正确,提交相关信息后可获得签名校验结果 (2) 根据选择的校验方式填入对应的的XML或参数值 XML校验:请将提交到接口或接口返回...
  • Native 层校验签名 NDKx团参数加密和签名校验MD5 加密校验 Java 层做加密 (防止别人抓包,无法防止别人反编译或者调试,混淆,爱加密 3W) Native层加密 NDK https://www.jianshu.com/p/097f4486f3e6 安全(200多...
  • 支付宝开发,秘钥、签名校验工具。可生成秘钥对,可生成签名等
  • 1. 有些应用会使用工具对应用进行扫描进行判断所存在的风险,特别一些国企,金融,安全类等应用,其中扫描有一项是**应用签名校验风险**。 2. 危害的风险描述: 签名证书是对App开发者身份的唯一标识,开发者可利用...

    1. 为什么要进行签名校验

    1. 有些应用会使用工具对应用进行扫描进行判断所存在的风险,特别一些国企,金融,安全类等应用,其中扫描有一项是应用签名未校验风险

    2. 危害的风险描述: 签名证书是对App开发者身份的唯一标识,开发者可利用签名证书有效降低App的盗版率,。未进行签名证书的App,可能被反编译后进行二次打包。重新打包签名的应用,可能导致App被仿冒盗版,影响其合法收入,甚至可能被添钓鱼代码、病毒代码、恶意代码,导致用户敏感信息泄露或者恶意攻击。

    3. 因此针对这个需求需要对应用进行处理,可以检测App程序启动时是否校验签名证书。增加签名证书的校验代码,降低App被二次打包的几率。

    4. 通常会对应用进行几步操作: 1.增加签名校验。 2.对App进行加固。3.对加固的App进行二次签名。

    2. 查看安卓证书信息

    Ionic移动开发完成后,需要进行打包操作,此时需要使用证书进行打包。

    证书可以自己进行生成。那如何查看证书生成后的信息呢?

    # 在证书的文件根目录运行
    keytool -list -v -keystore mykeystorefile(证书名称)
    # 输入密钥库口令,这时请输入证书生成时所用到的密码
    
    钥库类型: JKS
    密钥库提供方: SUN
    
    您的密钥库包含 1 个条目
    
    别名: com.zhangguoye.app
    创建日期: 2018-1-26
    条目类型: PrivateKeyEntry
    证书链长度: 1
    证书[1]:
    所有者: CN=com.zhangguoye.app, OU=com.zhangguoye.app, O=com.zhangguoye.app
    发布者: CN=com.zhangguoye.app, OU=com.zhangguoye.app, O=com.zhangguoye.app
    序列号: 1230f945
    有效期开始日期: Tue Feb 26 15:04:52 CST 2018, 截止日期: Sat Feb 20 15:04:52 CST 2020
    证书指纹:
             MD5: 12:34:56:68:6A:78:22:90:12:FE:12:95:12F:6E:29:12
             SHA1: 12:23:45:E1:D1:01:A2:12:37:B2:E3:B1:79:21:16:FD:0B:8D:3E:EB
             SHA256: 12:10:37:15:73:D1:11:F1:32:23:33:63:12:9A:12:12:ED:12:12:12:12:2D:02:12:22:4A:52:62:92:51:22:01
             签名算法名称: S*********
             版本: 3
    
    扩展: 
    
    .....
    *******************************************
    *******************************************
    

    3. 编写检验文件

    3.1 编写校验文件SignCheck.java

    package com.zhangguoye.app; // 你的包名
    import android.app.AlertDialog;
    import android.content.Context;
    import android.content.pm.PackageInfo;
    import android.content.pm.PackageManager;
    import android.content.pm.Signature;
    import android.content.DialogInterface;
    import android.util.Log;
    
    import java.io.ByteArrayInputStream;
    import java.io.InputStream;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.security.cert.CertificateEncodingException;
    import java.security.cert.CertificateFactory;
    import java.security.cert.X509Certificate;
    
    
    
    public class SignCheck {
      private Context context;
      private String cer = null;
      private String realCer = null;
      private static final String TAG = "SignCheck";
    
      public SignCheck(Context context) {
        this.context = context;
        this.cer = getCertificateSHA1Fingerprint();
      }
    
      public SignCheck(Context context, String realCer) {
        this.context = context;
        this.realCer = realCer;
        this.cer = getCertificateSHA1Fingerprint();
      }
    
      public String getRealCer() {
        return realCer;
      }
    
      /**
       * 设置正确的签名
       *
       * @param realCer
       */
      public void setRealCer(String realCer) {
        this.realCer = realCer;
      }
    
      /**
       * 获取应用的签名
       *
       * @return
       */
      public String getCertificateSHA1Fingerprint() {
        //获取包管理器
        PackageManager pm = context.getPackageManager();
    
        //获取当前要获取 SHA1 值的包名,也可以用其他的包名,但需要注意,
        //在用其他包名的前提是,此方法传递的参数 Context 应该是对应包的上下文。
        String packageName = context.getPackageName();
    
        //返回包括在包中的签名信息
        int flags = PackageManager.GET_SIGNATURES;
    
        PackageInfo packageInfo = null;
    
        try {
          //获得包的所有内容信息类
          packageInfo = pm.getPackageInfo(packageName, flags);
        } catch (PackageManager.NameNotFoundException e) {
          e.printStackTrace();
        }
    
        //签名信息
        Signature[] signatures = packageInfo.signatures;
        byte[] cert = signatures[0].toByteArray();
    
        //将签名转换为字节数组流
        InputStream input = new ByteArrayInputStream(cert);
    
        //证书工厂类,这个类实现了出厂合格证算法的功能
        CertificateFactory cf = null;
    
        try {
          cf = CertificateFactory.getInstance("X509");
        } catch (Exception e) {
          e.printStackTrace();
        }
    
        //X509 证书,X.509 是一种非常通用的证书格式
        X509Certificate c = null;
    
        try {
          c = (X509Certificate) cf.generateCertificate(input);
        } catch (Exception e) {
          e.printStackTrace();
        }
    
        String hexString = null;
    
        try {
          //加密算法的类,这里的参数可以使 MD4,MD5 等加密算法
          MessageDigest md = MessageDigest.getInstance("SHA1");
    
          //获得公钥
          byte[] publicKey = md.digest(c.getEncoded());
    
          //字节到十六进制的格式转换
          hexString = byte2HexFormatted(publicKey);
    
        } catch (NoSuchAlgorithmException e1) {
          e1.printStackTrace();
        } catch (CertificateEncodingException e) {
          e.printStackTrace();
        }
        return hexString;
      }
    
      //这里是将获取到得编码进行16 进制转换
      private String byte2HexFormatted(byte[] arr) {
    
        StringBuilder str = new StringBuilder(arr.length * 2);
    
        for (int i = 0; i <arr.length; i++) {
          String h = Integer.toHexString(arr[i]);
          int l =h.length();
          if (l == 1)
            h = "0" + h;
          if (l > 2)
            h = h.substring(l - 2, l);
          str.append(h.toUpperCase());
          if (i < (arr.length - 1))
            str.append(':');
        }
        return str.toString();
      }
    
      /**
       * 检测签名是否正确
       * @return true 签名正常 false 签名不正常
       */
      public boolean check() {
    
        if (this.realCer != null) {
          cer = cer.trim();
          realCer = realCer.trim();
          if (this.cer.equals(this.realCer)) {
            return true;
          }
        }else {
          Log.e(TAG, "未给定真实的签名 SHA-1 值");
        }
        return false;
      }
    
      public void checkSuccess() {
        new AlertDialog.Builder(context)
          .setTitle("签名校验成功")
          .setMessage("成功!")
          .setPositiveButton("确定", null)
          .show();
      }
    
      public void showCheckErrorTips() {
        new AlertDialog.Builder(context)
          .setTitle("签名校验失败")
          .setMessage("存在签名异常,请在官方下载最新的APP!http://zhangguoye.com")
          .setCancelable(false)
          .setPositiveButton("确定", new DialogInterface.OnClickListener() {
    
            @Override
            public void onClick(DialogInterface dialog, int which) {
              System.exit(0);
            }
    
          })
          .show();
      }
    }
    
    

    3.2 修改MainActivity.java

    修改MainActivity.java,增加检验签名方法

    
    package com.zhangguoye.app; // 你的包名
    
    import android.content.Intent;
    import android.os.Bundle;
    import android.text.TextUtils;
    import android.util.Base64;
    import org.apache.cordova.*;
    import java.io.UnsupportedEncodingException;
    
    public class MainActivity extends CordovaActivity {
    
        public static String param = "";
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            // ...
            // 此处增加方法调用
            singCheck();
            // ...
        }
    
    	/**
    	* 增加签名检验方法
    	**/
        private void singCheck() {
          SignCheck signCheck = new SignCheck(this, "12:23:45:E1:D1:01:A2:12:37:B2:E3:B1:79:21:16:FD:0B:8D:3E:EB"); // 第2步查看签名指纹的SHA1
          if (signCheck.check()) {
            // 签名正常
          } else {
          	// 签名异常
            signCheck.showCheckErrorTips();
          }
        }
    }
    

    3.3 其他方案

    可以编写安卓的方法,通过ionic层使用javascript调用进行通信,同样实现运行时进行签名的校验。
    可以封装成签名校验的cordova插件,供ionic项目进行使用。

    4. 打包

    4.1 移除android平台

    ionic cordova platform rm android
    

    4.2 增加android平台版本

    ionic cordova platform add android
    

    若需要指定版本则

    ionic cordova platform add android@6.5.0
    

    4.3 覆盖MainActivity.java

    • 在目录下找到此文件并覆盖
      platforms/android/src/com/zhangguoye
      因为目录可能跟你的不同,你可以直接在项目里搜索此文件进行覆盖

    • MainActivity.java当前目录加入文件SignCheck.java

    4.4 使用证书进行打包

    ionic cordova build android --device --minifycss --minfyjs --optimizejs --prod --release -- -- --keystore=./certification/keystore/zhangguoye --alias=com.zhangguoye.app
    
    • 注意keystore=./certification/keystore/zhangguoye --alias=com.zhangguoye.app
      这一段是配置成你自己的证书路径和证书名称、证书别名

    5. 打包完成安装apk

    打包完成,安装apk并启动。
    此时若是App被人使用其他证书进行二次签名后,会有对应的提示。
    确定后会关闭APP
    在这里插入图片描述

    6. 加固原理

    这里有一篇介绍安卓加固的原理文章,可了解下:
    https://www.cnblogs.com/my-testing-life/articles/12613496.html

    展开全文
  • android 如何绕过签名校验

    千次阅读 2020-07-29 14:47:59
    我们知道 android 存在二次打包的现象,这样可以篡改一些功能,所以部分app会在一些重要功能或者接口请求或者程序的入口的地方使用签名校验,校验下 SHA1 或 MD5 等信息,看看是否是自己原始的签名,如果不是,则...


    上一章我们说了签名的一些基本信息,这一章说说它的用法。我们知道 android 存在二次打包的现象,这样可以篡改一些功能,所以部分app会在一些重要功能或者接口请求或者程序的入口的地方使用签名校验,校验下 SHA1 或 MD5 等信息,看看是否是自己原始的签名,如果不是,则强制程序退出。这个属于安全防御,自然有盾就有矛,现在就探讨一下怎么绕过签名校验,也就是常说的 hook 。


    所谓 hook ,也就是通过反射和动态代理,做一些常规做不到的操作,比如替换对象的属性值,或者拦截对象的方法等等,现在先说个简单的,比如 View 的点击事件,通常是 setOnClickListener() 设置个点击回调,如果这个 view 是三方的,并且已经被设置了点击事件,此时如果我们想统计它的点击次数怎么办?这时可以考虑反射了。

    我们知道 View 的 点击事件

        public void setOnClickListener(@Nullable OnClickListener l) {
            if (!isClickable()) {
                setClickable(true);
            }
            getListenerInfo().mOnClickListener = l;
        }
    
        ListenerInfo getListenerInfo() {
            if (mListenerInfo != null) {
                return mListenerInfo;
            }
            mListenerInfo = new ListenerInfo();
            return mListenerInfo;
        }
        
        static class ListenerInfo {
            ...
            public OnClickListener mOnClickListener;
        }


    看看 getListenerInfo() 是什么,它是个 ListenerInfo 对象,并且还是 View 的成员变量。由上面的这些信息,我们通过反射可以获取到这个属性值,然后进一步通过反射后去 ListenerInfo 里面的属性 mOnClickListener 的值,这样我们就拿到了 View 的点击事件的回调,我们这时候把点击回调包裹起来,重新赋值给 mOnClickListener 即可。好,上代码

        private void hookClick(View view) {
    
            try {
                // 得到 View 的 ListenerInfo 对象
                Method getListenerInfo = View.class.getDeclaredMethod("getListenerInfo");
                getListenerInfo.setAccessible(true);
                Object listenerInfo = getListenerInfo.invoke(view);
                // 得到 原始的 OnClickListener 对象
                Class<?> listenerInfoClz = Class.forName("android.view.View$ListenerInfo");
                Field clickListener = listenerInfoClz.getDeclaredField("mOnClickListener");
                clickListener.setAccessible(true);
                View.OnClickListener originOnClickListener = (View.OnClickListener) clickListener.get(listenerInfo);
                // 用自定义的 OnClickListener 替换原始的 OnClickListener
                View.OnClickListener hookedOnClickListener;
                if(originOnClickListener instanceof HookedOnClickListener){
                    HookedOnClickListener listener = (HookedOnClickListener) originOnClickListener;
                    hookedOnClickListener = listener.origin;
                } else {
                    hookedOnClickListener = new HookedOnClickListener(originOnClickListener);
                }
                clickListener.set(listenerInfo, hookedOnClickListener);
            } catch (Exception e) {
            }
        }
    
        static class HookedOnClickListener implements View.OnClickListener {
            public View.OnClickListener origin;
    
            HookedOnClickListener(View.OnClickListener origin) {
                this.origin = origin;
            }
    
            @Override
            public void onClick(View v) {
                // 在这里添加自己想做的逻辑
                Toast.makeText(getApplicationContext(), "hook click", Toast.LENGTH_SHORT).show();
                if (origin != null) {
                    origin.onClick(v);
                }
            }
        }


    以上,就是一个简单的hook样例,这个是用反射来完成的。

    InvocationHandler 及 Proxy 是动态代理,它里面的实现原理就是反射,封装了反射的代码,InvocationHandler 使用必须是接口才能调用。饶了这么一圈,才到了怎么hook签名这一步。 我们一般获取签名 SHA1 和 MD5 的方法如下

        public static String takeSHA1Finger(Context context) {
            PackageManager pm = context.getPackageManager();
            String packageName = context.getPackageName();
            int flags = PackageManager.GET_SIGNATURES;
            PackageInfo packageInfo = null;
            try {
                //获得包的所有内容信息类
                packageInfo = pm.getPackageInfo(packageName, flags);
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
    
            //签名信息
            Signature[] signatures = packageInfo.signatures;
            byte[] cert = signatures[0].toByteArray();
    
            //将签名转换为字节数组流
            InputStream input = new ByteArrayInputStream(cert);
    
            CertificateFactory cf = null;
            try {
                cf = CertificateFactory.getInstance("X509");
            } catch (Exception e) {
                e.printStackTrace();
            }
            X509Certificate c = null;
            try {
                c = (X509Certificate) cf.generateCertificate(input);
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            String hexString = null;
    
            try {
                //加密算法的类 
                MessageDigest md = MessageDigest.getInstance("SHA1");
    //            MessageDigest md = MessageDigest.getInstance("SHA256");
    //            MessageDigest md = MessageDigest.getInstance("MD5");
    
                //获得公钥
                byte[] publicKey = md.digest(c.getEncoded());
                hexString = byte2HexFormatted(publicKey);
            } catch (Exception e) {
                e.printStackTrace();
            } 
            return hexString;
        }
    
        /**
         * 编码进行16 进制转换
         */
        public static String byte2HexFormatted(byte[] arr) {
            StringBuilder str = new StringBuilder(arr.length * 2);
    
            for (int i = 0; i <arr.length; i++) {
                String h = Integer.toHexString(arr[i]);
                int l =h.length();
                if (l == 1)
                    h = "0" + h;
                if (l > 2)
                    h = h.substring(l - 2, l);
                str.append(h.toUpperCase());
                if (i < (arr.length - 1))
                    str.append(':');
            }
            return str.toString();
        }


    比较关键的是 Signature[] signatures = packageInfo.signatures; 这行代码,签名的信息都存在这个里面,为了获取 App 的这个信息,我们也可以写个方法

        public static void getSignature(Context context) {
            try {
                PackageInfo packageInfo = context.getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES);
                if (packageInfo.signatures != null) {
                    return packageInfo.signatures[0].toCharsString();
                }
            } catch (Exception e) {
            }
            return null;
    
        }

    打印后,会是一个长串的值

    30801b751426aa6dd5589e853489d13fccd2034130820229a0030201020204272eafed300d06092a864886f70d01010b05003051310b300906035504061302434e310b3009060355040813027368310b3009060355040713027368310c300a060355040a1303647a70310c300a060355040b1303647a70310c300a06035504031303647a70301e170d3230303731353038323434375a170d3435303730393038323434375a3051310b300906035504061302434e310b3009060355040813027368310b3009060355040713027368310c300a060355040a1303647a70310c300a060355040b1303647a70310c300a06035504031303647a7030820122300d06092a864886f70d01010105000382010f003082010a0282010100b0cef1dd755268d6f339cd897d7748f2f6cb641d8d7c4f0f2ccb6de3ed2d5a4c9405d792debdaa672aa8f8c1aa45f8233ebcacfc3343dc5b0eefa5f7038e9d4808067d2075eacc1ba1c80bc19bcaaba87d50073856de1715f3c5c2af52dd32709da2f254b106db20c96054c4efb238463d7b8b6b840adb5ac10feaf7d557181a8f5f07c36c1b24a2ba240442b71ca88771c5e471e7f24ef386226ca073890c46866a2da9c6ae5a318ede037291f72f5cc8ee022b00da014b68954c5c137a7637f0380a6349b9397b4e6ccee70ce04ba47433c2f6e4c8c9f0702a6eab2db0992ac6ad7d29791b6491d9fb3e37d7f275badb902053bff6175508a605d8ab0f7d5d0203010001a321301f301d0603551d0e0416041471c21602ed4357112be3e354f82b2c3c27590bd8300d06092a864886f70d01010b0500038201010080360bcb21c74c448c91d59c57f3aae80e1b7de47ffc06e85842268051e8642e6922b44bcb9bf4803b1b22ba49379a58200b4589decbf74eb28b2c059e524316d6f75cab06b2e8fdb8b22c73e327ef4ba3ab788a5f8194daf46e6ece40760f6d92bade8928289b5ff3700b36f96d72d40c5b059b8725f6c7f564f48caa8b3140f1398a501381b8c560ccd28fd1365320cc998064cb67a433590919a24d01f3aa4664c29316a9327ccaeae68d5fe3183eb3a6805a200fdaa03ffa0d8aac44131004846db2730d786fd51abdf47bd8c2bff4f518006b62aecf22cc64c5b6716d1754dccf20b44d78e85f068445c018d505


    到这,基本就明白了。上面这个值决定了签名的值,那么我们只要想办法替换这个签名值就行了。一般二次打包用的都是自己的key,那么在这个地方返回自己的签名值就能把签名判断的问题给解决了,怎么返回呢?动态代理出场了。 

    这个值是通过 getPackageManager() 获取的,那么我们只要想办法去改变 PackageManager 的方法返回值就行。了解 Activity 的启动跳转流程的朋友,应该都了解 ActivityThread 这个类,我们获取它的 sPackageManager 这个属性。为什么是它? Context 调用 getPackageManager() 方法,实际上调用了 ContextImpl 的 getPackageManager() 方法

        @Override
        public PackageManager getPackageManager() {
            if (mPackageManager != null) {
                return mPackageManager;
            }
    
            IPackageManager pm = ActivityThread.getPackageManager();
            if (pm != null) {
                // Doesn't matter if we make more than one instance.
                return (mPackageManager = new ApplicationPackageManager(this, pm));
            }
    
            return null;
        }
    
    
        // ApplicationPackageManager 类
        protected ApplicationPackageManager(ContextImpl context,
                                  IPackageManager pm) {
            mContext = context;
            mPM = pm;
        }
    
        @Override
        public PackageInfo getPackageInfo(String packageName, int flags)
                throws NameNotFoundException {
            return getPackageInfoAsUser(packageName, flags, mContext.getUserId());
        }
    
        @Override
        public PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
                throws NameNotFoundException {
            try {
                PackageInfo pi = mPM.getPackageInfo(packageName, flags, userId);
                if (pi != null) {
                    return pi;
                }
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
            throw new NameNotFoundException(packageName);
        }

    从这一串代码中,可以看出最终调用的是 mPM.getPackageInfo(packageName, flags, userId) 这行代码,mPM 是 IPackageManager pm = ActivityThread.getPackageManager() 从这里来的,看看这个方法

        public static IPackageManager getPackageManager() {
            if (sPackageManager != null) {
                //Slog.v("PackageManager", "returning cur default = " + sPackageManager);
                return sPackageManager;
            }
            IBinder b = ServiceManager.getService("package");
            //Slog.v("PackageManager", "default service binder = " + b);
            sPackageManager = IPackageManager.Stub.asInterface(b);
            //Slog.v("PackageManager", "default service = " + sPackageManager);
            return sPackageManager;
        }


        
    这里返回的就是  ActivityThread 中的 sPackageManager 这个属性。前因清楚了,那么就看看怎么替换它吧,老规矩,使用反射;动态代理也登场了,   

        public static void hookPMS(Context context, String signed){
            try{
                Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
                Method currentActivityThreadMethod =
                        activityThreadClass.getDeclaredMethod("currentActivityThread");
                Object currentActivityThread = currentActivityThreadMethod.invoke(null);
                // 获取 ActivityThread 里面原始的 sPackageManager
                Field sPackageManagerField = activityThreadClass.getDeclaredField("sPackageManager");
                sPackageManagerField.setAccessible(true);
                Object sPackageManager = sPackageManagerField.get(currentActivityThread);
                // 代理对象, 用来替换原始的对象
                Class<?> iPackageManagerInterface = Class.forName("android.content.pm.IPackageManager");
                Object proxy = Proxy.newProxyInstance(iPackageManagerInterface.getClassLoader(),
                        new Class<?>[] { iPackageManagerInterface },
                        new PmsHookInvocationHandler(sPackageManager, signed));
                // 1. 替换掉ActivityThread里面的 sPackageManager 字段
                sPackageManagerField.set(currentActivityThread, proxy);
                // 2. 替换 ApplicationPackageManager里面的 mPM对象
                PackageManager pm = context.getPackageManager();
                Field mPmField = pm.getClass().getDeclaredField("mPM");
                mPmField.setAccessible(true);
                mPmField.set(pm, proxy);
            }catch (Exception e){
            }
        }

    上面是替换了 ActivityThread 和 PackageManager 中的 sPackageManager ,我们看看代理类的代码

    public class PmsHookInvocationHandler implements InvocationHandler {
    
        private Object base;
        private String SIGN;
    
        public PmsHookInvocationHandler(Object base, String sign) {
            this.base = base;
            this.SIGN = sign;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if("getPackageInfo".equals(method.getName())){
                String pkgName = (String)args[0];
                Integer flag = (Integer)args[1];
                if(flag == PackageManager.GET_SIGNATURES){
                    Signature sign = new Signature(SIGN);
                    PackageInfo info = (PackageInfo) method.invoke(base, args);
                    info.signatures[0] = sign;
                    return info;
                }
            }
            return method.invoke(base, args);
        }
    
    }


    hookPMS(Context context, String signed) 方法中的 signed 就是我们自己二次打包的签名值,可以先通过代码获取一下,这个值只与签名keystore有关。

    今天涉及到的是逆向的基本知识点,我们使用逆向知识为了确保手机安全及技能提升,不是用于钻 App 的漏洞,不允许用于非法盈利及篡改别人的应用。
     

    展开全文
  • 签名校验破解过程

    千次阅读 2019-11-12 14:30:29
    签名校验破解过程 一、前提说明 我们知道Android中的每个应用都是有一个唯一的签名,如果一个应用没有被签名是不允许安装到设备中的,一般我们在运行debug程序的时候也是有默认的签名文件的,只是IDE帮我们做了签名...

    签名校验破解过程

    一、前提说明

    我们知道Android中的每个应用都是有一个唯一的签名,如果一个应用没有被签名是不允许安装到设备中的,一般我们在运行debug程序的时候也是有默认的签名文件的,只是IDE帮我们做了签名工作,一般在应用发版的时候会用唯一的签名文件进行签名,那么我们在以往的破解中可以看到,我们有时候需要在反编译应用之后,然后从新签名在打包运行,这个又给了很多二次打包团队谋取利益的一种手段,就是反编译市场中的包,然后添加一些广告代码,最后使用自家的签名在此从新打包发布到市场中,因为签名在反编译之后是获取不到的,所以只能用自己的签名文件去签名,但是在已经安装了应用设备再去安装一个签名不一致的应用也是安装失败的,这样也有一个问题就是有些用户安装了这些二次打包的应用之后,无法再安装正规的应用了,只有卸载重装。那么这时候我们可以利用应用的签名是唯一的特性做一层防护。
    在这里插入图片描述
    APP签名校验,主要作用是保护APP本身的安全利益,防止被违法分子进行内部代码和文件修改,然后插入广告等获利行为,进行违法操作。签名不通过的话有两种现象,第一种:签名校验不通过直接退出程序,也就是你会发现回编译二次打包运行失败的现象,第二种:签名校验不通过不触发程序指定逻辑,导致没有错误信息,也进不了应用的现象。
    在这里插入图片描述
    Java层校验
    我们为了防止应用被二次打包,或者是需要破解我们的apk的操作,在入口处添加签名验证,如果发现应用的签名不正确就立即退出程序,我们可以在应用启动的时候获取应用的签名值,然后和正规的签名值作比对,如果不符合就直接退出程序即可.
    在这里插入图片描述在这里插入图片描述
    NDK校验
    NDK的签名校验,就是把关键的信息放入so文件中,然后在so文件中获取到相应的APP签名信息,然后在so文件中进行比较。
    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述
    服务器验证
    会把相应获取到的签名信息,直接上传到服务器,然后在服务器端进行一个签名的校验,或者请求服务器,服务器返回一个数据,与本地获取到的签名信息进行比较。

    二、分析破解过程

    今天我们分析一款曾经比较火的手游,贪吃虫大战V3.3版本。首先我们运行正常的app的现象是下面这样的,没有任何异样。
    在这里插入图片描述
    然后我们再进行一次重新编译,问题就出来了,界面一直加载到99%,然后就是进不了下个界面,这个就是我们上面说的第二个现象,所以说明该运用是有进行签名校验的。
    在这里插入图片描述
    首先我先全局搜索signature这个关键字,发现有一堆的地存在到signature,所以猜测可能有很多地方进行签名校验,所以我先看看是不是会先请求服务端,用burpsuite工具进行抓取请求包。
    在这里插入图片描述
    一打开app之后,就有很多请求,其中有个特殊的请求,就是get_user_info这个请求。我们就全局搜索get_user_info这个关键字,这两个是定义的代码,然后全局搜索,看看那些有调用到这个方法。
    在这里插入图片描述在这里插入图片描述在这里插入图片描述
    我们查看些调用到的位置,就一个地方,再点进去看。
    在这里插入图片描述
    在这里插入图片描述
    继续看调用的地方,又四个选项,我们要依次排除非相关的选项。
    在这里插入图片描述
    最下面两个方法,主要是进行update下载方面的操作,所以可以排除在外。其中倒数第三个选项,主要是关于皮肤申请的操作。在我们请求中根本没有皮肤相关的数据,所以也去除掉,然后就剩下一个方法。 (下图是排除选项)
    在这里插入图片描述
    我们打开那个选项,其中有useinfo的关键字,查看谁调用到这个e方法。
    其中有三个选项,发现它们都是在一起的,但是关键的是a.a.e这个调用,前面有个run。在最上面有一个比较的判断,我认为就是比较前面信息。进去里面查看源码。
    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述
    上面一看这是个两个判断,一个为真就成立,所以我们查看下h.a方法是字符串的构造算法,所以我们就编码下,看下是结果是什么。
    在这里插入图片描述
    用esclipe进行编写,然后得到结果是,得到了相应的一个结果,看的有点像Md5的字符串。
    在这里插入图片描述
    我们在打开equals后面的q.a的方法,发现这就获取app的签名信息,然后返回一个MD5值,然后在与a678a930b9829b54a44f92a840916f7d1这个签名信息进行对比,这就是这个app本身的签名校验的逻辑。那我们就直接修改smali文件,使得这个比较为正确。这里我们就直接用androidKiller这个工具进行操作。
    在这里插入图片描述
    然后编译,运行app,但是发现还是不行,还是卡在相同的地方。所以我们就在回去查看原先的代码。
    在这里插入图片描述
    在其中一个方法中,还有一个校验。所以我们在进行一个修改。
    在这里插入图片描述在这里插入图片描述
    所以我们还是像上面一样进行修改相应的smali文件。再次运行,发现还是卡在相同地方。现在回想一下,我们每次登录的时候,都是卡在进度条,所以,会不会在进度条的地方也是有进行一个匹配,这样的话,我们就尝试查找一下相应的进度条应该会有的代码。
    在这里插入图片描述
    查看日志信息,尤其是看刚开始时候的日志。还有就是看看在请求列表中,是否有特殊的字符。我们看到config这个特殊的字符两个地方都有出现。查看到日志信息有个地方getconfigAndroid这个特殊的关键字,然后全局进行搜索。有三个选项。
    在这里插入图片描述在这里插入图片描述
    打开第一个,又是一个比较,又是一个获取app信息,所以再次进行修改。
    在这里插入图片描述在这里插入图片描述
    修改后,进行运行,终于成功进入了界面!!!
    在这里插入图片描述在这里插入图片描述

    三、总结

    最后做一个总结,这个就是一个本地的签名校验,通过多处地方获取本地的签名然后转换为MD5然后与算法得出的值进行校验,从而达到相应的签名校验的目的。处理这个过程其实就是要抓住关键字,还有就是分析相应得代码。本地得签名校验不是太过复杂,但是如果开发人员用心得话,就会像这个软件有多处的校验。如果是服务端的校验,我认为也是进行抓包,然后在代码查询关键字,然后在顺着进行分析。麻烦的在于把校验信息放在native层,如果在加上反调试,那么这样分析就会比较麻烦。
    签名校验是目前比较快捷简便,成本很低的一个安全措施,所以注重这方面的加强,是非常有利于app的安全性的。但是如果每次这样操作下来,可能会花费很多时间,如果app没有一个反pticte注入的措施的话,我觉得可以使用xposed模块进行一个破解,这样又省时间,又省精力。所以下次可以的话,我将多食用xposed模块进行一个破解。

    展开全文
  • 一键破解APK签名校验 参考自,在其基础上进行了部分改进,并用纯 Java 实现。 原理 通过插入代码到 Application 入口,hook 了程序中 PackageManager 的 getPackageInfo 方法,改变了其获取到的签名信息。 处理步骤 ...
  • Java实现两种方式 RSA签名, RSA签名校验通过 .keystore密钥文件实现生成密钥文件 test2.keystore相关使用通过密钥生成器实现Byte数据转换成 Hex字符串相关使用 通过 .keystore密钥文件实现 生成密钥文件 test2....

    通过 .keystore密钥文件实现

    生成密钥文件 test2.keystore

    • ./keytool -genkeypair -v -alias test2 -dname “CN=test2,OU=Wenyao,O=RnD,L=Yangpu,ST=Shanghai,C=China” -keyalg RSA -keysize 2048 -keypass 123456 -keystore d:/test2.keystore -storepass 123456 -validity 99999 -storetype JCEKS
    
    import org.springframework.util.Base64Utils;
    import java.io.FileInputStream;
    import java.io.InputStream;
    import java.security.KeyStore;
    import java.security.PrivateKey;
    import java.security.PublicKey;
    import java.security.Signature;
    import java.security.cert.Certificate;
    import static org.springframework.http.converter.FormHttpMessageConverter.DEFAULT_CHARSET;
    
    /**
     * 通过已生成的密钥文件签名类
     * */
    public class SignatureByFile {
        /** 密钥路径及文件名称*/
        private String keyStoreFilePath = "D:/test2.keystore";
        /** 密钥库口令*/
        private String keyStorePass = "123456";
        /** 密钥库算法*/
        private String keyStoreType = "JCEKS";
        /** 密钥项别名*/
        private String keyAlias = "test2";
        /** 密钥口令*/
        private String keyPass = "123456";
        /** 签名算法 [SHA256withRSA/SHA1withRSA/MD5withRSA/MD2withRSA]*/
        private String algorithm = "SHA256withRSA";
    
        /** 获取公钥*/
        public PublicKey getPublicKey(final String keyAlias) throws Exception {
            /** 指定密钥库算法创建实例*/
            final KeyStore ks = KeyStore.getInstance(keyStoreType);
            /** 指定要读取的密钥库文件, 创建文件输入流*/
            final InputStream is = new FileInputStream(keyStoreFilePath);
            /** 载入密钥库文件输入流*/
            ks.load(is, keyStorePass.toCharArray());
            /** 生成证书(X.509标准)*/
            final Certificate cert = ks.getCertificate(keyAlias);
            /** 从证书中获取公钥部分*/
            final PublicKey publicKey = cert.getPublicKey();
            return publicKey;
        }
    
        /** 获取私钥*/
        public PrivateKey getPrivateKey(final String keyAlias, final String keyAliasPass) throws Exception {
            /** 指定密钥库算法创建实例*/
            final KeyStore ks = KeyStore.getInstance(keyStoreType);
            /** 指定要读取的密钥库文件, 创建文件输入流*/
            final InputStream is = new FileInputStream(keyStoreFilePath);
            /** 载入密钥库文件输入流*/
            ks.load(is, keyStorePass.toCharArray());
            /** 通过别名和密钥口令, 从已载入的密钥库获取私钥*/
            final PrivateKey privateKey = (PrivateKey) ks.getKey(keyAlias, keyAliasPass.toCharArray());
            return privateKey;
        }
    
        /**
         * RSA签名
         *  @param plainText 源文本
         *  @return 签名
         **/
        public String sign(final String plainText) throws Exception {
            /** 获取私钥*/
            final PrivateKey privateKey = getPrivateKey(keyAlias, keyPass);
            /** 生成签名算法实例*/
            final Signature instance = Signature.getInstance(algorithm);
            instance.initSign(privateKey);
            instance.update(plainText.getBytes(DEFAULT_CHARSET));
            final byte[] signature = instance.sign();
            return Base64Utils.encodeToString(signature);
        }
    
        /**
         * RSA签名校验
         *  @param plainText 源文本
         *  @param signedText 签名
         *  @return 签名
         **/
        public boolean verify(final String plainText, final String signedText) throws Exception {
            /** 获取公钥*/
            final PublicKey publicKey = getPublicKey(keyAlias);
            /** 生成签名算法实例*/
            final Signature instance = Signature.getInstance(algorithm);
            instance.initVerify(publicKey);
            instance.update(plainText.getBytes(DEFAULT_CHARSET));
            final byte[] signature = Base64Utils.decodeFromString(signedText);
            return instance.verify(signature);
        }
    
    }
    
    

    相关使用

    
            final String data = "签名前数据";
            final SignatureByFile signature = new SignatureByFile();
            final String signedData = signature.sign(data);
            final boolean verify = signature.verify(data, signedData);
            System.out.println(signedData);
            System.out.println(verify);
    
    > M5hgiRY30fX7zSo5i+YRpAzpLR0i7dNX1qN8cwN6Sf9JtjoCaVXUywSOz1PC3w311jXtixPMfK3B1+2R7qUlOjTOWPFBiMBqUYy4f0xXLFQw9kMDK/WTtn+nbVezHgqxtBjFNC1/nttmH7PSxhY+SV2KzU+RzC0F2tGhWm0JQQpCZuVQQzVdikJL8NBY8vdqT9SuEpFSX++f2gAQVFyro92ntye/zTbqOFr/qAhXF03CN2jGlvp7ZEbxkxqcMLSHjeTGF0VfzrLgDVpsQdh4qb3dr+tgrs1rgBsVKKiRKmsus4J7YKrjWzcgd/DZAIEozoMnDT/GOonAphRKG6VDMA==
    > true
    
    

    通过密钥生成器实现

    
    import org.springframework.util.Base64Utils;
    import java.security.KeyPair;
    import java.security.KeyPairGenerator;
    import java.security.Signature;
    import java.security.interfaces.RSAPrivateKey;
    import java.security.interfaces.RSAPublicKey;
    import static org.springframework.http.converter.FormHttpMessageConverter.DEFAULT_CHARSET;
    
    /**
     * RSA算法签名类
     * */
    public class SignatureByRSA {
        /** 签名算法 [SHA256withRSA/SHA1withRSA/MD5withRSA/MD2withRSA]*/
        private String algorithm = "MD5withRSA";
        /** 秘钥对实例*/
        private KeyPair keyPair;
    
        /**
         * RSA签名校验
         *  @param keyalg 密钥算法 [RSA]
         *  @param keySize 密钥位大小 [1024,2048]
         * */
        public SignatureByRSA(final String keyalg, final int keySize) throws Exception {
            final KeyPairGenerator pairGenerator = KeyPairGenerator.getInstance(keyalg);
            pairGenerator.initialize(keySize);
            /** 生成秘钥对实例*/
            keyPair = pairGenerator.generateKeyPair();
        }
    
        /** 获得 RSA公钥*/
        public RSAPublicKey getPublicKey() {
            return (RSAPublicKey) keyPair.getPublic();
        }
    
        /** 获得 RSA私钥*/
        public RSAPrivateKey getPrivateKey() {
            return (RSAPrivateKey) keyPair.getPrivate();
        }
    
        /**
         * RSA签名
         *  @param plainText 源文本
         *  @return 签名
         **/
        public String sign(final String plainText) throws Exception {
            /** 生成签名算法实例*/
            final Signature instance = Signature.getInstance(algorithm);
            instance.initSign(getPrivateKey());
            instance.update(plainText.getBytes(DEFAULT_CHARSET));
            final byte[] signature = instance.sign();
            return Base64Utils.encodeToString(signature);
        }
    
        /**
         * RSA签名校验
         *  @param plainText 源文本
         *  @param signedText 签名
         *  @return 签名
         **/
        public boolean verify(final String plainText, final String signedText) throws Exception {
            /** 生成签名算法实例*/
            final Signature instance = Signature.getInstance(algorithm);
            instance.initVerify(getPublicKey());
            instance.update(plainText.getBytes(DEFAULT_CHARSET));
            final byte[] signature = Base64Utils.decodeFromString(signedText);
            return instance.verify(signature);
        }
    
    }
    
    

    Byte数据转换成 Hex字符串

    
        public static String convertBytes2HexString(final byte[] data) {
            final StringBuilder sb = new StringBuilder();
            for (int i = 0; i < data.length; i++) {
                String s = Integer.toHexString(0xFF & data[i]);
                if (s.length() == 1){
                    sb.append(0).append(s);
                }else {
                    sb.append(s);
                }
            }
            return sb.toString();
        }
        
    

    相关使用

    
            final String data = "签名前数据";
            final SignatureByRSA signature = new SignatureByRSA("RSA", 2048);
            final String signedData = signature.sign(data);
            final boolean verify = signature.verify(data, signedData);
            System.out.println(convertBytes2HexString(signature.getPublicKey().getEncoded()));
            System.out.println(convertBytes2HexString(signature.getPrivateKey().getEncoded()));
            System.out.println(signedData);
            System.out.println(verify);
    
    > 30820122300d06092a864886f70d01010105000382010f003082010a0282010100a0830541f5d36f87f02b2b5b4da4717fc6a6f6466f73ac6df0a6f5ac58494632f4ca6b524c96a8086e4d92fa2db90f8b74a98e4d647530974c5bb050b7eb103b3f60a29a98511ed8c14a58ca2caffa20be79f961a91daabf6311f58a9c3cf4df065b6943eea139c41fd982fca0172f6e0500d0a0fae1ec9891d488d78fb476b09aa278f2128f6cf41d0548af9057a967bf0f91405de2564221129d1a7b56ac8c6366fa507166a5549fd80c5297bb9f73add9ff2fe4d91e9c2fb0c7119762ed0f1441d1786340b8550760a9f8a752bc134754432c2ded1f6cfd8ae2a3b8a6f6febac7fbbd8201c668cdbacbde740f61fd3d3bb56c359a4e68b17054074f23d8670203010001
    > 308204bd020100300d06092a864886f70d0101010500048204a7308204a30201000282010100a0830541f5d36f87f02b2b5b4da4717fc6a6f6466f73ac6df0a6f5ac58494632f4ca6b524c96a8086e4d92fa2db90f8b74a98e4d647530974c5bb050b7eb103b3f60a29a98511ed8c14a58ca2caffa20be79f961a91daabf6311f58a9c3cf4df065b6943eea139c41fd982fca0172f6e0500d0a0fae1ec9891d488d78fb476b09aa278f2128f6cf41d0548af9057a967bf0f91405de2564221129d1a7b56ac8c6366fa507166a5549fd80c5297bb9f73add9ff2fe4d91e9c2fb0c7119762ed0f1441d1786340b8550760a9f8a752bc134754432c2ded1f6cfd8ae2a3b8a6f6febac7fbbd8201c668cdbacbde740f61fd3d3bb56c359a4e68b17054074f23d86702030100010282010021f5d6a4f007fe2efac78772d7e89502fddad17c71943dc53d07762f32b6be1d85e155f7a1b0b678a22dd38d0f237a807cfc6ad94109f26ff07dedbe064bbbb16d655ac1eebc2b149d40c7ac6eec04b5ecd70dc675eb8af6d7553368524a2b5d0c9146a252d8d5ca031b1835af0178cd844d79d6b57e1b7ae44fdd6c0e419662ccc1d12d019e0ca5c89b336e06a316807bdf0fd490587ed2064456c4052179b896a1943be20c3badb49670a296672b70016c1a5cae9ab09caf46a4c189cb2ceb951347ff014f865fe027e529b9f3be6768fda01c0cb969e341302b64d6bab1f06e433cbd044d92e3c1a2d29209679964ce0d9effb7a845e660631c937b060a9102818100e5b60a8d6b16f7710eb8685096c19614feedcb81ea5923025c160b7d34decc4f67310185d813d23911bc96b9b53b2f8cf2ed82d31063e718649a760f41d6e42868e57545821c1734af883e010cf297247b114ec37bc06ef002943acbda9cc97fa57e6151d594b399dd31da14cf962ce713ff3c5fe4b7b03019fa5a3e0dcb3de502818100b2e19d0d0dc512bdb4d02e390d48960b8d7146f36f6aefeced4d4478c22d03769e4ece695ede8da73332125c74e0557a735c850ff8b15850cd5a1c211e8d7fa32bbb885b0792732108f0f3d21a8d2f9ea52d09b879a70c0cac075f3184c39a1bc9db2dee41ec841b2e010ea5a425da6db8595d969660c2f30519a553fd43f85b0281806cccf168941d77abcbfe1a2279954d81ba51653fae80f3ad0d72bf94593361f005ff572a4ef2ec726d6d36b2d51d2863893867eb926acf2d65912774088a40dcc6fa4fb6516efeaaccad2a391329dfdebe8faf0fb610aa3af1edc57f82971a75642c5cea06d07cd4d6c8e2f352735de74138761f3154b395c4e8285015f67ae50281810098724611d5e659ee6aa5f07e969f81a01ecea714d7a35f0d9fbef5d7a444106b8ac6c68a6e5b648555d01a6dc8fd565ba0ecfc497c3c4773b54e5222b793076fdbd95a7f9998699d81ad375d9b5b2f761876a58e1dbf0b39d17206e280aee22b44801248b6ce0657d380fb269719db691e6eaa9fa7509753dcc8da6915b253790281800f5db934ef6397c6081583ba35732fd8808fd9d955debbb3cfd013dde14c4bdef29dbe8577c865f7c6fb2696c55a44edef90a89b6c35969154376898dd120224218885070fb26d300837c34335052723cf44ab3d609f8ccc7430eb1e1e6e44028aa26d334c48ba426cef2cb1f78125fbe681e62f27dba8544cd9c9b2d838bef9
    hoJghoDi0WDNDcmsEyED1LNoaRTnZRD439v8tBlUimeKuBezrVH7fS7iTqyyCgBc+X6cdgk4pMo7eIMEBt+JwqBlKhjpdJZXQUHSbZQG7D+MGSXk9Ip5/FCIf3Y2NBc3Y5dHiqYVy0vQ5QClj8QK0l8hPZKUuvXSHHWNo72tF7OHkqdy/gysmVRbul3Kyt/QqA7CVU4d9IkNOoePlvqYcZeAdx2yIDisS0jR+RnsrKM2UuRrRJRMaPpRcxtYz/tqnvr/x4ZeUvLT85XZZAf9pL/Me2XwmN3D5i7tyuKKG/bftMS65YOyNvFD0Kytd9fAoOaeMVilfos9R4C3rjSh1w==
    > true
    
    

    如果您觉得有帮助,欢迎点赞哦 ~ 谢谢!!

    展开全文
  • 假如出现这个错误,可以仔细参考微信支付系列教程:http://www.wxapp-union.com/portal.php?mod=list&catid=19 微信支付签名校验地址:...使用微信支付签名校验工具生
  • 安卓,应用签名校验

    千次阅读 2019-01-02 19:30:43
    签名校验原理:  安卓应用程序,使用apkTool.jar,或其他反编译工具很容易被篡改。  为了保证应用程序未被别人修改过,可以在应用中添加签名信息校验逻辑。(当应用被反编译再重新打包后,签名信息无法与我们...
  • 签名校验和完整性校验主要是针对于二次打包的检测防范措施,如果没有签名校验和完整性校验功能,应用可能被恶意攻击者二次打包,被盗版的风险大大增加,同时也可能进行任意代码修改。 针对上面的问题,这章我们就对...
  • Java数字签名校验

    千次阅读 2019-06-15 17:15:36
    最近工作中有使用到数字签名校验来验证API请求是否被劫持,从而保证数据安全,特此记录便于日后查阅。 1、签名校验流程图 功能介绍: 用户通过签名来保护数据安全,后端通过比较前端传过来的签名是否相同,来...
  • 安卓应用签名校验

    2021-03-15 17:08:53
    问题:app程序未对签名证书进行校验,被其他证书重新签名可正常启动 apk打包签名思路: Build -> Generate Signed Bundle/APK -> APK -> Create New 创建一个新的证书 (也可以选择一个已有的证书)->ok ...
  • 破解安装签名校验

    2020-10-30 11:39:23
    破解安装签名校验 案例App:书旗 一、未修改App正常运行 APP正常运行,无闪退 二、反编译后不做修改重新打包 开始反编译,打开命令行: apktool d shuqi.apk 结果如图: 不做修改,直接打包: ...
  • android 增加自己的应用签名校验

    千次阅读 2017-07-03 13:59:06
    需求:在Android原本的签名校验系统中,添加自己的校验过程。 android使用Java的数字证书相关的机制来给apk加盖数字证书,要理解Android数字证书,需要先了解以下数字证书的概念和java的数字证书机制。 1、基础...
  • 使用微信支付签名校验工具生成的签名跟我自己服务器生成的签名是一样的,但还是报签名错误。 A:appId不需要传入支付api,但是需要参与签名。 以下为全网的讨论,包括本站曾经的一些贴内的讨论: 相关讨论: MD5加密...
  • apk去签名校验

    千次阅读 2019-06-26 15:02:59
    1.确认app是否有签名校验,将apk重新签名打包安装后发现apk出现直接闪退或安装失败则说明apk存在签名校验,在代码中去寻找相关签名校验代码。 判决校验代码是写在java层还是so层 java层搜索关键字sign,signature...
  • PHP中签名校验步骤

    2019-09-27 00:37:25
    在接口开发中,PHP会向面向用户的APP端,小程序端提供安全有效的校验接口。 在PHP中,可实践的有签名sign验证法: PHP服务端约定一个或多个混淆参数,参与签名生成,一般签名生成使用md5函数。 假如客户端提交的参数...
  • 为何你的应用老是被破解,该如何有效的做签名校验? 前言 上一篇发了个简单的NDK实现的签名校验,众所周知,签名校验是防止二次打包最普遍的方式。下面是常见的签名校验方法: /** * 做普通的签名校验 */ private ...
  • 文章目录背景思路Android应用签名apk安装白名单进行签名校验获取apk签名的证书指纹android源码中获取证书指纹签名校验结语微信公众号 思路 白名单功能主要是通过确认要安装的应用是否在白名单上,如果不在,则不允许...
  • system分区签名校验方法

    千次阅读 2018-06-07 11:07:54
    ##签名过程 整个system签名过程如下图所示: 哈希树的生成 Dm-verity 使用加密散列树提供块设备的透明完整性检查,每个块以 4k 的大小来划分,都有一个 SHA256 的值。树中的每个节点是加密 hash,其中叶...
  • 注意签名校验的使用函数 getPackageManager() 得到包管理结构体 getPackageName() 得到包名 getPackageInfo()->signatures 得到apk签名 signatures ->hashCode() 得到签名的哈希值 二.逆向一个有签名校验的app...
  • android逆向过so库签名校验

    千次阅读 2020-01-03 06:12:20
    00- APK已经被混淆,并且有签名校验, 目的是过签名校验,混淆且不用管,直接搜索 使用到的so库名, apk逆向分析.png 开始IDA加载so库静态分析。。。。 ida.png 直接查看 Exports 导出函数窗口 ,...
  • 目录一、签名校验二、定位入口函数三、定位校验签名函数四、修改smali绕过签名校验五、重新打包/签名/安装检验 一、签名校验 签名验证,就是在APP中写入自己私钥的hash值,和一个获取当前签名中的私钥hash值的函数...
  • 为了安全起见,一些应用会利用自身的签名信息对应用做一层防护,为了防止应用被二次打包操作,在之前已经介绍了很多关于应用签名校验爆破的方法,一条基本原则不能忘:全局搜索”signature”字符串,这里可以在J...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 67,939
精华内容 27,175
关键字:

签名校验