Android 保护应用内购买免受 Freedom Hack 的侵害

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21966369/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 05:22:40  来源:igfitidea点击:

Protecting in-app purchases from Freedom Hack

android

提问by Asaf

I've throughtoutly searched this site as well as others for answers and found no actual one.

我已经彻底搜索了该站点以及其他站点以寻求答案,但没有找到真正的答案。

My question is what exactly does the Freedom Hack (which allows users to get in-app purchases without paying) do. That is, what part of the process is altered. I've found this listof applications for which the hack works, and some of the entries there are dated to this month, meaning that it hasn't been completely fixed yet. The responses I've seen were "verify the application in your server", but if the hack, for example, alters the Java.Security's signature verification function, so it always returns true, then adding my own signature in the server wouldn't help much.

我的问题是 Freedom Hack(它允许用户无需付费即可获得应用内购买)究竟是做什么的。也就是说,过程的哪一部分被改变了。我找到了这个hack 所针对的应用程序列表,其中一些条目的日期是本月,这意味着它尚未完全修复。我看到的响应是“验证服务器中的应用程序”,但是如果黑客改变了 Java.Security 的签名验证函数,因此它总是返回 true,那么在服务器中添加我自己的签名就不会帮助很大。

采纳答案by lemycanh

I don't know if the author still follow this topic or not. But I spent sometime to find out (googling) the way how freedom work and how to prevent it (until they update the way freedom work) in my project and it works. My implementation is really simple and you don't need to verify by sending request to server (which affect the performance and take more effort to implement it).

不知道作者是否还在关注这个话题。但是我花了一些时间在我的项目中找出(谷歌搜索)自由的工作方式以及如何防止它(直到他们更新自由的工作方式)并且它是有效的。我的实现真的很简单,你不需要通过向服务器发送请求来验证(这会影响性能并且需要更多的努力来实现它)。

The current implementation of freedom is that it will replace (redirect) all the method calls of java.security.Signature.verify(byte[])to a freedom's jni method which in turn just simply always return true (or 1).

自由的当前实现是它将替换(重定向)所有对java.security.Signature.verify(byte[])自由的 jni 方法的方法调用,而后者只是简单地始终 return true (or 1)

Take a look at java.security.Signature.verify(byte[]):

看看java.security.Signature.verify(byte[])

 public final boolean verify(byte[] signature) throws SignatureException {
        if (state != VERIFY) {
            throw new SignatureException("Signature object is not initialized properly");
        }
        return engineVerify(signature);
    }

Here the engineVerifymethod is an abstract protectedmethod which is first defined in java.security.SignatureSpi(Signature extends SignatureSpi). OK, that enough, because I can't believe java.security.Signature.verify(byte[])method anymore, I would use engineVerifymethod directly. To do that, we need to use reflection. Modify the verifymethod of IABUtil/Securityfrom:

这里的engineVerify方法是abstract protectedjava.security.SignatureSpi( Signature extends SignatureSpi) 中首先定义的方法。好吧,够了,因为我不能再相信java.security.Signature.verify(byte[])方法了,我会engineVerify直接使用方法。为此,我们需要使用反射。修改from的verify方法IABUtil/Security

public static boolean verify(PublicKey publicKey, String signedData, String signature) {
        Signature sig;
        try {
            sig = Signature.getInstance(SIGNATURE_ALGORITHM);
            sig.initVerify(publicKey);
            sig.update(signedData.getBytes());
            if (!sig.verify(Base64.decode(signature))) {
                Log.e(TAG, "Signature verification failed.");
                return false;
            }
            return true;
        } catch (...) {
            ...
        }
        return false;
    }

To:

到:

public static boolean verify(PublicKey publicKey, String signedData, String signature) {
        Signature sig;
        try {
            sig = Signature.getInstance(SIGNATURE_ALGORITHM);
            sig.initVerify(publicKey);
            sig.update(signedData.getBytes());
            Method verify = java.security.SignatureSpi.class.getDeclaredMethod("engineVerify", byte[].class);
            verify.setAccessible(true);
            Object returnValue = verify.invoke(sig, Base64.decode(signature));
            if (!(Boolean)returnValue) {
                Log.e(TAG, "Signature verification failed.");
                return false;
            }
            return true;
        } catch (...) {
            ...
        }
        return false;
    }

That is simple but it works with the current implementation of freedom until they update its algorithm in the future.

这很简单,但它适用于当前的自由实现,直到他们将来更新其算法为止。

回答by Shereef Marzouk

then adding my own signature in the server wouldn't help much.

然后在服务器中添加我自己的签名不会有太大帮助。

That is not correct, the signature that "Freedom" uses is invalid and the order id is also invalid.

那是不正确的,“Freedom”使用的签名无效,订单ID也无效。

What I did to ensure that my Application is safe is:

我为确保我的应用程序安全所做的是:

  1. Send isPurchaseValid(myPurchase.getSignature(), myPurchase.getOriginalJson())to my server to verify over there and it works with real purchases but freedom fails everytime.

  2. On the server I check if the signature matches

  3. If it does match I contact "Google APIs Google Play Android Developer API > androidpublisher.inapppurchases.get" to verify that the Purchase exists and that returns my developer payload.

  4. I then use the developer payload to make sure that this purchase is for this specific user and not some other user and this user is sending me his data.

  1. 发送isPurchaseValid(myPurchase.getSignature(), myPurchase.getOriginalJson())到我的服务器在那里进行验证,它适用于真正的购买,但每次都无法自由。

  2. 在服务器上我检查签名是否匹配

  3. 如果匹配,我联系“Google APIs Google Play Android Developer API > androidpublisher.inapppurchases.get”以验证购买是否存在并返回我的开发人员有效负载。

  4. 然后我使用开发人员负载来确保这次购买是针对这个特定用户而不是其他用户,并且这个用户正在向我发送他的数据。

P.S. The developer payload is a Stringyou set before the purchase is made from your android app, it should be something unique to your user.

PS 开发者有效负载是String您在从您的 android 应用程序进行购买之前设置的,它应该是您的用户所独有的。

It maybe a lot of work but It ensure that no one will buy your stuff with freedom and succeed.

这可能需要很多工作,但它确保没有人会自由地购买您的东西并取得成功。

The only thing that I am unable to do is not let freedom have an affect on my application, for example the folks in Path did something I don't know what which made Freedom have no effect what so ever!!!!

我唯一不能做的就是不要让自由影响我的申请,例如 Path 中的人做了一些我不知道的事情,这使得自由没有任何影响!!!!

回答by nexx

I'm using something like this, I know it's not a good solution compared to a remote server check for your signature. I'm checking if Freedom app is installed, if so I'm not opening my app.

我正在使用这样的东西,我知道与远程服务器检查您的签名相比,这不是一个好的解决方案。我正在检查是否安装了 Freedom 应用程序,如果安装了,我不会打开我的应用程序。

@Override
protected void onCreate(Bundle arg0) {
    super.onCreate(arg0);
    if(isHackerAppIsntalled())
        finish();
}

private boolean isHackerAppInstalled() {        
    final PackageManager pm = getApplication().getPackageManager();
    List<ApplicationInfo> packages = pm
            .getInstalledApplications(PackageManager.GET_META_DATA);
    for (ApplicationInfo packageInfo : packages) {
        String packageName = packageInfo.packageName;
        if (packageName.contains("cc.madkite.freedom")
                || packageName.contains("madkite.freedom")) {
            return true;
        }
    }
    return false;
}