颤抖了吗?九步逆向破解银行安全令牌 (颤抖吧,原著女主免费阅读下拉式漫画)

教程大全 2025-07-14 01:47:56 浏览

作者历经千辛万苦,逆向了一个银行动态口令的APP,过程艰辛曲折。最后发现,其实生成算法也不算很复杂,其中主要使用了 android_id 系统时间戳 作为生成变量。看起来高大上的动态口令,在逆向工程师抽丝剥茧的分析下,亦不过如此哇~

正文

我这次要对全巴西最大的银行之一开刀,我也经常会使用到这家银行所提供的服务。他使用包括用户密码在内的多种途径来验证用户的身份。我逆向的动态口令(OPT)也是其中一种,并且我把他移植到了Arduino-compatible 平台上。

在文章中我已经去掉了敏感的信息,以保护无辜的人的利益。而且这项研究的结果也不足以让我能够黑掉别人的银行账户。即使一个拥有root权限的第三方恶意应用,在没有足够的账户信息的前提下,都不能模拟动态口令的生成。而且,这次研究并没有发现任何代码层面的漏洞,这家银行的生成算法甚至比google的认证算法还要安全,甚至可以说这篇文章是对该银行APP安全性的一次褒奖。他的动态生成算法,完全符合TOTP规范。把数据安全做到了极致。

通常对于新用户,他们会得到一张密保卡,但是密保卡这东西是极其不方便的。另外一个更好的方法就是使用android app生成一个动态口令,大胆的猜测,这个口令的生成可能把手机号,或者是pin值作为参数。在我每次刷机,或者更换手机的时候,都要重新绑定一遍这个安全服务。虽然过程比较简单,但还是令我不爽。所以我决定,逆向这个android APP做一个自己的动态密码生成工具。

Activating the application 激活应用

在阅读源码之前,我还是喜欢先把应用下载下来看看他长啥样,下面是三个阶段的截图。

第一张是安装时的截图,看一下他所需要的权限,这可不是开发人员为何好玩才加上去的。他们其中有些甚至有可能影响到动态口令的生成。第二张是激活界面,需要填四个数字,这四个数字只能通过给银行打电话被告知。第三幅图就是,成功激活后生成的动态口令。

The toolset 工具相关

逆向android应用需要用到的几个小工具,在下面列出来了,其实就是网上常用的几种工具。

Android SDK提供adb这个强大的命令行工具,提取apk文件,和获取手机信息全靠它。dex2jar这个工具可以吧dex转换成jar包的形式JD, JD-GUI这个就是··java反编译工具,直接出源码Eclipse这个就不多说了,地球人都知道。

Getting the APK file from the phone 第一步:取得APK文件

这个相当简单的嘛~。可以直接在play里面下,也可以使用ADB在手机中把他抓出来。

查找包名

$ ./adb shell pm list packages | grep mybankpackage:com.mybank

确定路径

$ ./adb shell pm list packages | grep mybankpackage:com.mybank

下载

$ ./adb pull /data/app/com.mybank-1.apk2950 KB/s (15613144 bytes in 5.168s)

第二步:解压缩APK文件

APK直接可以被解压缩,其实他就是一个压缩包文件,只是后缀不同。解压缩以后的classes.dex文件中包含了java源代码信息。

解压缩

$ unzip com.mybank-1.apk(file list omitted for brevity)

把dex转换成jar文件

$ mv classes.dex com.mybank-1.dex$ ./d2j-dex2jar.sh com.mybank-1.dex dex2jar com.mybank-1.dex -> com.mybank-1-dex2jar.jar

第三步:瞅代码

把jar包放进JD—GUI里面,就可以看到源代码了。

很轻易的就可以发现几个比较特殊的包,br.com.mybank.integrador.token, br.com.othercompany.token , com.mybank.varejo.token毫无疑问核心代码就在里面,只不过代码应该被混淆了。

第四步:通过异常字符串反混淆

代码中经常的许多字符串名都被混淆了(其实是加密了,不过加密的秘钥在代码中能找到~,作者说是混淆就是混淆把~),这比较蛋疼。要知道,代码中的字符串会对逆向起到很大的帮助。

