单身狗多年了,为了追剧不再吃狗粮,为了出去吃饭、游玩不再当电灯泡,为了回家过年不再被不计其数的亲戚朋友追问“谈了吗”,我决定通过各大交友app神器来寻我一世姻缘。用的很多的相亲软件,基本都要收费,而且很多都是机器人。机器人还TM收费,老子就不信这个邪了,老子就不充金币也要撩妹。

所以我的这篇文章的主题就是反编译,这里我使用的相亲软件是“陌声”,起初它会送你一些金币,让你和美女聊聊尝尝甜头。但是,当你聊的意由未尽的时候,app就会提示你金币不足。

点击“重发”,提示充钱。(还有一种办法:互相关注加为好友可以免费聊。但是我让一妹子关注我,这样就不用花金币了,她说我小气,这点钱都舍不得花。好吧,待我破解之日,我会成为你们眼中的土豪!)

这里,我要自我辩解我一下:我不是小气,我是要享受破解的快感,我是要与这些小流氓软件斗上一斗。

我为什么选择破解这款软件

因为正好这款app没有加固(关于加固破解的技术,正在研究当中。)

我想实现的功能

找到”重发“关于金币的限制,直接就能发送消息。

反编译app

先看我的这篇文章http://xinyiworld.top/wordpress_it/?p=4632 ,通过apktool即可反编译出app,指令如下:

//反编译出smali源码 (加上-r参数,表示不反处理资源文件,能避免重打包的错误。但是后面要修改清单文件,就不要加-r参数了。)
.\apktool.bat -r d  -f   E:\ApkNiXiang\MyTool_MoShen\source\in\com.mosheng_4.6.4_464.apk  -o E:\ApkNiXiang\MyTool_MoShen\source\out 

//修改smali源码后,用此指令重打包apk。
.\apktool.bat b -o E:\ApkNiXiang\MyTool_MoShen\source\rebuild\out.apk E:\ApkNiXiang\MyTool_MoShen\source\out

//生成签名文件
keytool -genkey -alias demo.keystore -keyalg RSA -validity 40000 -keystore demo.keystore

//对重新打包的apk进行重新签名。(签名才能跑起来)
jarsigner -verbose -keystore demo.keystore E:\ApkNiXiang\MyTool_MoShen\source\rebuild\out.apk demo.keystore

调试smali源码

1.通过as调试smali源码

先看我的这篇文章https://www.jianshu.com/p/438ea3cd11ec,了解什么是smali,如何通过android studio进行调试。

