本文破解的apk版本为早期版本
在这次实践当中需要抓取模拟器中应用的数据包,因此选择Fiddler作为我们的抓包工具,其是一款非常流行并且实用的http抓包工具,它的原理是在本机开启了一个http的代理服务器,然后它会转发所有的http请求和响应,因此,它比一般的firebug或者是chrome自带的抓包工具要好用的多。不仅如此,它还可以支持请求重放等一些高级功能。显然它是可以支持对手机应用进行http抓包的。
首先,在Fiddler中设置代理,在tools->options中,如下图所示:
勾选上其中的允许远程主机进行连接。
接下来在模拟器中设置代理主机,点击设置,鼠标左键长按连接的WLAN,出现修改网络,在高级选项中设置代理主机以及端口信息:
至此,即可抓取模拟器中的数据包。但是仅仅通过以上设置,在模拟器中使用https时会出现安全证书错误的提示,这里需要进一步设置https。首先在Fiddler中options中的勾选以下框出的内容:
接下来在模拟器中的浏览器中访问http:ip:8888
并根据提示下载安装证书。图片懒得截了,盗张图。
现在就可以进行下一步的工作了。
将最新版的apk使用adb install安装到模拟器中运行,运行程序,并任意点击一段视频,Fiddler抓取数据包结果如下图:
可以看到,这里并没有出现在优酷视频或者土豆视频中具有明显ad标志的host(在优酷视频中可以通过反编译源码并查找替换该链接为空来去除广告)。我们依次点击抓取到的数据包并查看右边解析出来的内容,发现host为lives.l.qq.com的数据包十分可疑,查看TextView,可以看到以下内容:
其中的链接比较可疑,访问该链接发现确实是一段广告视频,因此该数据包与广告有关。
接下来对apk使用android killer进行反编译,我们尝试直接查找livemsg,思路是将其替换为空串,但是最后却发现并不能找到该链接,因此需要换一个思路,对反编译之后的源码定位关键点。
观察lives.l.qq.com数据包的JSON数据,
JSON中的adList以及item即为apk中需要进行解析的内容,以此来获取广告视频的链接信息,下面在Android Killer中全局搜索关键词item,结果如下:
搜索到的结果中与广告相关的类显然只有smali\com\tencent\ads\data\VideoInfo.smali以及..\AdConfig.smali。
展开第一个类查看其内容,应该是与JSON格式解析相关的Java代码,我们通过Jeb查看对应smali文件转换为Java之后的代码:
不难看到,该代码即为解析JSON的Java代码,方法完整的代码如下:
private AdItem[] parseAdItem(Document arg57) {
Integer v4_5;
ArrayList v4 = XmlParser.getNodeList(arg57, "/root/adList/item[*]");
ArrayList v15 = new ArrayList();
String v16 = "item/order_id";
String v17 = "item/display_code";
String v18 = "item/dsp_name";
String v19 = "item/type";
String v20 = "item/image/vid";
String v21 = "item/duration";
String v22 = "item/link";
String v23 = "item/pass_through_params/horizontal_native_link";
String v24 = "item/pass_through_params/vertical_native_link";
String v25 = "item/reportUrl";
String v26 = "item/ReportTime";
String v27 = "item/image/url[*]";
String v28 = "item/image/width";
String v29 = "item/image/height";
String v30 = "item/image/md5";
String v31 = "item/image/cs";
String v32 = "item/no_click";
String v33 = "item/params";
String v34 = "item/time_list";
String v35 = "item/weight";
HashMap v36 = new HashMap();
SimpleDateFormat v6 = null;
Iterator v37 = v4.iterator();
while(v37.hasNext()) {
Object v5 = v37.next();
AdItem v38 = new AdItem();
String v11 = XmlParser.getNodeTextValue(((Node)v5), v16);
String v39 = XmlParser.getNodeTextValue(((Node)v5), v17);
String v40 = XmlParser.getNodeTextValue(((Node)v5), v18);
String v12 = XmlParser.getNodeTextValue(((Node)v5), v19);
String v41 = XmlParser.getNodeTextValue(((Node)v5), v20);
String v8 = XmlParser.getNodeTextValue(((Node)v5), v21);
String v42 = XmlParser.getNodeTextValue(((Node)v5), v22);
String v43 = XmlParser.getNodeTextValue(((Node)v5), v23);
String v44 = XmlParser.getNodeTextValue(((Node)v5), v24);
String v45 = XmlParser.getNodeTextValue(((Node)v5), v25);
String v4_1 = XmlParser.getNodeTextValue(((Node)v5), v26);
String v9 = XmlParser.getNodeTextValue(((Node)v5), v28);
String v10 = XmlParser.getNodeTextValue(((Node)v5), v29);
String v46 = XmlParser.getNodeTextValue(((Node)v5), v30);
String v14 = XmlParser.getNodeTextValue(((Node)v5), v31);
String v13 = XmlParser.getNodeTextValue(((Node)v5), v32);
String v47 = XmlParser.getNodeTextValue(((Node)v5), v33);
ArrayList v48 = XmlParser.getNodeTextValueList(((Node)v5), v27);
String v49 = XmlParser.getNodeTextValue(((Node)v5), v34);
String v50 = XmlParser.getNodeTextValue(((Node)v5), v35);
String v7 = !Utils.isNumeric(v4_1) ? "0" : v4_1;
if(!Utils.isNumeric(v8)) {
v8 = "0";
}
if(!Utils.isNumeric(v9)) {
v9 = "0";
}
if(!Utils.isNumeric(v10)) {
v10 = "0";
}
if(!Utils.isNumeric(v11)) {
v11 = "1";
}
if(!Utils.isNumeric(v14)) {
v14 = "0";
}
if(v13 == null) {
v13 = "";
}
if(v12 == null) {
v12 = "";
}
if(!TextUtils.isEmpty(((CharSequence)v49))) {
SimpleDateFormat v4_2 = v6 == null ? new SimpleDateFormat("yyyy-MM-dd") : v6;
this.parseDateList(v38, v49, v4_2);
v6 = v4_2;
}
ReportItem[] v4_3 = this.parseReportUrlOther(((Node)v5));
ReportItem[] v49_1 = this.parseReportUrlSdk(((Node)v5));
ReportClickItem[] v51 = this.parseReportClickUrl(((Node)v5));
ReportItem v52 = new ReportItem(v45, Integer.parseInt(v7));
v38.setOid(Long.parseLong(v11));
v38.setType(v12);
v38.setDspName(v40);
v38.setVid(v41);
v38.setFileSize(Long.parseLong(v14));
v38.setDuration(Integer.parseInt(v8));
v38.setClickUrl(v42);
v38.setCanvasHorizontalUrl(v43);
v38.setCanvasVerticalUrl(v44);
v38.setReportItem(v52);
v38.setReportUrlOther(v4_3);
v38.setReportSdkItem(v49_1);
v38.setReportClickItems(v51);
v38.setWidth(Integer.parseInt(v9));
v38.setHeight(Integer.parseInt(v10));
v38.setMd5(v46);
if(Utils.isNumeric(v50)) {
v38.setWeight(Integer.parseInt(v50));
}
v38.setNoClick(v13);
v38.setControlParams(v47);
v38.setUrlList(v48);
Object v4_4 = v36.get(v12);
if(v4_4 == null) {
v4_5 = Integer.valueOf(1);
}
v38.setLcount(v4_5.intValue());
v36.put(v12, Integer.valueOf(v4_5.intValue() + 1));
if("blur_background".equals(v39)) {
v38.setBlurBgAd(true);
}
else if("RichMediaFront".equals(v39)) {
v38.setRichMediaAd(true);
}
else if("VipCommend".equals(v39)) {
v38.setVipCommendAd(true);
}
v38.setAnchorBindingItems(this.parseAnchorBind(((Node)v5)));
v38.setCreativeItems(this.parseCreative(((Node)v5)));
this.handleControlParams(v38, v47);
SLog.v("Lview", "itemList.add " + v38);
v15.add(v38);
}
this.parseAidInfo(arg57);
this.parseVmindInfo(arg57);
this.parseSceneInfoTag(arg57);
this.parseSceneInfo(arg57);
this.adSelector = XmlParser.getNodeTextValue(arg57, "/root/adList/adSelector");
return v15.toArray(new AdItem[v15.size()]);
}
解析的最终结果是存在v38中的,我们可以通过注释掉语句:v15.add(v38);
从而丢弃解析之后的内容达到目的。
在Android killer中对smali源码进行修改:
重新进行编译、签名安装,到这一步基本上就完成了。
其实在实际操作中可以尝试直接使用bakesmali.jar反编译我们定位到的dex文件,然后修改其中的smali源码,并使用smali.jar工具重新编译为dex文件,将dex文件移入.apk文件直接进行替换,这样程序也能够正常的运行。