public void trocaPINcomLogin(int paramInt, boolean paramBoolean, Perfil paramPerfil){ if (paramPerfil == null) throw new IllegalArgumentException(a.a("1p5/eEf/sl3kbeUcP509qg==")); if (!this.jdField_a_of_type_U.jdField_a_of_type_JavaUtilHashtable.contains(paramPerfil)) throw new RuntimeException(a.a("86jcmKgr/ZshQu9aGVbuGscy2nHW4UEWqudRoUXhImQ=") + a.a("7u8KqqwqUD3a7FM339fp6pRrxUtQrHDMyqvZ6A2MurQ=")); if ((this.jdField_a_of_type_BrComOtherCompanyTokenParamsGerenciador.isPinObrigatorio()) && (!paramBoolean)) throw new RuntimeException(a.a("aMsL/5kjkXKD4K1SvpTuuJZUS0U0fL19UT2GxjJ/QzQ=")); Configuracao localConfiguracao = paramPerfil.getConfiguracao(); if ((localConfiguracao.a().a()) && (paramPerfil != this.jdField_a_of_type_BrComOtherCompanyTokenPerfil)) throw new RuntimeException(a.a("ASszutKFJW3iqDb7X/+vqAcYxTLXN2SJOIs0ne596Pu3ZoRxjiiscwhV6fT70efX")); localConfiguracao.a().a(paramInt); localConfiguracao.a().a(paramBoolean); this.jdField_a_of_type_U.a(paramPerfil); if (!paramPerfil.equals(this.jdField_a_of_type_BrComOtherCompanyTokenPerfil)) a(paramPerfil);}

不过幸运的是,在抛出异常的语句中,我们可以找到一些蛛丝马迹,我们通过观察可以发现,混淆字符串的函数是a.a。根据这些信息的提示,我们可以猜测a.a是一个解密有关的类。顺理成章,我们直接去a函数中分析解密所使用的代码。

这是分析完a类之后的一些额外收获

p类是一个base64解密的类。b类,实现了AES的功能。搜索这个类之中的一些字符串,我发现它是网络上的一个开源实现 Paulo Barreto's JAES中的内容。a类中的private static byte[]是混淆所使用的秘钥,可以通过一个简短的程序来反混淆。

不过不幸的是,a.a不单单是JAES的AES加密的包装,其中也包含着自己实现的一些加密。

不过这都不是事儿,我还是把a.a中的解密函数用python实现了。

def decodeExceptionString(str): aesKey =  xorKey =  blockSize = 16  aes = AES(aesKey) stringBytes = Base64.decode(str) outputString = ""  for blockStart in xrange(0, len(stringBytes), blockSize): encryptedBlock = stringBytes[blockStart:blockStart+blockSize] plaintextBlock = aes.decrypt(encryptedBlock) outputString += plaintextBlock ^ xorKey xorKey = encryptedBlock  return outputString

简而言之,除了AES和混淆秘钥,这个类还实现了CBC(密码段链接)。

试验一下上述代码的功能

$ ./decode "ASszutKFJW3iqDb7X/+vqAcYxTLXN2SJOIs0ne596Pu3ZoRxjiiscwhV6fT70efX"N?o é possível alterar PIN sem estar logado.

这段葡萄牙语的意思是,it is not possible to change PIN without being logged in。看来代码运行的还不错。

第五步:逆向核心代码–随机密码生成过程

颤抖吧

解决了,字符串混淆的问题,接下来个就要弄清楚随机密码的生成过程了~找啊找啊找啊找~~~~找了好久,我终于发现了一个切入点,br.com.othercompany.token.dispositivo.OTP这个类。下面是它抛出的一些异常,反混淆之后我们可以看到原文。

public String calculate() throws TokenException { int i = (int)Math.max(Math.min((this.a.getConfiguracao().getAjusteTemporal() + Calendar.getInstance().getTime().getTime() - 1175385600000L) / 36000L, 2147483647L), -2147483648L); a(); if (i < 0) throw new TokenException("Janela negativa"), i); int j = (0x3 & this.a.getConfiguracao().getAlgoritmos().a) >> 0; switch (j) { default: throw new TokenException("Algoritmo inválido:" + j, i); case 0: return a(i); case 1: } return o.a(this.a.getConfiguracao().getChave().a(20), i);}

很容易读懂,变量i是一个时间戳,从2007年4月11日到现在的秒数除以36,36就是每个动态口令的存活时间。

至于为什么是2007年4月11日,我就不知道了,大概是程序员他老婆的生日 : )

