python 您所在的位置:网站首页 蚂蚁加速度器 python

python

2023-08-17 07:51| 来源: 网络整理| 查看: 265

0x00 工具准备Fiddler(代理抓包)

Xposed(hook框架)

Inspeckage(通用hook插件)CryptoFucker/算法助手(常见加密算法hook插件,用途同上)反射大师(APP脱壳插件)HEX编码转换/数据加解密工具(测试加解密与结果对照)MT管理器/NP管理器(脱壳后dex修复)GDA(反编译工具)工具打包下载:https://lanzoui.com/b0eknupng 密码:4vpf各工具使用方法介绍等详细内容可参考各自文档或。0x01 发现问题

目标APP:蚂蚁加速器(不提供下载)

由于目标APP没有公开下载渠道,接口域名也无法直接访问,所以全文未打码。

打开Fiddler,模拟器中设置好代理,安装证书,打开APP,具体步骤自行百度。

Fiddler中可以看到,所有请求接口发送的数据都是差不多的,看不出发送和返回的数据是什么内容,这次的目标就是将其转为可读的明文,也就是其中的data和sign参数生成、返回数据的解密。

0x02 用hook插件简单分析

需提前安装Xposed框架。

对简单的APP,如果只用了很常用的标准加密算法(如明文字符串拼接后取MD5、使用明文key进行对称加密),那只要用Inspeckage、CryptoFucker或算法助手其中一个就可以解决了,这三个工具都是用来hook常用加密算法的,如SHA、MD5、AES、DES等,详见各自文档。

三个工具大同小异,本次测试过程中发现的优缺点如下:

Inspeckage

优点各参数会base64编码,不会导致显示乱码。

功能很多,加密hook只是其中之一。

缺点使用方法比其他两个麻烦一点。AES的IV参数有时候会漏掉。有时不显示加密模式。sha-256字符串显示错误。

CryptoFucker

优点会显示HEX编码和解码后内容,不会导致只显示乱码。加密模式全部能显示。

开源,可以自己修改源码。

缺点所有加解密都放在同一个文件,看着很乱。AES的IV参数有时候会漏掉。

算法助手

优点AES的IV参数不会漏掉,加密模式全部能显示。

加解密参数一一对应,浏览方便。

缺点参数显示乱码。

多个工具可以同时开启。配置好一个或多个hook插件后,打开目标APP,随意切换两个界面后,可以在查看各工具结果。本次测试以算法助手和Inspeckage为例。

从Inspeckage的Crypto结果中一部分json内容可以确定加密算法是AES,key是tJb4MRKOT+HeM/S3osRuDdR3JSSkgm/kSG/MB+PiuH8=(base64编码),没有拿到iv,解密模式是AES/CFB/NoPadding,加密模式应该是一样的。

从Inspeckage的Hash结果中可以看到sign的生成是SHA-256后再取MD5,但是在测试SHA-256加密时结果对不上,暂放。

从算法助手结果中可以看到hook到多种类型加密,点进AES,可以看到key和iv都是乱码,也就是说这两个参数都是不可见字符,不过可以确定加密模式是AES/CFB/NoPadding。

如果是简单点的APP(像前面说的,明文字符串拼接后取MD5、使用明文key进行对称加密),这个时候可能就结束了,直接拿到明文key、iv以及MD5拼接字符串模板,直接去写代码就完事了。

0x03 脱壳

上面用hook插件得到的结果:

请求data加密和返回数据解密模式为AES/CFB/NoPadding。key是tJb4MRKOT+HeM/S3osRuDdR3JSSkgm/kSG/MB+PiuH8=(base64编码,可转为HEX编码)。sign生成是SHA-256后再取MD5,但是SHA-256结果对不上。

由于没有拿到iv、SHA-256结果对不上,此时需要进一步反编译分析。

GDA是一个国产免费安卓反编译工具,只有一个几兆的可执行文件,不用配置环境,反编译速度极快,功能比较强大,整体使用体验较好。这里没有选择一些老牌反编译工具,只用GDA就够了。

直接用GDA打开APK,发现提示有360加固,需要脱壳。

GDA中也有拖dex的功能,不过没弄明白怎么用,所以还是用反射大师来无脑拖dex(步骤:Xposed启用模块/反射大师内选择目标APP并打开/点击六芒星图标/点击当前Activity/长按写出DEX)。

