转自:

<a href="http://www.pedant.cn/2014/07/22/crack-a-draw-app/">http://www.pedant.cn/2014/07/22/crack-a-draw-app/</a>

  

东窗事发

偶然见到一个应用内有抽奖的活动(应用具体名称就不便告知),而且是每天都可以抽。同时抽奖之前也不需要用户登录什么的,但限定了用户一天(自然天)只能抽奖一次。那么应用的服务端在用户没有登录的情况下是依据什么来判定当前用户今天是否已抽过奖了呢?这当中判断的依据是否可靠,能否被伪造然后实现一天多次抽奖呢?带着这些问题,让我们来剥开应用的层层外纱。

初识庐山

首先在PC端打开LogCat,然后手机连接PC,最后手机上打开该应用的抽奖界面。这时我们在LogCat中发现了该应用输出的如下日志:

里面有onPageStart并且包含了被加载网页的url。由此可以得出结论,抽奖页是用WebView加载网页实现的。那加载这个WebView的Activity又是什么呢?

在PC端打开Android DDMS,在进程列表中找到该应用包对应的进程(如下图的进程ID为21641),然后查看Activity Manager State。

这时我们得到的Activity Stack就像这样。

由此我们就可以大概知道它Java内部的实现结构是怎样的了。首先创建的是ActivityToolsExplorer,然后在其中嵌入了一个名为FragmentToolsExplorer的Fragment,最后由FragmentToolsExplorer在onCreate里实现WebView加载网页的过程。

顺水推舟

由上面我们知道了抽奖页的Url。好了,我们首先在浏览器上打开这个Url,看看它的抽奖是怎样一步步实现的。

这个页面打开后,在页面正中间有一个用来作抽奖操作的按钮,嵌套样式名为.lucky-btn .lucky-cell-conn。当然在浏览器上这个程序是运行不起来的,我们关心的只是开始抽奖这个动作是怎样发起的。查看源码打开main.js。js文件竟然没有压缩,就更方便我们逆向跟踪了。我们找到对这个按钮绑定点击事件的位置:

main.js

function init(info) {
...
var deviceID=info.deviceId;
checkDrawEnable(drawingObject,deviceID);
$(".lucky-btn .lucky-cell-conn").on("click",function(e){
...
window.setTimeout(function(){
..
getJSONP(BASE_URL+"draw/"+deviceID,function(data){
drawResult(drawingObject,data);
});
},Math.random()*1000+1000);
}); }

  

ok,在这里我们就直接发现了,服务端用来判断用户当天已抽奖的依据是DeviceId(设备ID)。现在我们需要的是一步步逆向跟踪,找到是谁调用了init函数并带入的info参数是怎样构成的,这样似乎整个程序逻辑就会变得清晰起来。

这个网页中bridge对象为java层注入的对象,而脚本中一些初始化信息也是通过调用java方法获得的。那么我们要跟踪整个代码流程,就必须对java代码也进行逆向分析。首先拿到应用的apk包,unzip后使用baksmali将classes.dex解为smali代码包。结合脚本代码,对代码调用层次一步步追踪,这个过程需要你对smali代码也比较熟悉。最后得到如下图所示的调用流程图(具体追踪过程就不在这展开):

由此,我们就知道deviceId串是主要就由Lcom/xx/util/e;->a(Landroid/content/Context;)Ljava/lang/String;方法负责生成。这个方法中获取到AndroidID与DeviceId后用”_”符连接。

Lcom/xx/util/e;->j(Landroid/content/Context;)Ljava/lang/String;就是获取DeviceId的方法,具体的smali代码为:

Lcom/xx/util/e;->j(Landroid/content/Context;)Ljava/lang/String;

method public static j(Landroid/content/Context;)Ljava/lang/String;
.registers 2 const-string v0, "phone" invoke-virtual {p0, v0}, Landroid/content/Context;->getSystemService(Ljava/lang/String;)Ljava/lang/Object; move-result-object v0 check-cast v0, Landroid/telephony/TelephonyManager; invoke-virtual {v0}, Landroid/telephony/TelephonyManager;->getDeviceId()Ljava/lang/String; move-result-object v0 return-object v0
.end method

  


偷梁换柱
至此找到获取DeviceId真正的位置,我们就破案了。

上面我们知道了,服务端用来判断用户当天已抽奖的依据是DeviceId,同时我们也追踪到了代码中获取DeviceId具体位置。这样,程序每次获取DeviceId时我们就可以伪造返回值(每次伪造的结果都不能相同),就实现一天无限次抽奖了。那怎样伪造这样的返回值呢,下面的简单一句Java代码就可以实现了。

1
return String.valueOf(System.currentTimeMillis() ^ 537400335373457L);

把上面这句翻译成smali代码后,替换掉j(Landroid/content/Context;)Ljava/lang/String;方法内原来的全部smali代码,偷梁换柱。

registers 9

invoke-static {}, Ljava/lang/System;->currentTimeMillis()J

move-result-wide v0

const-wide v2, 0x1e8c344179491L

xor-long/2addr v0, v2

invoke-static {v0, v1}, Ljava/lang/String;->valueOf(J)Ljava/lang/String;

move-result-object v0

return-object v0

  


魔高一尺,道高一丈

最后用smali.jar打包回去,签名后安装。运行起来后应用在抽奖页面已经没有了次数限制。

