当前位置:主页>产 业>业界新闻>

颤抖了吗?九步逆向破解银行安全令牌(2)

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 mybank

package:com.mybank

确定路径

$ ./adb shell pm list packages | grep mybank

package:com.mybank

下载

$ ./adb pull /data/app/com.mybank-1.apk

2950 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 possvel 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, data blob not null);";

竟然是一条,SQL语句,真是很有趣,原来它使用数据库来存储配置信息。其中还有一个文件名token.db,很可能是一个SQLite数据库。紧接着通过研究PersistenciaDB类中的 carregar(load)函数,我们可以确定这一想法,他通过SQLiteDatabase类来访问这个数据库。

(责任编辑:安博涛)

分享到:

更多
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
  • 微笑/wx
  • 撇嘴/pz
  • 抓狂/zk
  • 流汗/lh
  • 大兵/db
  • 奋斗/fd
  • 疑问/yw
  • 晕/y
  • 偷笑/wx
  • 可爱/ka
  • 傲慢/am
  • 惊恐/jk
用户名: 验证码:点击我更换图片
资料下载专区
图文资讯

中国重启银行业安全新规 下月征集意见

中国重启银行业安全新规 下月征集意见

8月19日,路透社今日援引知情人士的消息称,中国将重启银行业安全新规。在此之前,该...[详细]

网络安全准则还需“下一步”

网络安全准则还需“下一步”

近日,联合国信息安全问题政府专家组召开会议,并向联合国秘书长提交报告。专家组包括...[详细]

用大数据为互联网金融保驾护航

用大数据为互联网金融保驾护航

近日,在2015上海新金融年会暨外滩互联网金融外滩峰会上,中国人民银行条法司司长张涛...[详细]

互联网深刻变革:世界正向黑客帝国演进

互联网深刻变革:世界正向黑客帝国演进

2015年中国互联网大会于7月23日在北京国家会议中心闭幕了,很显然,由于去年办过一次...[详细]

《网络安全法》有望在三季度出台

《网络安全法》有望在三季度出台

2015年,第十二届全国人大常委会第十五次会议初次审议了《中华人民共和国网络安全法(...[详细]

返回首页 返回顶部