2.修改源码

  • 第1步:使app可以被debug
    在清单文件里加上android:debuggable="true"属性,这样android studio才能debug app.

  • 第2步:把app自己的Log开启
    搜索AppLogs相关文件,研究发现最终是由AppSetting.java来控制的

    打开日志,再看smali代码,简直无比清爽!

  • 第3步:定位“重发”按钮
    先看我这篇文章https://www.jianshu.com/p/e9a5d9d2898a,绝对让你事半功倍。

    as里搜索:right_iv_text_readState_iv,
    找到对应的id值:0x7f090b2c,
    再搜索这个id值

    引用这个id值的smali文件还挺多(每个位置都打上断点),这个就需要耐心的去研究源码,debug调试定位了。(前提是你必须十分熟悉smali的语法和调试技巧,然后结合应用开发的经验。)
    最终,我定位到的文件是q$u.smali文件,文件的头信息:

    .class Lcom/mosheng/d/a/q$u;   #如果找不到包,换个smali文件夹找。
    .super Ljava/lang/Object;
    .source "NewChatListAdapter.java"

    是Adapter那就没有错了,这个app里还有个废弃的ChatListAdapter.java(我当时就是误导在这里调试了半天,有点怀疑人生。),注意不要混淆了。

  • 第3步:一步步调试,找到发送消息的核心方法。
    (也许你的断点不是我下面走的,因为我发现只有和某一个妹子的聊天断点才会走下面这个逻辑,我还没有搞明白为什么鬼。)
    》点击方法的开始

    》这个方法debug要step into,就是进入这个方法的实现位置。(一定要debug,如果不debug,这里是个接口,很难找到接口真正的实现位置。)







    这里我加了日志,看了一下发送给底层的参数是啥。

    .method public static sendMessageByType(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I
    #.locals 1
    .locals 6
    
    .line 1
    invoke-static {}, Lcom/weilingkeji/sip/SipManager;->getInstance()Lcom/weilingkeji/sip/SipManager;
    
    move-result-object v0
    
    #step into here
     #CZAdd-----------------------------------
            #变量寄存器V1开始
    
                new-instance v1, Ljava/lang/StringBuilder; # 创建了一个 StringBuilder
                invoke-direct {v1}, Ljava/lang/StringBuilder;-><init>()V
    
                # 定义 Log msg参数中第一部分字符串字面量值
                const-string v2, "json data p0:\n "
                const-string v3, "\njson data p1:\n "
                const-string v4, "\njson data p2:\n "
                const-string v5, "CZLog"    #TAG
    
                # 拼接并输出 String 存入 v1 寄存器中
                invoke-virtual {v1, v2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
                invoke-virtual {v1, p0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
    
                invoke-virtual {v1, v3}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
                invoke-virtual {v1, p1}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
    
                invoke-virtual {v1, v4}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
                invoke-virtual {v1, p2}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
    
                invoke-virtual {v1}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
                move-result-object v1
    
                # 调用 Log 方法打印日志
                invoke-static {v5, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
        #CZAdd-----------------------------------
    
    invoke-virtual {v0, p0, p1, p2}, Lcom/weilingkeji/sip/SipManager;->sendMessageByType(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I
    
    move-result p0
    
    return p0
    .end method

    我特意对比了有金币和没有金币参数是否有所区别,发现一样。


    到这里就没法继续step into了,因为这是个native方法。

    搜索“System"找到so库

    so库,那我就没有辄了。怪不得都说使用so库比较安全。

3.修改源码(找新的突破口)

通过修改“重发”的逻辑行不通,而且作用范围太小。我想着是不是能找到充金币的方法,然后避过微信,直接有可能就能修改金币的数据呢?有了金币,不就可以为所欲为了。
依葫芦画瓢,我找到了微信充值成功后执行的代码。

直接放到微信预支付响应接口的位置上

但是一直报错:
连接上报服务器失败:182.92.194.193 8001[YouMeDataChannel.cpp#ReportProc:157]
这里可以看到这个app即时通讯使用的库好像是:
游密IM:https://youme.im/doc/IMGuideCocosJS.php
但是官方文档好像并没有libsua.so这样的库要引入。

所以,修改的时候尽量不要修改程序原有的执行逻辑,而是去修改数据更为保险。
我又有了一个邪恶的想法:能不能下单的时候,将3块钱买50的金币,将50金币改成个大的数值?

回顾一下微信支付

Android 技术:微信支付接入详细指南
https://blog.csdn.net/zzgzhangzhiguang/article/details/65444113

梳理一下当前app微信支付的逻辑

把前面改的代码改回去,再来走正常支付流程:

1.点击“微信支付”

界面分析

使用uiautomator2 current来获取当前activity

{
    "activity": "com.android.internal.app.DoubleAppResolverActivity",
    "package": "android"
}

https://ask.csdn.net/questions/341236
可以知道这个是微信调起了应用选择器,然后过滤微信应用。
源码分析
》执行了AsyncTask

这一步就是下单,但是过程十分复杂。
它会调用GenerateOrderAsynctask.java (不是带New的那个类),然后这个类会调用HttpInterfaceUri.java(后台接口API)。

用Fiddler抓取下单的请求接口:(https://www.cnblogs.com/yoyoketang/p/6582437.html

》调用WXAPIFactory.createWXAPI方法

》调用IWXAPI.sendReq方法
这个是向微信发起https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_12&index=2调起支付接口
执行这个方法,只显示微信的应用选择器就会弹出来。
看看微信SDK的日志

MicroMsg.SDK.WXApiImplV10: sendReq, req type = 5
MicroMsg.SDK.MMessageAct: send, targetPkgName = com.tencent.mm, targetClassName = com.tencent.mm.plugin.base.stub.WXPayEntryActivity
MicroMsg.SDK.MMessageAct: send mm message, intent=Intent { flg=0x18000000 cmp=com.tencent.mm/.plugin.base.stub.WXPayEntryActivity (has extras) }

2.点击“微信”

微信支付失败,打印日志

CrashReport: >>> com.mosheng.wxapi.WXPayEntryActivity onCreated <<<
MicroMsg.PaySdk.WXFactory: createWXAPI, appId = wxc2cca9846361b1f6, checkSignature = false
MicroMsg.SDK.WXApiImplV10: <init>, appId = wxc2cca9846361b1f6, checkSignature = false
MicroMsg.SDK.WXApiImplV10: handleIntent, cmd = 5
Typeface: CurFontPath: /system/fonts/DroidSansFallbackBBK.ttf
CrashReport: >>> com.mosheng.more.view.ChooseRechargeWayActivity onResumed <<<
CrashReport-Native: Set native info: isAppForeground(true)
CrashReport: >>> com.mosheng.wxapi.WXPayEntryActivity onDestroyed <<<

这是出错的原因应该就是,微信底层会校验当前app和后台上这个应用的签名是否一致,我重打包,签名不一致,自然无法支付了。所以我之前用的微信登录,反编译之后微信登录也无法使用,也是签名的问题,后来我就改用手机号来登录了。

那么,我想到办法1:使反编译重打包的app和原始的app的签名一致不就行了?(求路过的高手指教了,我不会。)
下面是我查到的一些资料:

最近android官方发布了一个漏洞:Janus
https://www.jianshu.com/p/5e35902cddb2
https://www.52pojie.cn/thread-750588-1-1.html
Janus 官网脚本 https://github.com/V-E-O/PoC/tree/master/CVE-2017-13156
判断应用签名的类型:https://blog.csdn.net/HDZWo/article/details/80852905
》Janus.py只能针对单个dex处理,apk有多个dex文件咋办?

获取应用的签名
https://www.jianshu.com/p/33c909940f74

办法2:将app的包名改了,重新签名,然后微信支付平台重新上传

但是我发现支付宝是可以调起支付的。

3.结果

上面的分析,可知首先微信是无法支付的,最重要的:充金币时传递的参数是product_id,并不是金币的数量,所以这个没法篡改。如果app充金币,参数中有金币数量,或许真的能够用支付宝支付成功。

所以后台在定义接口的时候,尽量不要暴露敏感参数,免得让我有机可乘。

4.修改源码(找新的突破口)

在上面的试验中,我尝试以充金币为突破口,但由于下单时传递的参数是product_id,所以没法往下进行。但是我还没有死心,我立马又有了个邪恶的想法:充会员,买一个最低等级的会员,充的是最高级别的等级,骚不骚?

1.源码研究

找到”贵族中心“页面PrivilegeActivity.java,找到点击支付的按钮id:ll_price,然后下断点调试。
找到NobleDetailView.smali,这个类会设置Product_id
下单请求抓包:

就算我充最低等级骑士的时候将Product_id改成最高等级国王级别,支付的钱还是国王级别的钱。

5.修改源码(找新的突破口)

目前看来,和支付相关的功能是无法修改了。但是研究了这么久,不搞点成就感真是不爽。于是,我琢磨出一个可能能修改的功能:查看谁看过我,这也是贵族的权利。

1.布局分析


分析布局,猜测是id为layout_no_visitor_view的View挡住了访客列表,找到它的id为0x7f090699,搜索,找到view的源码。

2.源码分析


这就相当于android里的findViewById,然后this.W = xxxx;

》搜索Lcom/mosheng/view/activity/VisitorRecordActivity;->W,看看哪里对它作了操作。

再往上看v2,

0x8对应的就是android里的View.GONE,那好我改成0x0(也就Visible),看效果:

原来有这么多的美女偷看过我,哈哈!
上面还有些view需要隐藏掉,我就不改了,反正不影响我查看美女。

更多的故事还将继续

现在市面上,绝大多数的app基本都会加固。关于加固的app如何反编译,目前正在研究,有研究成果再分享过大家了。


如有疑惑或者好的建议,或者想纠正作者的博文,请联系如下
公众号:微信公众号搜索“修符道人”或者扫描下方二维码

微信号:XinYi1349308479
QQ邮箱:1349308479@qq.com


0 条评论

发表回复

您的电子邮箱地址不会被公开。