吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 65748|回复: 505
上一主题 下一主题
收起左侧

[Android 原创] wx支付流程以及加密的分析

    [复制链接]
跳转到指定楼层
楼主
三年三班三井寿 发表于 2020-1-4 21:01 回帖奖励
本帖最后由 三年三班三井寿 于 2020-1-9 18:47 编辑

本贴仅供学习交流,请勿用作其他任何用途

19年底捣鼓了一阵wx协议,但没有相关资料所以摸索了挺久。后来找了一套旧版的协议源码,奈何支付协议是转不了帐的,所以自己就看了一下这个。而有了协议框架,我们只需要去看转账相关的组包逻辑。
准备:
分析工具:xp,frIDA,jadx,IDA               wx版本:都差不多,706,707,708都看过
首先开启wx的日志xlog,直接搜索

然后找到开关isLogcatOpen赋值的地方,修改第五个参数为1即可

当然也可以直接找其上层调用


[JavaScript] 纯文本查看 复制代码
        var XLOG=Java.use("com.tencent.mm.sdk.platformtools.ab");
        //var StringClz=Java.use("java.lang.String");
        XLOG.i.overload('java.lang.String', 'java.lang.String', '[Ljava.lang.Object;').implementation=function(s1,s2,s3){
        if(s3==null){
            console.log("i:"+s1+","+s2);
        }else{
            console.log("i:"+s1+","+s2+s3);
        }
            return this.i(arguments[0],arguments[1],arguments[2]);
        }
        XLOG.f.overload('java.lang.String', 'java.lang.String', '[Ljava.lang.Object;').implementation=function(s1,s2,s3){
        if(s3==null){
            console.log("f:"+s1+","+s2);
        }else{
            console.log("f:"+s1+","+s2+s3);
        }
            return this.f(arguments[0],arguments[1],arguments[2]);
        }


微信组包以及加解密的so文件,LibMMProticalJni.so,其组包函数为pack

我们直接hook其Java层调用

[JavaScript] 纯文本查看 复制代码
   var MMProtocalJni=Java.use("com.tencent.mm.protocal.MMProtocalJni");
[/color][/size][/color][/size][size=5][color=red][size=3][color=black]   MMProtocalJni.pack.implementation=function(){  
       console.log("MMpack:"+bytesToHex(arguments[0]));
       return this.pack(arguments[0],arguments[1],arguments[2],arguments[3],arguments[4],arguments[5],arguments[6],arguments[7],arguments[8],arguments[9],arguments[10],arguments[11],arguments[12],arguments[13]);
   }

首先我们扫付款码,这是之前用xp hook的日志,前面一些基本都是环境设备信息,deviceId,clientVersion之类的,和其他功能组包类似,我们所需要关注的是后面的字符串。明显的是transfer_url就是二维码的字串,进行了简单的编码。WCPaySign是本地计算出来的一个值,而WCPaySign以及channel之间还有一段序列特征,具体是啥也没深入研究,有知道的大佬可以告诉我一下。测试中有时会有encrypt_key以及encrypt_userinfo字段,具体也是由WCPaySign以及channel之间字段决定的。
transfer_url是直接通过java库函数URLEncoder解析得到的:
我们可以在这里替换用户扫描的二维码,使得对方不管扫什么码都会跳到我们自己的二维码上。比较简单的做法就是直接hook URLEncoder,通过收款码特征或者堆栈进行判断过滤,当然也可以自己重写这个w类构造,构造一个hashMap传入
[JavaScript] 纯文本查看 复制代码
 URLEN.encode.overload("java.lang.String").implementation=function(str){
        console.log("URLEN");
        var res=this.encode(str);
        var stack=instance.currentThread().getStackTrace();
        var full_call_stack=where(stack);
        return res.indexOf("wxp%3A%2F%2F")==0&&full_call_stack.indexOf("com.tencent.mm.plugin.remittance.model.w.<init>")?
        "wxp%3A%2F%2Ff2f0NtReekKHV87BM0pY6k3TVjHlljtYL4sQ":res
    }