他还引入了一个修正函数getAjusteTemporal(),为了解决各地区的时差问题。上文代码中的o.a函数是用于生成动态密码,他的两个参数一个是刚才说到的时间戳,还有一个是遗传byte数组(应该是一个密钥)。

第六步:寻找密钥

看一下,生成语句的这段调用,this.a.getConfiguracao().getChave().a(20) this.a 是一个Perfil (profile) 对象,getConfiguracao() 返回一个Configuracao (settings) 对象getChave()返回一个z类,a(int)返回一个byte数组,这个数组就是key。

z类中的字符串,也经过了混淆,但是比较简单,反混淆过程就不提了.查看一下c类中的a(int)函数,是返回一个byte数组,长度截取到参数值。Perfil (profile) 对象反而是由PersistenciaDB类创建的,这个类中也包含了很多被混淆的字符串。

a = a.a("DwYyIlrWxIS9ruNMCKH/PQ==");b = a.a("SceoTjidi0XqlgRUo9hcDw==");c = a.a("yrYBlcp8nEfVKUT9WSqTqA==");d = a.a("jUTzBfsP/AO/Kx/1+VQ3CQ==");e = a.a("Y56SnU/pIKROPCLHu7oFuw==") + b + a.a("38oyp4eW3xqT3TaMfWZ5RA==") + "_id" + a.a("3Q+FCEVH2PxZ31ms4WHHwNB40EbmtWzHPhwoaB1nM7lGr+9zZzuVpx5iZ4YR+KUw") + c + a.a("bYYIl6LtqthcUCCFFb7JCRSC8zr5hKIFXe5JHFCCkZA=") + d + a.a("ENCtPBu4RtFta2XI1GsQag==") + a.a("ImPhDy43f+Nr4G5ofkZz+g==");

幸好a.a的机制我们前面研究过,翻译出的原文如下。

a = "token.db";b = "perfis";c = "nome";d = "data";e = "create table perfis (_id integer primary key autoincrement, nome text not null,"Row: 0 name=android_id, value=0123456789abcdefshell@hammerhead:/ $ exit

a.aa把blob分成很多段,96位的头,16位的随机数,16位的标签和为加密数据预留的空间。

进一步研究aa这个类,我们的到了,如下的信息。

类a实现了 EAX AEAD(Authenticated Encryption with Associated>

公司注销有哪些程序

企业注销流程第一步、指定日报刊登注销声明。第二步、地税所办理地税注销第三步、国税所办理国税注销第四步、质量技术监督局办理组织机构代码注销第五步、统计局办理统计证注销第六步、相应签发部门办理其它证件注销第七步、工商局办理营业执照注销第八步、开户银行办理银行开户许可证注销第九步、公安局销毁印章备注:办理税务注销时须提交由北京华澳诚会计师出具的审计报告办理工商执照注销时须提交由北京华澳诚会计师出具的清算报告

听力下降有哪些原因?

单侧听力患者没有方向感。 一般讲,下列因素可损伤听力:中耳炎症、遗传因素、器官自然老化、声创伤、耳毒性药物,肿瘤放化疗、长时间暴露噪声中、疾病或先天缺陷。 发现听力下降应尽量保存现有的残余听力是至关重要的。 首先对听力有害的一些耳毒性药物要慎重使用,例如:庆大霉素、链霉素、新霉素、水杨酸类止痛药、利尿药和抗癌药等等。 第二、要避免接触强噪声,如鞭炮声、迪斯科舞厅的强音乐声等。 第三、对尚有残余听力的各种耳聋要尽早佩戴助听器

如何炒白银开户,白银td怎么炒?

白银TD开户流程 流程步骤如下:第一步:到银行网点开一个银行卡 第二步:必须开通个人网上银行贵宾版 第三步:开卡成功后登陆个人贵宾网上银行,进行客户签约. 第四步:首次签约,需要风险评估. 第五步:填写风险评估相关资料. 第六步:风险评估通过,开始签约. 第七步:仔细阅读交易规则,了解交易时间,品种等. 第八步:了解延期交收交易风险. 第九步:填写机构号. 第十步:确认资料,点击确定,开户成功,就可以进行交易了!

本文版权声明本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,请联系本站客服,一经查实,本站将立刻删除。

发表评论

热门推荐