此时拿到两个dex,第一个文件比较大,第二个很小,尝试用GDA打开,发现第一个dex无法反编译,而第二个dex内搜不到什么有用信息,所以要用MT管理器或NP管理器修复第一个dex,再用GDA打开。

0x04 用GDA反编译分析

GDA使用比较方便,上方一排工具栏很明确,还有快捷键清单,上手教程见https://zhuanlan.zhihu.com/p/...。

因为前面确定了加密模式为AES/CFB/NoPadding,所以GDA反编译完成后,直接全局搜索(快捷键S)字符串AES/CFB/NoPadding或CFB来定位加密点(也可搜其他字符串/类名/方法名/变量名,一般从请求接口中来寻找搜索的字符串,可以多尝试)。

搜索结果只有两个,双击进入,发现没有混淆,应该比较简单。这里直接定位到了加密、解密的方法中,左侧看方法列表,可以大致确定算法就是这个了。

先稍微读一下Java代码,不会的函数就百度,弄懂这几句还是比较简单的。

public static String decrypt(String p0,String p1){ // p0是明文key,p1是返回数据包的16进制字符串data值(交叉引用查看) String str = "UTF-8"; byte[] obyteArray = null; try{ // 待解密data值16进制字符串转字节数组 byte[] bhex2byte = Cfb_256crypt.hex2byte(p1); // 初始化实例,加密模式为"AES/CFB/NoPadding" Cipher cInstance = Cipher.getInstance("AES/CFB/NoPadding"); // AES实例参数初始化,key由明文经过EVP_BytesToKey算法生成,iv为data值的前16字节 cInstance.init(2, new SecretKeySpec(Cfb_256crypt.EVP_BytesToKey(32, 16, obyteArray, p0.getBytes(str), 0)[0], "AES"), new IvParameterSpec(Arrays.copyOfRange(bhex2byte, 0, 16))); // 待解密数据为为data值16字节以后的数据 return new String(cInstance.doFinal(Arrays.copyOfRange(bhex2byte, 16, bhex2byte.length)), str); }catch(java.lang.Exception e6){ e6.printStackTrace(); return obyteArray; } } public static String encrypt(String p0,String p1){ // p0是明文key,p1是待加密数据(交叉引用查看) String str = "UTF-8"; byte[] obyteArray = null; try{ // 初始化实例,加密模式为"AES/CFB/NoPadding" Cipher cInstance = Cipher.getInstance("AES/CFB/NoPadding"); // AES实例参数初始化,key由明文经过EVP_BytesToKey算法生成,iv未指定,为随机生成 cInstance.init(1, new SecretKeySpec(Cfb_256crypt.EVP_BytesToKey(32, 16, obyteArray, p0.getBytes(str), 0)[0], "AES")); // 随机生成的iv+加密后的数据,合并后转为16进制字符串,发送给服务器 return Cfb_256crypt.byte2hex(Cfb_256crypt.byteMerger(cInstance.getIV(), cInstance.doFinal(p1.getBytes(str)))); }catch(java.lang.Exception e6){ e6.printStackTrace(); return obyteArray; } }

光标放在encryp/decrypt方法上,可以查看是哪里调用的(即交叉引用,快捷键X),直接定位到了请求接口的封装类,顺便直接拿到了key的明文。

同时在左边发现了sign方法,进去看一下发现果然是接口中的sign(也可以搜索字符来定位,比如搜索前面看到的appVersion=2.1.8)

public static String sign(String p0,String p1){ // p0是data值,p1是时间戳 return MD5Util.getMD5(Cfb_256crypt.getSHA256StrJava(new StringBuilder()+"appId=android&appVersion=2.1.8&data="+p0+"×tamp="+p1+"2d5f22520633cfd5c44bacc1634a93f2")); }

分析部分到这里就可以结束了,有key(明文fjeldkb4438b1eb36b7e244b37dhg03j传入EVP_BytesToKey生成,HEX编码为B496F831128E4FE1DE33F4B7A2C46E0DD4772524A4826FE4486FCC07E3E2B87F)、有iv(加密iv随机生成,解密iv是数据包前16字节,即前32位字符),可以直接调用AES算法,有SHA-256字符串拼接模板,可以直接拼接调用SHA-256和MD5算法。

注:1byte(字节)=8bit=8位二进制=2位16进制(2的4次方=16,即每4位2进制等于1位16进制)0x05 验证结果

用数据加解密工具来验证一下,注意key是由算法生成的,base64解码后是乱码,所以这里只能填hex编码转换后的了(工具有网页在线版,GDA内也有,这里用的是Fiddler的一个插件,因为其他的基本不能使用HEX)。

// 发送数据 appId=android&appVersion=2.1.8×tamp=1621007748&data=B5A74CCEA048E1AE91FB4C5BD5A6FA27CCD86F24ABD094763247C15100FA2129469EF559AEA2086A0FA28E78910180580993AE35CCDEBA6C7AD1E07E2508AF9307AD2179B3068D63B2B9260B91E0E79BAE26B36F7CD1824496EB26326AAA76831872EE49998292CDC0D2D7B011C340558D384F01212E9C335BD4CB337E2D72974748B7586050BDB45708930E7512F9AE88BD33FCCFD5257DAFB13D7F5766F5000AA3968DF5ED8434F27384226AE111343DB670A7C7014FD5BB96898E5621E1E0&sign=656c9fb2f0d00b9fcf3fa2d38fa2488d // 返回数据 {"errcode":0,"timestamp":1621007686,"data":"645328E9B5A19FDCC309EE6067BB3F56E04D05060C268D8B8F0CC850C2DD14117158D6C28D2EAB5C5E27C10690FBAE7729CDE74A20A48CC59BE6FD6C6533DB6D3D120AB160B46A594324F65C2135EB5C9C00FF1EE0CE5682DD62EECDD8BD2E0697DD35AC49DC2735F16C878A46B4C810D1A850A5A80FA85F02833752F7224B9460340B62D20CD1E177CCA878463FA7F76FB0798B2E35B0B75EE1580E0515115670C3E1F504E34F268767BE9A32601D29538724EA6CDDE3FD1D16C605C83A2C30E0AF1F05F6ABEC631CABB3491990FD0623B91466D2E36F4806F7549E839ACF21485AED3EEB768753AF952ED52399A38B1FD4FC42319CA83452F8B76F62B46CEB64E8BB78D3CF28E8A75C045C2D18DE595046584EB37CD3A8FE15831807827ECABB028A3C77334C1FF726B5B075087AE2908A0308188A0D21D604EB11CE00D85FFF8F70C3AF2F4339463D1A93782EB4A7A0AEF880B024419846B207015B4A541E9A57D8F0B0E7FCE055C82DAF90720106862C5DC6F11471E86347AC2B17BC9ED3C60BB9C29043FF838F71F9E8CFA8F9579CF1CFA821F8388A5CE3868C6AE6992FB6D69AD85B8672AE682AEE9A5BD8F2D86640B7167B26BC3B67493B21FAD7D5FA6D670082F9E669B1FDC02FA6007E440453B5FDD0193BB99494B33786407883DFCD881E076205EF8929BD33CEA2357A3F1F02EAFF20FD3D2449011E6A728FE02F4C7109A27A066631D238F5B84BC75977201D36ABDE343D6C7C626987AE66232BB918870FF6F35C4EEEFE5D728D4069B89D6F0099E92F3B1E1D13A4BE9D5DF9EF66B35223105EAC37EEB15E37F7CA77533FCEBF9329983D625BCE2B4690E87EDEAEFC0C1AF145474435A8D322781FFCCE13113189764A65079D281B2FC7066AC5201AA2C191A076C9C55B89E198C1FAEAD690CF4D4F1D441A926EAEBAFC2F6ED1ECECF8571F9C60CBBD0A509EF07CDB3492F9DAB27F6EBE397CA1DB558B8C28A7C6517AEAEA0E57062702D27B4B217A862C80211E92A4B436991707120782E859242A2A95746A5514F0EC1FFFA409300A92D95A3D6B8496EEC86126EF98159AD56A5F45CCE9D0AB7AF582B0B80AB2DC65141B777307E8CA47418F9546C353E422688AB3F3F53C12E10277F8ABAC09ED4E6B3A44C90740A4147488E647E9D7C9105DF3B9D104872112380CC9D46B563ADAB09C9D815FE310084FEBFA253F8A728D3F0CB64CEA1E99FD0EDE2C7FABB7A9C915142E2B6E110C046D838019008B98A66F4CC0142460105C42C407287E629D5A77A1EFBD6CD212BFD1E8D2AC2CB446FF54C8ED111D14D8C6BF060AC9B623154D2793DC4893176C89D25B22BE39231CB3C2915803F76DEA27D828BB95B32E42439415231AC3BEF11B510061F03C60F8DBEB13941C1E3368EC13445A82EC1360EC7FEE82E260F31D1D3D6DA1E59C1ECA4FCF03DCCC7FA787DCD779662F60CE8310021B7A7A2090F9499FE96F6F1A165B4171049220DF2CA60F1A57F7A7EED72F711CB1B1BD823765A2843AB1271BBD452498DBF9614E0E2FBAC4D3222560461259EC2BA22A054B5AAA74715D11C93253F9D27C3A2B530DD3421E5A4809E17F361E05F1BB875EDFA7614743BA1B940934057E0398867992D6F47F7C713C2C927231EDA322118E37C162E3640A914CC13B194502ED8461591946A0F0B2E26D332E27F677B5986C94B06FD5C8ED8B9C0C7CA2AF3E13F00101E83D0C71370626A89D783253CBA409786C8CF1D3AF4AEE2BFC1E47185F37D2A6DAF25AD0BC2D0C60FFE8F2F8BDE3CA61A6D3841A3936B0B8B3EC0F7D2F6F524D4EBF07195AEF8625781A758D47190F14B264ABFD36D9D7151E036D770001F0F4B0E14C6E5AB1D3A69EA9414006B3C8B4AC5F94D4B481DD82CD58DEBFD00896A9266157B31B18C6682ED30119B75BD74B23242BEE09E5E35E3A8721B6CECB2349611858501CEFD8B10335AD8883A85FECDA647379E6F5CB5F7E9C211EE38F4562AF19E79904FE3CCFC7D912B43563EE20ABA37BBD5E0971887511AA2A3218FF12321B3AAD5CA8635FB38A2D59D0FAF03A04747B7DC06E45EB447161099173B0B31B1FCE03B5496797B261183FB6E1C467B64A02B44A78680D7A9DEC731A5A4DE89300F99C8A850D806E13E596A7882D140409E3EA30BD87584BE35594F89109C7F95F30D92B8CDC589DAAF121C5604FD1243911A135336ED7C97CFD0EE1CBDE5997DA3778C72827327FD38556AC417E0C1F4BBD82AC34D8F742C211C472411EA4C16C37176B973419CF85FBCFAFA400D916350ED6BFBD4F5DB5695865010BFB6B4D39DA6DCC4346DA8E2D186E5E633E7B4D57044373105590F8507D897F508604568B055EBF86023A2B273A765104CE1A616BFAAF31560EA96169255644D1A57EC91DBC07A2029FFABFCD910458192C30D1506D79BC383991A2FCB65029CECDE540E70E2734F18DF04A71616A6229BCE80082AA9114C7670F1A126AEFFB30375B99840A65792631106211A352AFCB54FF2EA687D771709FECCA08FF6254FBED743F78E9528D7255AF87EE6276C658D67CBD788460526896DFEE9CCF57EF23F5FC116CFD59414465FA05094AEA80D4687459982C12C5B98BF8A3DF38F13E24F7DAB4D85E7FB4910C0992B9E14285E2CDE40C619244A4ACC1A261A5883C4E504FF549EC43776964ABFF10016D380919CD2AB62FAF312412702E1C9075941BD5791B756B951C539A86871D92C36CA033BF41EF31B21EE35582947B20AA1D00A65A6290E9FAC14CD21B11DE0BB6CDFB906B28F859FBCD9AC2588C5CCD0D06A49DC6DC80603EDE4F6CF709EF7BD4D5F79968664C502282B51F78F1E61FCBB10FC7D7E2747AADA81397C459772936925048E689EF4E681C75F3D6222DA8DC90","sign":"9a0ab084be6f0bc9415b0c204f7fdee5"}

将发送包或返回包data数据分割为两部分,前32位是iv,后面是待解密数据(需要将待解密数据HEX转为base64编码)。

sign字符串拼接模板为"appId=android&appVersion=2.1.8&data="+p0+"×tamp="+p1+"2d5f22520633cfd5c44bacc1634a93f2,其中p0是data值,p1是时间戳,经SHA-256和MD5加密后结果正确。

0x06 刷邀请

数据包解密完成,就可以写代码了。

这时会意识到,抓包不能看到明文,那怎么写代码?答案还是对照着Inspeckage等工具查看数据明文。当然也可以是手动hook刚才定位到的加解密方法(Inspeckage中有自定义hook,但测试无效,估计要重新写个xp插件或者找其他工具替代),实现自动输出数据包明文。再或者根据源码中封装请求包的格式去手动还原。

Python代码# -*- encoding: utf-8 -*- ''' @File : main.py @Time : 2021年05月16日 15:55:51 星期天 @Author : erma0 @Version : 1.0 @Link : https://erma0.cn @Desc : 蚂蚁加速器刷邀请 ''' import requests import time import json from base64 import b64decode from hashlib import sha256, md5 from Crypto.Cipher import AES from Crypto.Random import get_random_bytes # from Crypto.Hash import SHA256, MD5 # 和hashlib库一样 class Ant(object): """ 蚂蚁加速器刷邀请 """ def __init__(self, aff): self.aff = aff self.oauth_id = '' self.timestamp = '' self.url = 'http://ant.hyysapi.com/api.php' self.headers = { # 加不加header都可以 # 'User-Agent': # 'Mozilla/5.0 (Linux; U; Android 7.1.2; zh-cn; E6533 Build/N2G48H) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30' } # 明文key,再经EVP_BytesToKey方法生成最终key,最终HEX为:B496F831128E4FE1DE33F4B7A2C46E0DD4772524A4826FE4486FCC07E3E2B87F self.key = 'fjeldkb4438b1eb36b7e244b37dhg03j' # 没发现哪个加密库中有EVP_BytesToKey算法 self.hexkey = 'B496F831128E4FE1DE33F4B7A2C46E0DD4772524A4826FE4486FCC07E3E2B87F' self.b64key = 'tJb4MRKOT+HeM/S3osRuDdR3JSSkgm/kSG/MB+PiuH8=' @staticmethod def get_timestamp(long=10): """ 取时间戳,默认10位 """ return str(time.time_ns())[:long] def decrypt(self, data: str): """ aes解密 """ ct_iv = bytes.fromhex(data[:32]) ct_bytes = bytes.fromhex(data[32:]) ciper = AES.new( b64decode(self.b64key), AES.MODE_CFB, iv=ct_iv, segment_size=128) # CFB模式,iv指定,块大小为128(默认为8,需填8的倍数,貌似AES标准区块大小就是128,和密钥大小128/192/256无关) plaintext = ciper.decrypt(ct_bytes) return plaintext.decode() def encrypt(self, data: str): """ aes加密 """ # cipher = AES.new(bytes.fromhex(self.hexkey), AES.MODE_CFB) cipher = AES.new(b64decode(self.b64key), AES.MODE_CFB, segment_size=128) # CFB模式,iv自动随机,块大小为128 ct_bytes = cipher.iv + cipher.encrypt(data.encode()) # iv+加密结果合并 return ct_bytes.hex().upper() # hex编码 def get_sign(self): """ 生成sign """ template = 'appId=android&appVersion=2.1.8&data={}×tamp={}2d5f22520633cfd5c44bacc1634a93f2'.format( self.encrypt_data, self.timestamp) # sha256 sha = sha256() sha.update(template.encode()) res = sha.hexdigest() # nd5 m = md5() m.update(res.encode()) res = m.hexdigest() return res def request(self, d): """ 请求封包 """ plaintext = {"version": "2.4.3", "app_type": "ss_proxy", "language": 0, "bundleId": "com.dd.antss"} d.update(plaintext) self.timestamp = self.get_timestamp(10) self.encrypt_data = self.encrypt(json.dumps(d, separators=(',', ':'))) sign = self.get_sign() data = { "appId": "android", "appVersion": "2.1.8", "timestamp": self.timestamp, "data": self.encrypt_data, "sign": sign } res = requests.post(url=self.url, data=data, headers=self.headers) resj = res.json() res = self.decrypt(resj.get('data')) print(res) return res def get_user(self): """ 生成新用户 """ # 取随机md5 m = md5() m.update(get_random_bytes(16)) oauth_id = m.hexdigest() data = {"oauth_id": oauth_id, "oauth_type": "android", "mod": "user", "code": "up_sign"} self.request(data) self.oauth_id = oauth_id print(oauth_id) def invite(self): """ 刷邀请,邀请码:self.aff """ self.get_user() data = { "oauth_id": self.oauth_id, "oauth_type": "android", "aff": self.aff, "mod": "user", "code": "exchangeAFF" } self.request(data) if __name__ == "__main__": ant = Ant('a6aVx') ant.invite()


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有