不过如果这样做有一个问题,就是不管是扫谁的二维码,都会跳转到自己的付款页面。
那么能不能实现,扫谁的码就出现给谁付款的页面,但实际转账却并不是给他转?换句话说就是页面上显示的一切都是正常的,但实际却转给了另一个人?当然是可以的,不过我们需要先进行模拟扫我们码的操作,然后获取到返回的openid,ticket等。之后在后面付款的时候将正常的这些字段替换成我们的就可以了。扯远了,下面进行定位,寻找WCPaySign的算法。调用堆栈如下,直接调用函数为com.tencent.mm.ak.t.a
这个函数比较长,jadx反编译的有问题需要修改设置选项,也可以用jd直接查看调用位置
接着找参数赋值的地方,由一个成员变量req实例化为l.b类型后,进行了序列化
而req的初始化在t构造函的数中
同样的方法hook其构造函数,调用堆栈如下

前两个不用看了,u的构造参数qVar.getReqObj也是t的构造参数,而u的构造是在com.tencent.mm.ak.m.dispatch中,构造参数是它的第二个参数q.getReqObj()的返回值。
也是com.tencent.mm.wallet_core.c.u.dispatch的第二个参数

具体赋值的地方在com.tencent.mm.wallet_core.tenpay.model.m.doScene中的rr
这里的rr,之前的q,以及最初的req,他们的类型其实都不一样。rr实际上是com.tencent.mm.wallet_core.tenpay.model.m所继承的父类的成员,该类也正是我们所需要找的,赋值的地方在setRequestData中的最后。在此之前,正是计算了WCPaySign。
getEncryptUrl是q中一个抽象的方法,实现如下
从名称看就是一个3DES(md5(str))的算法,实际上也确实如此
其md5计算只是在Java层调用的标准库
3DES算法Java层调用接口为encrypt,前一个参数是key,没有则默认
除此之外,接口类q中提供了getUri接口,其返回值是post的cgi目录,在com.tencent.mm.wallet_core.c.u onGYNetEnd中的第四个参数传入了q的实例
通过反射获取到post接口为/cgi-bin/mmpay-bin/transferscanqrcode
接下来进入so层
调用的so为libtenpay_utils.so,也是标准生成的C函数
在encrypt中很容易发现默认密钥
但用这key尝试了几种加密方式,结果都不正确
可能并非标准的算法,看了一下置换表也没发现有什么变化,直接将其算法抠出来
Des3Str就是分组加密函数,Des3进行了单个分组加密
流程就是3DES的加密方式:EK3(Dk2(Ek1(P)))
DES_Encode:
代码太长就不贴了,当时用的IDA6.8,其反编译的还是多多少少有些坑的,现在用的7.0但也懒得去这部分反编译是不是一样的。
注意内存结构就行,然后再用frida对so中的调用依次hook定位问题函数,动态调试即可。这里提一点,sub_D86C函数中反编译的代码中有很多__PAIR__,如果直接用网上ida头文件中的宏定义的话会有问题。而且这个问题并非语法问题,而是ida反编译得不够准确,只能通过调试或看反汇编解决
仔细一点观察,就会发现其伪代码逻辑比较奇怪,实际汇编代码如下:
可以将那句伪代码直接用x86内联汇编给替代了
[Asm] 纯文本查看 复制代码
push eax;
mov eax,r5;
sub eax,1;
sbb r5,eax;
shl r5,1;
mov eax,r5;
mov v21,eax;
pop eax;
其实稍作分析,其真实逻辑只是在判断r5是否为0,可以将伪代码改成:
v21=(BYTE2(v49)&0x20)?2:0;到此,paysign算出的结果总算正确了。
再通过有源码的协议进行封包发送及解包,可以获取到返回的各字段
[C] 纯文本查看 复制代码
{{
  "retcode": "0",
  "retmsg": "",
  "user_name": "wxid_7vf3tr41v3g921",
  "true_name": "**鹏",
  "fee": "0",
  "desc": "",
  "scene": "32",
  "transfer_qrcode_id": "aOqTgOotZtAyyz2gsfUHWPV9hsUkxMEHkCVpM5OynlvT6Q2fy6Cwv1ffb7NLyPf9PNB-CY1mWSZW0YqQjo39TbJJWLdpPDnX2EROxb1aHTx1FKd6jqZf1wgFS98q0D32",
  "rcvr_ticket": "Y4pH5nL20VA7CcRPboeyg-4PBk3ma7_U_vksGZzWTBYE4ioVEcz_v6PrG_ZS1QtY",
  "get_pay_wifi": 1,
  "receiver_openid": "oX2-vjvhwAutxXTxz85dJeSzzG-k",
  "scan_scene": 1,
  "favor_list": [],
  "amount_remind_bit": 4
}}
不过12月开始好像就不返回wxid了,也就是user_name返回的是空""
类似的,在转账的时候,还有一个密码的算法,快速地说一下,还是一样的通过调用栈去找
hook com.tencent.mm.plugin.wallet.pay.a.a.b构造
密码是第一个参数authen的成员变量
com.tencent.mm.plugin.wallet.pay.a.a.a同理也是一样
con.tencent.mm.plugin.wallet.pay.a.a.e里有post地址,当然之前paysign里面那个hook也能获取到:
再上一层调用:
Authen为cZw()返回值
密码通过成员变量hef进行赋值
hef赋值的地方
再往上找密码字串
又找到一个字段vfo
密码加密后字符串由getText()得到,具体是com.tencent.mm.wallet_core.ui.formview.c.a.a的返回值
跟进去看到,payu和tenpay有两种返回值
我们好友转账,扫码转账,包括708新加入的手机号转账走的都是tenpay,i大概是触发的类型,确定交易时是1,输入金额时是100,其实现部分
当输入金额时,返回的就是输入的明文数字,其他类型会进行加密。实际上com.tencent.mm.wallet_core.ui.formview.WalletFormView以及com.tencent.mm.wallet_core.ui.formview.EditHintPasswdView的两个getText实现分别返回了输入的金额以及密此码明文,通过此可以修改转账金额,再将生成订单金额还原成之前的金额,即可控制实际转账金额
[JavaScript] 纯文本查看 复制代码
        var WalletOpenViewProxyUI=Java.use("com.tencent.mm.wallet_core.ui.e");
        var old="";
        WalletOpenViewProxyUI.e.overload("double","java.lang.String").implementation=function(a1,a2){
            //var uPn=Java.cast(Authen.class,clazz).getDeclaredField("uPn");
            //uPn.setAccessible(true);
            //send("uPn:"+uPn.get(a1));
            if(a1==0.02)//显示原有金额
                a1=Number(old) ;
            var res=this.e(a1,a2);
            send("a1:"+a1.toString()+",res:"+res);
            var stack=instance.currentThread().getStackTrace();
            var full_call_stack=where(stack);
            //console.log(full_call_stack);
            return res;
        }
        var wwww=Java.use("com.tencent.mm.wallet_core.ui.formview.WalletFormView");
        wwww.getText.implementation=function(){
            old=this.getText();
            return "0.02";//设置实际转账金额
        }