学习破解的目的不是为了走更多的捷径。有个道理大家都知道,最好的防守就是进攻。我们只有了解怎样去破解一个应用,才知道应该如何去加固自己的应用。就比如这个应用中,有下面这些问题可以改进来加大各方面破解的难度:

  • Debug日志不应该存在于发布版本,Error日志也尽量少暴露敏感信息。
  • 线上网页的脚本代码及样式应该压缩,压缩后可以增加逆向时阅读及追踪上的难度。
  • 不能以客户端上任何固有参数作为服务端上的关键凭据,比如这里的抽奖凭据依赖了客户端的DeviceID。
  • Java代码混淆还是需要的,虽然代码仍可以被一步步逆向跟踪,但语义上的隔离会加大追踪的时间成本。

Android 一个抽奖应用的逆向破解全流程之加固自己应用的更多相关文章

  1. Android逆向破解表单注册程序

    Android逆向破解表单注册程序 Android开发 ADT: android studio(as) 程序界面如下,注册码为6位随机数字,注册成功时弹出通知注册成功,注册失败时弹出通知注册失败. 布 ...

  2. Android逆向破解表单登录程序

    Android逆向破解表单登录程序 Android开发 ADT: android studio(as) 程序界面如下,登录成功时弹出通知登录成功,登录失败时弹出通知登录失败. 布局代码 <?xm ...

  3. 2018-2019-2 20165316 《网络对抗技术》Exp1 PC平台逆向破解

    2018-2019-2 20165316 <网络对抗技术>Exp1 PC平台逆向破解 1 逆向及Bof基础实践说明 1.1 实践目标 本次实践的对象是一个名为pwn1的linux可执行文件 ...

  4. 20165207 Exp1 PC平台逆向破解

    20165207 Exp1 PC平台逆向破解 0.写在最前面 在做三个实验的前两个的时候,我还没有到博客里去看作业的要求.当时我的主机名是kali5207也就是用我的学号命名的,要求的是姓名全拼命名k ...

  5. # 2018-2019-2 20165210《网络攻防技术》Exp1 PC平台逆向破解(BOF实验)

    2018-2019-2 20165210<网络攻防技术>Exp1 PC平台逆向破解(BOF实验) 实验分为三个部分: 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数. ...

  6. 2019-2020-2 20174318张致豪《网络对抗技术》Exp1 PC平台逆向破解

    Exp1_PC平台逆向破解 前期准备 一.逆向及Bof基础实践说明 1.1 实践目标 本次实践的对象是一个名为pwn1的linux可执行文件. 该程序正常执行流程是:main调用foo函数,foo函数 ...

  7. 【MyEclipse 2015】 逆向破解实录系列【2】(纯研究)

    声明 My Eclipse 2015 程序版权为Genuitec, L.L.C所有. My Eclipse 2015 的注册码.激活码等授权为Genuitec, L.L.C及其付费用户所有. 本文只从 ...

  8. Android引入高速缓存的异步加载全分辨率

    Android引进高速缓存的异步加载全分辨率 为什么要缓存 通过图像缩放,我们这样做是对的异步加载优化的大图,但现在的App这不仅是一款高清大图.图.动不动就是图文混排.以图代文,假设这些图片都载入到 ...

  9. 20165223《网络对抗技术》Exp1 PC平台逆向破解

    目录--PC平台逆向破解 1 逆向及BOF基础实践说明 1.1 实践内容 1.2 实践要求 1.3 基础知识 2 实验步骤 2.1 直接修改程序机器指令,改变程序执行流程 2.2 通过构造输入参数,造 ...

随机推荐

  1. 查询EBS在线用户SQL(R12)

    SELECT U.USER_NAME, APP.APPLICATION_SHORT_NAME, FAT.APPLICATION_NAME, FR.RESPONSIBILITY_KEY, FRT.RES ...

  2. QF——iOS程序运行原理(APP的生命周期)

    iOS程序的运行原理: main.m: 1. main.m 主函数是所有程序的入口函数. 2. 在main函数里是UIApplicationMain函数,开启了一个无限循环,以监听该应用. 该UIAp ...

  3. mysql建立数据库的方法

    mysql建立数据库的方法 方法一:使用create mysql> create database roudy; Query OK, 1 row affected (0.00 sec) mysq ...

  4. 读jQuery源码 jQuery.data

    var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, rmultiDash = /([A-Z])/g; function internalData( elem, n ...

  5. Art of Unit Test (1) - Breaking Dependency

    #!/usr/bin/env python # encoding: utf-8 import unittest """ the simplyest way to test ...

  6. Bash debug

    Debugging bash scripts Bash can help us to find problems in bash scripts in some ways. You don't exp ...

  7. visual leak dector内存泄漏检测方法

    http://vld.codeplex.com/ QT 内存泄露时,你们一般用什么工具检测啊 ------解决方案--------------------这篇你觉得详细么 :http://newfac ...

  8. Centos 升级MySQL版本或者Yum安装Mysql5.6

    Centos 升级MySQL版本或者Yum安装Mysql5.6 1.从MySQL Yum仓库下载最新的rpm文件:http://dev.mysql.com/downloads/repo/yum/Cen ...

  9. 关于ajax中async参数的感悟

    async,这个参数默认为true. 就是异步去处理信息. 当把它设置为false的时候,就是同步去处理数据了. var current_lead_id = '<?php echo $curre ...

  10. docker基于 aufs 文件系统

    docker的核心功能就是容器版本管理,在容器层实现了CVS版本管理,比如git那种可以commit/roll back 而AUFS就是其实现基础 AUFS可以实现文件系统的snapshot,这样对f ...