近年来,对于安卓系统开放性、免费性等特征让开发者和用户趋之若鹜。当人们渐渐习惯安卓应用免费模式,在免费背后显露出巨大的安全阴影。
由于Google官方市场国内访问的不稳定性,95%以上的国内用户选择从第三方应用市场下载应用。由于手机应用可以通过升级等方式更改程序内容,第三方应用市场几乎很难通过技术手段智能识别。虽然部分第三方市场开启定期人工抽查模式,一经发现就及时下架,但耗时耗力并非就能逐一查到。根据资料显示,国内的第三方应用市场已经变成了鱼龙混杂,流氓、病毒软件丛生之地。
除了“二次打包”之后,黑客通过攻击反编译获得安卓应用的源代码,刺探应用的内部逻辑,寻找可能的代码漏洞并加以利用,危害应用的安全。尤其是针对金融类应用,这种反编译操作不仅危害应用安全,而且可能发现接口漏洞,从而对整个核心业务系统产生威胁。
2014年5月16日在江苏通付盾举办了主题为“反欺诈”的OWASP沙龙,来自支付宝、IBM、通付盾的安全专家发表了演讲并与大家进行了讨论;届时,发布了“移动支付业务风险管理研究报告”,其中对xxx宝、xxx钱包、xxx钱包等第三方支付应用和多家金融机构的移动应用进行了风险评估,所以的应用都或多或少的存在漏洞。在此做个分享,希望对大家有帮助。
下表为主要的评估方向:
项目 |
内容 |
安全评级 |
分析和建议 |
攻击模拟 |
源代码安全 |
重要函数逻辑安全 |
安全 |
2.1.1 |
附录1 |
加密算法 |
不安全 |
|
|
|
是否混淆 |
|
|
|
|
是否允许动态调试 |
|
|
|
|
Activity的exported属性设置 |
|
|
|
|
是否存在硬编码问题 |
|
|
|
|
数据存储安全 |
是否保存手机号、密码等敏感信息 |
|
|
|
敏感信息是否加密处理 |
|
|
|
|
加密是否易破解 |
|
|
|
|
数据是否能被别的应用访问 |
|
|
|
|
调试信息是否泄漏关键信息 |
|
|
|
|
数据传输安全 |
关键数据是否加密传输 |
|
|
|
是否可进行中间人攻击 |
|
|
|
|
是否进行数据合法性验证(客户端/服务器) |
|
|
|
|
安全增强测试 |
是否进行签名验证 |
|
|
|
键盘劫持测试 |
|
|
|
|
进程保护测试 |
|
|
|
|
组件安全测试 |
|
|
|
|
服务器安全测试(Web渗透) |
|
|
|
|
业务安全 |
密码保护机制 |
|
|
|
密码策略测试(找回密码等) |
|
|
|
|
登录次数限制 |
|
|
|
|
会话保护策略 |
|
|
|
|
合规安全 |
行业合规 |
|
|
|
总评 |
|
主要的程序执行流程用“—>”表示;接口与实现类之间的关系用“|||”表示,接口在前,实现类在后;继承或实现同一父类或接口的子类之间关系用“||”表示。
整个工程的入口地址是:
com/xxx/mobile/framework/xxx.java
该文件主要进行工程配置和application上下文生成。它调用
com/xxx/mobile/core/impl/MicroApplicationContextImpl.attachContext(Lcom/xxx/mobile/framework/xxx V—>com/xxx/mobile/core/b.a(Ljava/lang/String;Ljava/lang/Object;)Z,
将
com/xxx/mobile/core/a|||com/xxx/mobile/core/a/a/a
和
com/xxx/mobile/core/a/a/b
放入
com/xxx/mobile/core/b;->a:Ljava/util/Map;
再调用
com/xxx/mobile/core/init/impl/a;->b()V—>com/xxx/mobile/core/init/impl/c;->a()V—>com/xxx/mobile/framework/MicroApplicationContext;->registerService(Ljava/lang/String;Ljava/lang/Object;)Z—>com/xxx/mobile/core/b;->a(Ljava/lang/String;Ljava/lang/Object;)Z,进行服务注册。
同时
com/xxx/mobile/core/init/impl/a;->b()V
调用
com/xxx/mobile/core/init/impl/b;->a()V
读取assets下的bundles.xml进行MetaInfo设置,再调用
com/xxx/mobile/core/a;->c(Ljava/lang/String;)V
进行com/xxx/mobile/core/a/a/a;->e:Ljava/lang/String属性设置。
其中
com/xxx/mobile/core/impl/MicroApplicationContextImpl;->findServiceByInterface(Ljava/lang/String;)Ljava/lang/Object—>com/xxx/mobile/core/b;->a(Ljava/lang/String;)Ljava/lang/Object, 从com/xxx/mobile/core/b;->a:Ljava/util/Map取值。
UI入口:com/eg/android/xxxGphone/xxx.java,根据assets下package.properties的main_showGuide记录用户是否已经启动过app。如果没有启动过app跳转到
com/xxx/mobile/core/guide/StartGuideActivity.java,
否则启动位置服务缓存。
方法
com/eg/android/xxx/xxx.java.onCreate()
启动线程任务
com/eg/android/xxx/a。
方法
com/eg/android/xxx/xxx.java.d(Lcom/eg/android/xxx/xxx V
启动StartGuideActivity或调用
com/eg/android/xxx/xxx.java.b()Vcom/eg/android/xxx/xxx.a(Lcom/xxx/mobile/core/a;Landroid/net/Uri;)Vcom/xxx/mobile/core/a;->a(Landroid/os/Bundle;)V com/xxx/mobile/core/a/a/a.a(Landroid/os/Bundle;)V,
根据com/xxx/mobile/core/a/a/a;->e:Ljava/lang/String的值与com/xxx/mobile/framework/app/ApplicationDescription;->getName()Ljava/lang/String值作比较,获取appId(如果appId值为20000001,则根据com/xxx/android/launcher/MetaInfo的信息判断对应的app为com.xxx.android.launcher.TabLauncherApp)
接下来主要的程序执行流程是:
com/xxx/mobile/core/a/a/a.a(Ljava/lang/String;Ljava/lang/String;Landroid/os/Bundle;)V—>com/xxx/mobile/framework/app/RunnableApplication.start()V|||com/xxx/mobile/framework/app/ActivityApplication. start()V—>com/xxx/mobile/framework/app/ActivityApplication.onStart()|||com/xxx/android/launcher/TabLauncherApp.onStart()—>com/xxx/mobile/framework/MicroApplicationContext;->startActivity(Lcom/xxx/mobile/framework/app/MicroApplication;Landroid/content/Intent;)V|||com/xxx/mobile/core/impl/MicroApplicationContextImpl.startActivity(Lcom/xxx/mobile/framework/app/MicroApplication;Landroid/content/Intent;)V,启动主界面com/xxx/android/launcher/TabLauncher.smali。
com/xxx/android/launcher/TabLauncher.smali主界面由com/xxx/android/launcher/factory/XmlWidgetGroupFactory来构造Tab content List;Tab content List根据
com/eg/android/xxx/R$raw;->widgetgroups来生成。
接下来
widgetgroups.xml内容为:
id="20000002" className="com.xxx.mobile.android.widget.main.MainWidgetGroup"
defaultWidgetGroup="true"/>
id="20000003" className="com.xxx.android.widgets.bill.BillWidgetGroup"
defaultWidgetGroup="false"/>
id="20000004" className="com.xxx.android.widgets.asset.AssetWidgetGroup"
defaultWidgetGroup="false"/>
id="20000005" className="com.xxx.android.widget.security.SecurityWidgetGroup"
defaultWidgetGroup="false"/
下面列出每个主界面的对应关系:
代码 |
界面 |
com.xxx.mobile.android.widget.main.MainWidgetGroup |
xxx宝(默认主界面) |
com.xxx.android.widgets.bill.BillWidgetGroup |
账单 |
com.xxx.android.widgets.asset.AssetWidgetGroup |
我的资产 |
com.xxx.android.widget.security.SecurityWidgetGroup |
安全 |
表1xxx客户端界面和功能的对应关系
下面继续看选项卡处理的相关代码。该代码在
com/xxx/android/launcher/TabLauncher.smali文件中。每次切换选项卡时,根据选项卡的tag获取定制的选项卡视图IWidgetGroup(如,com/xxx/android/widgets/asset/AssetWidgetGroup)。当IWidgetGroup激活后,调用回调方法onResume(),进一步的执行流程是:
com/xxx/mobile/framework/service/ext/security/AuthService和
com/xxx/mobile/security/authcenter/service/AuthServiceImpl.isLogin()Z,将执行到
com/xxx/mobile/security/accountmanager/service/AccountServiceImpl.getCurrentLoginState()Z—>com/xxx/mobile/framework/service/ext/dbhelper/SecurityShareStore;->getString(Landroid/content/Context;Ljava/lang/String;)Ljava/lang/String,
从SharedPreferencesecuitySharedDataStore.xml中读取登录状态。如果登录状态为true,就正常显示选项视图,否者启动com/xxx/mobile/security/authcenter/ui/LoginActivity。
接下来,
com/xxx/mobile/security/authcenter/ui/LoginActivity,登录UI。登录按钮的点击事件回调方法
com/xxx/mobile/security/authcenter/ui/LoginActivity.onClick(Landroid/view/View;)V, smali 3332行 —> com/xxx/mobile/common/helper/HideUtils;->isHideAccount(Ljava/lang/String;),判断输入帐号是否包含“***”字符信息(防SQL注入检测)。如果输入账户含“***”信息,将账户置空,弹出警告框“账户不能为空”。之后进行输入信息的有效性检查,有效检查通过后, 调用com/xxx/mobile/security/authcenter/ui/LoginActivity;->a(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V进行登录,该方法的第一、第二个参数为登录的用户名密码的明文,第三个参数标识是淘宝账户或xxx宝账户。
接下来,程序执行
com/xxx/mobile/security/authcenter/a/b;->a(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lcom/xxx/mobile/framework/service/ext/security/bean/UserLoginResultBiz
—>com/xxx/mobile/framework/service/ext/security/LoginService;->login(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lcom/xxx/mobile/framework/service/ext/security/bean/UserLoginResultBiz |||com/xxx/mobile/security/authcenter/service/LoginServiceImpl->login,该方法对登录的信息进行RSA加密处理。
login()方法先调用
com/xxx/mobile/security/authcenter/service/LoginServiceImpl->d()—>com/xxx/mobile/security/authcenter/service/DeviceServiceImpl;->a(Ljava/lang/String;)Z,进行RPC远程调用,处理终端设备信息,如walletTid等。
login()方法再调用
com/xxx/mobile/security/authcenter/service/LoginServiceImpl;->a(Lcom/xxx/mobile/framework/service/ext/security/bean/UserLoginResultBiz;Lcom/xxx/mobilegw/biz/shared/processer/login/UserLoginReq;)Lcom/xxx/mobile/framework/service/ext/security/bean/UserLoginResultBiz
—>com/xxx/mobilegw/biz/shared/processer/login/UserLoginService;->login(Lcom/xxx/mobilegw/biz/shared/processer/login/UserLoginReq;)Lcom/xxx/mobilegw/biz/shared/processer/login/UserLoginResult,此为远程方法调用的Client代理,通过代理调用远程方法,进行登录处理。
StartGuideActivity—>“立即开启”按钮的点击事件回调com/xxx/mobile/core/guide/c.smali,回调之后返回至xxx.java的onActivityResult()方法—>com/eg/android/xxx/xxx.java.b()V。
com/xxx/mobile/security/authcenter/ui/LoginActivity,登录UI
com/xxx/mobile/security/authcenter/ui/RegisterActivity,注册UI
com/xxx/android/client/a/a本地配置类
com/xxx/mobile/common/helper/ReadSettingServerUrl,服务器网关URL为:https://mobilegw.xxx.com/mgw.htm
com/xxx/mobile/common/rpc/transport/http/HttpCaller,RPC代理生成时http访问处理
com/xxx/mobile/common/rpc/RpcInvoker,RPC代理生成处理
com/xxx/mobile/common/rpc/RpcInvocationHandler,RPC代理生成结果的处理
com/xxx/mobile/common/rpc/RpcFactory.getRpcProxy(Ljava/lang/Class;)Ljava/lang/Object方法创建动态RPC代理实例instance
com/xxx/mobile/framework/service/common/impl/RpcServiceImpl,调用RpcFactory
com/xxx/mobile/security/authcenter/MetaInfo,配置登录与注册的appId与app包对应关系。
com/xxx/mobilegw包为RPC的本地代理包。
com/xxx/mobile/common/transport包为http网络访问包。
com.xxx.mobile.android.widget.main.MainWidgetGroup,xxx宝界面。主截面由com/xxx/mobile/android/main/ui/AccountAreaLayout和com/xxx/mobile/android/main/ui/HomeViewpagerLayout两部分组成。com/xxx/mobile/android/main/ui/AccountAreaLayout负责构造UI上方的标题;com/xxx/mobile/android/main/ui/HomeViewpagerLayout负责构造UI中间的内容。
com/xxx/mobile/android/main/ui/HomeViewpagerLayout由定制的android.support.v4.view.ViewPager和
com.xxx.mobile.android.main.vpi.IconPageIndicator组成。
ViewPager负责构建分页横向滑动的内容;IconPageIndicator构造的是滑动状态的指示图标,在下方。页面横向滑动的事件由com/xxx/mobile/android/main/ui/HomeViewpagerLayout.onPageScrollStateChanged(I)V方法处理,根据滑动的事件设置当前需要显示的UI内容。
定制的android.support.v4.view.ViewPager由
com/xxx/mobile/android/main/i/b作为适配器(Adapter)进行填充。第一个页面为com/xxx/mobile/android/main/f/a构造的Fragment,即“当面付”;第二个页面为com/xxx/mobile/android/main/g/g构造的Fragment,即“待办事项列表”;第三个页面为com/xxx/mobile/android/main/b/g构造的Fragment,即“app服务列表”。
com/xxx/mobile/android/main/f/a“当面付”由ImageView和其他TextView构造,布局文件是face_to_face_pay.xml。
com/xxx/mobile/android/main/g/g“待办事项列表”由ListView和其他TextView构造,布局文件是todolist.xml.。
com/xxx/mobile/android/main/b/g“app服务列表”由一个GridView构造,布局文件是applications_center.xml。此处GridView由com/xxx/mobile/android/main/b/b/a构造的Adapter填充,com/xxx/mobile/android/main/b/b/a中的List构造时,调用com/xxx/mobile/framework/service/ext/openplatform/service/AppManageService;->getMyAppsFromLocal()Ljava/util/List|||com/xxx/mobile/appstoreapp/manager/AppManageServiceImpl.getMyAppsFromLocal()Ljava/util/List,调用
com/xxx/mobile/framework/service/ext/openplatform/persist/MyAppDao访问本地数据库open_platform_apps.db读取app数据信息;同时将app信息(appId,
com/xxx/mobile/framework/service/ext/openplatform/AppFactory构造的App等)放入
com/xxx/mobile/appstoreapp/manager/AppManageServiceImpl;->b:Ljava/util/Map中。
com/xxx/mobile/android/main/b/g“app服务列表”的点击事件由com/xxx/mobile/android/main/b/b/d处理。根据点击app,判断是否是未安装app,之后,调用
com/xxx/mobile/framework/service/ext/openplatform/app/App;->authAndLaunch(Landroid/os/Bundle;)V进行验证和登录目标。
com/xxx/mobile/framework/service/ext/openplatform/app/App;->authAndLaunch(Landroid/os/Bundle;)V判断是否是xxx宝所有权的app;如果是,调用
com/xxx/mobile/framework/service/ext/openplatform/app/App;->launchAppWithAuthCode(Lcom/xxx/mobile/framework/service/ext/security/bean/UserInfo;Ljava/lang/String;Landroid/os/Bundle;)V;否则调用com/xxx/mobile/framework/service/ext/openplatform/app/App;->checkLoginAndAuth(Landroid/os/Bundle;)V进行处理。
com/xxx/mobile/framework/service/ext/openplatform/app/App;->launchAppWithAuthCode(Lcom/xxx/mobile/framework/service/ext/security/bean/UserInfo;Ljava/lang/String;Landroid/os/Bundle;)V||com/xxx/mobile/framework/service/ext/openplatform/app/NativeApp;->launchAppWithAuthCode(….)||com/xxx/mobile/framework/service/ext/openplatform/app/ApkApp||com/xxx/mobile/framework/service/ext/openplatform/app/WebApp||com/xxx/mobile/framework/service/ext/openplatform/app/XmlApp。调用com/xxx/mobile/core/a/a/a的a(Ljava/lang/String;Ljava/lang/String;Landroid/os/Bundle;)V方法进行apk动态加载。
黑客经过分析研究,已经清楚xxx宝钱包使用了如下几种安全措施保证程序的安全性:
此处省略数万字…………
黑客通过对这些技术的分析研究得到以下结论:一方面,这些技术的使用增加了对系统的保护强度,使得黑客进行逆向工程、程序分析和攻击的难度都大大提高了;但另一方面,需要对这些技术的有效性及针对这些技术可能的攻击进行深入研究讨论。在下面的3.4小节中,黑客将继续深入讨论这一课题。
除了上面的具体技术外,还有一些技术因素也提升了整个程序的安全性及增加了逆向工程和分析的难度。黑客经过分析认为,虽然和上面提到的特定技术相比,这些技术不是特别关键,但使用这些技术也可大幅提高软件的质量,因此也有参考价值:
技术 |
示例 |
评论 |
调试信息移除 |
所有汇编代码中的.line等信息都已经被移除 |
需要在Java代码编译及dex文件生成过程中进行 |
抽象编程 |
面向接口和抽象类的抽象编程方式 |
加大了定位关键代码的难度 |
基于代码的布局 |
定制布局通过Java代码实现,而不是xml代码实现 |
加大了定位关键代码的难度 |
表3xxx钱包使用的其它安全技术
当然,黑客也发现了xxx钱包存在的一些安全及软件质量的问题,以下示例性的列举一些例子:
此处省略数万字…………