当然你有兴趣也可以把转账成功信息给改了,那么用户很可能都不清楚自己转账金额已经被篡改了,好像又扯远了。
我们继续分析加密函数getEncryptDataWithHash,另一个加密get3DesEncryptData不知是什么情况下进行的,getInputText()能获取到密码明文,紧接着会判断mlEncrypt有没有实现,该成员类型是一个接口类



具体实现在com.tenpay.android.wechat.TenpaySecureEncrypt.encryptPasswd,str就是传入的密码明文,先计算了一下md5。str2传入的是时间戳,时间戳是com.tenpay.android.wechat.TenpaySecureEditText的setSalt方法设置的

接下来进入so层还原其算法即可,算法为RSA2048。毕竟密码位数太短,取md5后也很容易被爆破,在加密之前还需要加盐,盐是时间戳以及随机数填充的。
代码太长也就不贴了,同样在IDA6.8中存在一些错误,提一点encrypt_pass1函数中有这三个变量取了同一个地址


然而事实并非这样,通过汇编可以看到在赋值前先抬高了sp,虽然每次都是取的var_6C位置,但前后栈顶已经不一样了:

也就是说只要操作sp的地方,伪代码都是有些问题的,比如这个else里面的v9=&res,res是传入的参数,这种逻辑一看就是有问题的:

通过反汇编看到这里也是通过sp去索引的变量,实际上sp已经抬高了三次,这里真实的索引不是参数res,而是在局部变量s中:

密码加密算法还原后我们就能模拟其支付协议了。不管是好友转账,扫码转账或者手机号转账最后都是需要req_key的,差不多就是个订单号的意思


好友转账可以通过CGI_TENPAY直接传对方wxid然后返回req_key,扫码比较麻烦些,之前WCPaySign那步获取到openid,ticket,qrcode_id等字段,再通过这些字段去生成订单走/cgi-bin/mmpay-bin/busif2fplaceorder获取到req_key。但在12月之前,扫码仍有wxid返回时,可以投机走旧版的协议/cgi-bin/micromsg-bin/tenpay,逻辑与之前setRequestData找的一样,大概就是将map中元素拼接成字符串,最后再计算一个paysign,而map中元素也就是扫码返回的数据

获取到req_key,最终完成转账操作,bank_type和bind_serial是绑定银行卡的类型id,都为CFT时使用零钱,获取bind_serial也很简单,这里就不讨论了。
708新增的通过手机号转账也是分为三步,通过/cgi-bin/mmpay-bin/transferphonegetrcvr获取到openid等信息,再通过/cgi-bin/mmpay-bin/transferphoneplaceorder生成订单,获取到req_key,最后再由/cgi-bin/mmpay-bin/tenpay/sns_tf_authen确认订单完成转账。组包中的金额好像进行了一种序列化之类的操作,但还是很容易看得出来的,其算法我们可以自己实现:
[C#] 纯文本查看 复制代码
        private int Pow(int x, int n)
        {
            int res = 1;
            while (n > 0)
            {
                if ((n & 1) == 1) res = res * x;//转化为二进制
                x = x * x;//将x平方
                n >>= 1;
            }
            return res;
        }
        public string getFee(int money,bool isfirst=false,int sign=-1)
        {
            if (money < 0x80 && isfirst == true)
                return String.Format("{0:X2}", money);
            int i = 0;
            int temp = (int)money;
            while ((temp /= 0x80)>=1)
                i++;//递归次数
            if (isfirst == true) sign = i;
            int pow= Pow(128, i);//128 i次方
            int dwRes = (pow==1)? money:money/pow;
            money = (pow == 1) ?0: money-dwRes * pow;
            if (isfirst == false) dwRes += 0x80;
            string res = String.Format("{0:X2}", dwRes);
            return sign == 0?res: getFee(money, false, --sign)+res;
        }

手机转账其实并没有分析很多,很多数据没有分析,只是写死的,但也是能实现手机转账的功能。
太晚了不写了,其实也就初探了下微信支付的流程,加密的算法。当然后续还需要进一步的封包压缩加密,但这都有现成的协议代码。虽然wx是一款社交软件,但其加密强度目前来看也是很高的,但在本地上,我们仍能做很多有趣的事情,所以建议大家不要使用wx模块之类的插件



图片1.png (16.25 KB, 下载次数: 202)

图片1.png

免费评分

参与人数 287吾爱币 +246 热心值 +259 收起 理由
万花筒D光彩 + 1 + 1 我很赞同!
光头是信仰 + 1 + 1 热心回复!
小年糕 + 1 用心讨论,共获提升!
Adm1nistrator + 1 + 1 用心讨论,共获提升!
这是一台AI + 1 用心讨论,共获提升!
WYiqiang + 1 + 1 我很赞同!
nm110220 + 1 + 1 用心讨论,共获提升!
thinsir + 1 + 1 鼓励转贴优秀软件安全工具和文档!
腌臜_萝卜 + 1 + 1 用心讨论,共获提升!
yuxuechao + 1 我很赞同!
孤独的鬼人 + 1 + 1 我很赞同!
煎包 + 1 热心回复!
小小金 + 1 + 1 过反调的我。
Rhine_JTG + 1 + 1 我很赞同!
α┃π丨K + 1 + 1 用心讨论,共获提升!
csw曾哥 + 1 用心讨论,共获提升!
叮当东东当当 + 1 + 1 谢谢@Thanks!
沧海行 + 1 + 1 不明觉厉系列+1
shush_sec + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
nofailyoung + 1 + 1 加油!!!
911伟少 + 1 用心讨论,共获提升!
bamqj + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
kal + 1 + 1 向大佬致敬
别拿我雨伞 + 1 + 1 用心讨论,共获提升!
逆向post + 1 用心讨论,共获提升!
selenologist + 1 楼主能够让微信回到只用身份证号码就能实名不用绑银行卡就太厉害了!
oxxo119 + 1 + 1 谢谢@Thanks!
寶刀屠龍生玄光 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
直至最终 + 1 + 1 用心讨论,共获提升!
wuweide + 1 + 1 鼓励转贴优秀软件安全工具和文档!
atts + 1 + 1 用心讨论,共获提升!
歌声振林樾 + 1 我很赞同!
19244024 + 1 能破解异地扫码风控的问题就好了
惧染大魔王 + 1 + 1 没看懂但是感觉很牛批
王妙言 + 1 + 1 果真大佬,不过其实不用担心,因为钱就算被转走了,也没什么人敢去取钱。
dnldnl + 1 谢谢@Thanks!
九笔~ + 1 + 1 --------
沾血灬蘑菇 + 1 + 1 我很赞同!
小呆呆霸王 + 1 + 1 我很赞同!
芯河斑斓 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
vanillasky0220 + 1 + 1 热心回复!
小宾是老子 + 1 + 1 热心回复!。大神好厉害。求一个wx收米码。这种可以写吗?
yishujia + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
早睡早起的JJ + 1 + 1 wx模块毕竟不开源了,如果真要用 就用一些开源应用吧
RCX666 + 1 用心讨论,共获提升!
如丶果 + 1 + 1 我很赞同!
kefeijiajia + 1 看不懂,我只是为了拉下来给个评分,然后牛掰
ailuo2005 + 1 + 1 热心回复!
WESTARK + 1 + 1 谢谢@Thanks!
CHASEXX + 1 鼓励转贴优秀软件安全工具和文档!
柚何不可 + 1 谢谢@Thanks!
不爱everyone + 1 + 1 牛B
嚯嚯嚯000 + 1 + 1 谢谢@Thanks!
hamson1026 + 1 我终于知道什么叫做“不明觉厉”
2毛钱 + 1 我一直总感觉我WX的钱怎么花得这么快,原来还有这种操作!
nieshi666 + 1 + 1 热心回复!
suss66 + 1 + 1 虽然看不懂,但是感觉很牛比的样子
呵呵__ + 1 + 1 谢谢@Thanks!
newpass + 1 热心回复!
国产精品 + 1 + 1 我很赞同!
zycjhcs + 1 + 1 谢谢@Thanks!
Sexyxuan + 1 + 1 我很赞同!
叽叽喳喳4320 + 1 用心讨论,共获提升!
jjm580 + 1 + 1 热心回复!
不谙世事的骚年 + 1 + 1 用心讨论,共获提升!
adofei + 1 + 1 我很赞同!
HG/飞飞 + 1 + 1 用心讨论,共获提升!
zl天涯行客 + 1 用心讨论,共获提升!
Qiao + 1 用心讨论,共获提升!
Jayfeng + 1 + 1 虽然没看懂。。但是...
余佳卓 + 1 + 1 不明觉厉
Algorithms + 1 + 1 我很赞同!
stederlee + 1 + 1 用心讨论,共获提升!
LunMP + 1 + 1 我很赞同!
wzyzzfzjq + 1 + 1 我很赞同!
Jerry_bean + 1 + 1 用心讨论,共获提升!
fushanpupil + 1 + 1 用心讨论,共获提升!
火柴堆火 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
awi0100hjz + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
蒹葭 + 1 用心讨论,共获提升!
hangel1 + 1 + 1 热心回复!
ljkss + 1 + 1 我很赞同!
fengyugudan + 1 + 1 我很赞同!
XZB0797 + 1 + 1 牛逼
幽仙—— + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
explorer126 + 1 我很赞同!
w1zar6 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
那些年打的飞机 + 1 + 1 谢谢@Thanks!
kexiao1987 + 1 + 1 看懂了,意思是不要随便下载插件。
渝A·666666 + 1 + 1 已经处理,感谢您对吾爱破解论坛的支持!
养鸡场厂长 + 1 + 1 用心讨论,共获提升!
葫芦里的小姐姐 + 1 + 1 用心讨论,共获提升!
是小溪嗷 + 1 我很赞同!
ZGSY + 1 用心讨论,共获提升!
cleanmgr + 1 虽然前边没看懂,但是最后一句看懂了,感谢大佬分享
zeorro + 1 + 1 我很赞同!
鱼罐头 + 1 用心讨论,共获提升!
xiaoyueer00 + 1 + 1 我很赞同!
lxq951 + 1 用心讨论,共获提升!
青衣豆子 + 1 谢谢@Thanks!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
xiong1992 发表于 2020-1-6 10:09
是不是可以理解为我买了东西当对方面去扫对方的微X二维码,显示支付成功后结果却是我给我的另外一个号转钱过去了;或者是2元的东西,显示成功支付2元后,实际上我只支付了1元;或者是别人给我转账,转1元,显示成功支付1元,但是实际上我却收到了他转的2元。这个如果给不法分子利用就麻烦了。
推荐
努力的T先生 发表于 2020-1-5 00:03
看不懂,不知道大神能不能给个变得跟你一样优秀的思路,至少把这个看得懂
推荐
TedChen 发表于 2020-1-4 23:55
推荐
股票亏损员 发表于 2020-1-4 21:37
感觉看到是天书
推荐
nshark 发表于 2020-1-4 22:03
看着都迷糊
推荐
 楼主| 三年三班三井寿 发表于 2020-1-8 12:43 |楼主
老哥还会军体拳 发表于 2020-1-7 18:13
老哥,漏洞搞得怎么样了

刚接触没多久
沙发
ccc800 发表于 2020-1-4 21:33
大神。路过 看的眼花
头像被屏蔽
3#
ebacn 发表于 2020-1-4 21:33
感谢,学习了!
头像被屏蔽
5#
52pojieggh 发表于 2020-1-4 21:46
天书一本
6#
linfengtai2008 发表于 2020-1-4 21:50
厉害,学习了
7#
冷月残星 发表于 2020-1-4 21:52
大神。。。
8#
于皙 发表于 2020-1-4 21:59
大哥厉害,之后买兰博基尼就靠你了。&#128516;
10#
winson365 发表于 2020-1-4 22:09
大神,望尘莫及啊
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-15 09:18

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表