android逆向奇技淫巧第二篇介绍了通过method profiling追踪找到了点赞的方法,于时就用jadx打开,找到了onclick的代码: com.ss.android.ugc.aweme.feed.quick.presenter.cb类下面的onclick方法:混淆非常严重,只能看懂java语法,至于代码的业务意义大部分是不好理解的;单从部分未混淆的方法和变量名看,点击后会先判断是否登陆,如果没登陆就弹出登陆界面;如果登陆了就show各种toast;具体是哪行代码向服务器发送数据了?静态分析完全看不出来!
public final void onClick(View view) { String string; boolean z = false; if (!PatchProxy.proxy(new Object[]{view}, this, f171628a, false, 251117).isSupported) { ClickAgent.onClick(view); cb.this.f172363h.a("VIDEO_CANCEL_REPORT_SKIP_BEHAVIOR", cb.this.f172358c.getAid()); int b2 = m.b(); if (b2 == 0) { z = !AccountProxyService.userService().isLogin(); } else if (b2 > 0 && !AccountProxyService.userService().isLogin() && !cb.this.l && cb.this.f172358c.getUserDigg() == 0 && bh.b() >= b2) { z = true; } if (z) { new z().a(cb.this.f172359d).a(cb.this.f172361f).b(cb.this.f172358c.getAid()).f(cb.this.f172358c).c("click_like").c(!bh.d() ? 1 : 0).d(1).e(); if (!bh.d()) { bh.c(); } String str = ""; if (TextUtils.equals(cb.this.f172359d, e.c.f229429a)) { string = str; } else { string = cb.this.m.getString(C4212R.string.f4m); } if (cb.this.f172358c != null) { str = cb.this.f172358c.getAid(); } if (a.h().isTeenModeON()) { AccountProxyService.showLogin((Activity) cb.this.m, cb.this.f172359d, "click_like", av.a().a("login_title", string).a("group_id", str).a("log_pb", ae.k(str)).f262690b, null); return; } AccountProxyService.showLogin((Activity) cb.this.m, cb.this.f172359d, "click_like", av.a().a("login_title", string).a("group_id", str).a("log_pb", ae.k(str)).f262690b, new cd(this, view)); } else if (com.ss.android.ugc.aweme.login.utils.a.a(cb.this.f172358c) && cb.this.f172358c.getUserDigg() == 0) { DmtToast.makeNegativeToast(cb.this.m, com.ss.android.ugc.aweme.login.utils.a.a(cb.this.f172358c, C4212R.string.n0t)).show(); } else if (cb.this.f172358c.isCanPlay() || cb.this.f172358c.getUserDigg() != 0) { if (cb.this.f172358c.isDelete() && cb.this.f172358c.getUserDigg() == 0) { DmtToast.makeNegativeToast(cb.this.m, (int) C4212R.string.n0t).show(); } else if (cb.this.f172358c.getVideoControl() != null && cb.this.f172358c.getVideoControl().timerStatus == 0) { DmtToast.makeNeutralToast(cb.this.m, (int) C4212R.string.mza).show(); } else if (ac.f262626b.d(cb.this.f172358c)) { DmtToast.makeNeutralToast(cb.this.m, (int) C4212R.string.h6t).show(); } else if (cb.this.l || !ac.f262626b.c(cb.this.f172358c) || !ac.f262626b.a(cb.this.f172358c)) { ((DiggAnimationView) cb.this.G.a(C4212R.id.byb).a()).a(view); if (!NetworkUtils.isNetworkAvailable(cb.this.m)) { DmtToast.makeNegativeToast(cb.this.m, (int) C4212R.string.f288170d).show(); return; } cb cbVar = cb.this; cbVar.a(cbVar.f172358c); } else { DmtToast.makeNeutralToast(cb.this.m, (int) C4212R.string.n5u).show(); } } else if (cb.this.f172358c.isImage()) { DmtToast.makeNegativeToast(cb.this.m, (int) C4212R.string.evm).show(); } else { DmtToast.makeNegativeToast(cb.this.m, (int) C4212R.string.n0t).show(); } } }
既然静态分析看不出,要不试试动态调试?这种想法理论上是可行的,但分析这里的点赞代码还是困难:(1)jeb或as调试,只能调试smali代码,无法调试java代码 (2)java代码就是smali反编译而来的,所以smali也是混淆的;如果不理解代码的业务目的,单步执行代码有啥意义了?(3)smali代码远比java多,动态逐行调试耗时也很多;怎么办了?
计算机网络发展到今天,已经找不到单机的app了! 客户端安装的app做的所有工作最终目的都是为了和服务器通信(就像打工人,每天干的所有活的最终目的都是为了挣钱、养家糊口);前面说过了,逆向破解一般都是从数据开始的。数据可以通过CE或其他软件搜索内存,也可以先抓网络封包,看看客户端给服务器发送了啥,然后根据数据的字段在代码里面找,看看这些字段的值都是怎么生成的!这里逆向android应用,所以先用fiddler抓封包!
这个app会给服务器发送很多数据包,哪个才是点赞的数据包了?这里介绍一个小技巧:这个软件通过发给服务器的host是aweme.snssdk.com,可以先用fiddler过滤一下,只选这个服务器;很快,就能抓到点赞的封包(个人很好奇,为啥点赞接口取名叫digg了?好奇怪的名字):
GET https://aweme.snssdk.com/aweme/v1/commit/item/digg/?aweme_id=6956180208793718055&type=1&channel_id=-1&city=510100&activity=0&os_api=22&device_type=M973Q&ssmix=a&manifest_version_code=150501&dpi=480&uuid=865166023654745&app_name=aweme&version_name=15.5.0&ts=1620296753&cpu_support64=false&app_type=normal&appTheme=light&ac=wifi&host_abi=armeabi-v7a&update_version_code=15509900&channel=xiaomi&_rticket=1620296754623&device_platform=android&iid=2744832902828125&version_code=150500&cdid=7addad62-d097-41da-b852-e0de8b24fe75&openudid=338803367b93a120&device_id=70039083151&resolution=1080*1920&os_version=5.1.1&language=zh&device_brand=Meizu&aid=1128&minor_status=0&mcc_mnc=46000 HTTP/1.1 Accept-Encoding: gzip x-tt-dt: AAA7NE2KX2XRH5ZIBMIIJHUGNWEXQLPT2G5U2VKAPRSBVVGBNOLZGYT36N7VMCWTDFTQBQXBW6FSC2IWBD36QSIH6MSUI2SKDMHF4T27M5MS6TL2XHUCXVFWWDJM2Y3HQZEHXQ42UCB7JUEHURQRH6Y passport-sdk-version: 18 X-Tt-Token: 000e8f473aea647b3c44fd3b9f7482439e02da23d858b1609a77f5edbf3bcc6c774f39175ea27eed09ffb2dee02c173fcc137b53019bb3614db48a84e88879cf2cabc8a810d38b88fb1d9bc7640b02e655a186f8b1e0daf197e60067b35a68adc68ad-1.0.1 sdk-version: 2 X-SS-REQ-TICKET: 1620296754625 Cookie: passport_csrf_token_default=f4442957bc07adf1986f4df139296dd7; install_id=2744832902828125; ttreq=1$184784690dc292ea1ad4e5db65553166a5674c3e; multi_sids=95063141447%3A0e8f473aea647b3c44fd3b9f7482439e; odin_tt=fac32cc300e49d8f22a70f1cd7b92569039d7305b6fdef7f2389cb98e0f9e9493f6c8374ad33dabed24ea41c55e1834e; n_mh=B6WRe0yd-1qIuffF6ZWNO-CSGlW1Q-VhC0E79NrqYTg; sid_guard=0e8f473aea647b3c44fd3b9f7482439e%7C1620296607%7C5184000%7CMon%2C+05-Jul-2021+10%3A23%3A27+GMT; uid_tt=d06498fc7329b7187e209d0e6279e1d7; sid_tt=0e8f473aea647b3c44fd3b9f7482439e; sessionid=0e8f473aea647b3c44fd3b9f7482439e; X-Ladon: gW1IO3ulq0eYymnbYa+7nAu2l116ADdAdmSIA1eB3Cv5BBIo X-Khronos: 1620296754 X-Gorgon: 0404401f40051f42c987ec09aebf8038d629adf2add584933f8f X-Tyhon: s2S+nKZ8jrepKJ2VtCulu7carp/ZLqe2gn24aHA= X-Argus: Uictv+neuH8ZqjOjXzvEIvXCEXYEgUy3dUKWzj08JUtmGeYa4HfNfN8bp7Yga22Jbik2N2dBKezA48YN1E9A1KaBjXi2ixu5cHzAkQU9Tl4f/+a9xWuLYIZ/+PkW/YXUsmRCfszWlcMePPeyNQYEGkb5FssTkIr3EZ87TJ9NLBDJCJGA0i4PICbLnClzdfmBs+57JVi1sU2/MELCr9gO/Nka5eIJsEoGB/CIaL3gPmEbZ0sUl7sxIvKksMwj3f7tYBCriYBKmfeREuiYf1S18c7i Host: aweme.snssdk.com Connection: Keep-Alive User-Agent: okhttp/3.10.0.1
服务器返回字段:每次点赞成功后,status_code会是0;
逆向的目的是重放,简单理解就是自己构造数据包模拟用户真实的点赞行为,所以这里也要搞清楚每个字段的含义,这些字段是怎么生成的,后续才能自己写代码实现;这里面的字段有很多,哪些字段可以直接写死复用,哪些字段需要我们重新用算法生成了?这里继续在app点赞,再抓一个digg数据包,然后把两个包对比,就知道哪些字段可以写死复用,哪些字段要动态生成了,对比如下:第一行是url,参数不同,待会单独分析;接着是X-SS-REQ-TICKET和X-Khronos,这两个明显是毫秒和秒级别的时间戳,生成很简单;还剩X-Ladon、X-GORGON、X-Tyhon、X-Argus4个字段不同。从这个4个字段的取值看,明显不是人能读懂的数据,这个就需要分析代码了!
回过头来再看看url的参数对比,如下:第一个不一样的地方就是aweme_id,明显是被点赞短视频的id;第二个是ts、第三个是_rticket都是时间戳,所以url这里也没啥需要额外分析代码的,比较简单!注意:url里面有个type字段,等于1表示点赞,等于0表示取消点赞!
综上所述:要自己写代码模拟实现真实的点赞行为,需要生成4个关键的字段:X-Ladon、X-GORGON、X-Tyhon、X-Argus;这4个字段都是怎么生成的了? 下一步就需要分析java个so层的代码了!
最后有一点疑惑:这个app为啥不学微信(在tls1.3的基础上打造mmtls协议了)自定义通信协议了? 目前用的还是https协议,很容易被fiddler抓包的!微信的mmtls协议通信,用的就不是https协议,fidder就无法解析,保障了数据安全!
该APP版本:15.5.0