记录一次IDA动态分析

以前也干过几次动态分析,不过都忘了。由于工作原因,对Android,cocos2d-x有所了解。

下一家要入职一家棋牌公司,想着先破解一款市面上的竞品来看看。于是看中了一款叫JJ斗地主的。前端基于cocos2d-x,lua。

我想看看它的lua代码,可通过ida静态分析后发现摸不着头脑。于是想着用IDA调试一下android的so文件。看看到底是如何解密lua文件的。

我基本是看着书上来做的《游戏安全——手游安全技术入门》,不过书上难免有些地方我这种小白get不到点,于是又看了很多其他人的博客。

准备阶段

在电脑上安装IDA pro软件

我用的是6.8版本,以前在网上找的破解版的。

Android手机ROOT

以前用的是一个华为的手机,现在手上的是一台魅族MX5。

  • 首先进入设置的指纹和安全中,输入Flyme 密码开启root 权限
  • 在手机上安装KingRoot(其实期间折腾了好久,都是由于sh文件的权限不够)

在手机上安装调试模块

在IDA目录下的dbgsrv目录下找到android_server,将它放到android的一个root权限的目录下去。

期间发现了一个非常不错的博客系列:

Android逆向系列之动态调试(六)–IDA调试so文件。虽然我没看,不过里面的内容真的很系统。。。

检测要调试的APK是否有调试权限

我用的android反编译工具是:ApkIDE最新3.3.5少月增强版。非常强大和方便,修改后可以直接打包。

既要确保AndroidManifest.xml里的android:debuggable="true"

调试阶段

调试阶段就是打断点,单步调试以及看代码了。主要还是看书,也参考了Android逆向系列之动态调试(七)–IDA调试so文件(下),下面只说说一些让我困惑的点

启动ddms

jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8700命令可以让处于Waitting For Debug的进程继续执行。

启动程序前要确保ddms已经打开(我也不知道怎么打开ddms,所以我的笨办法是:打开Android Studio,在菜单栏中依次点击Tools,Android,Android Device Monitor,里面就有ddms),不过如果ddms里没有显示出来对应的进程,调试是会失败的(也有可能是错觉)。

进程名

进程名即android的apk的包名,在AndroidManifest.xml中有配。

1
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="cn.jj.hall">

当程序启动后,在IDA选择调试并附加到进程列表的时候,发现列表中没有对应的进程,可能是root的权限不没有真正的获取到。

通过adb shell启动程序

am start -D -n cn.jj.hall/cn.jj.mobile.lobby.view.Main 启动进程并让进程处于Waitting For Debug状态

其中cn.jj.hall是包名,cn.jj.mobile.lobby.view.Main是activity的名称

1
2
3
4
5
6
<activity android:configChanges="keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize" android:label="@string/app_name" android:name="cn.jj.mobile.lobby.view.Main" android:screenOrientation="portrait" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:windowSoftInputMode="adjustPan">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>

结果

最后还是凭着某天晚上通宵到3点搞定了。

解码过程大致就是,先是用xxtea解码。

关键信息如下图:

(解出来的是一个压缩的东西),所以还需要解压缩。关键位置如下图:

以前碰到一个用zip算法解压的,不过这个并不是。所以还是单步进去,最后发现了一个关键字:inflateInit。即zlib算法。

我的做法是直接用python来解码和解压。

解码

期间碰到的问题是,python自带的xxtea竟然非要一个16位的字符串作为key,而且还不能为空(key.ljust(16, "\0")都不行)。。。

无奈自己在网上找了个第三方的实现,可以看出xxtea算法确实很简单。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import struct
_DELTA = 0x9E3779B9
def _long2str(v, w):
n = (len(v) - 1) << 2
if w:
m = v[-1]
if (m < n - 3) or (m > n): return ''
n = m
s = struct.pack('<%iL' % len(v), *v)
return s[0:n] if w else s
def _str2long(s, w):
n = len(s)
m = (4 - (n & 3) & 3) + n
s = s.ljust(m, "\0")
v = list(struct.unpack('<%iL' % (m >> 2), s))
if w: v.append(n)
return v
def encrypt(str, key):
if str == '': return str
v = _str2long(str, True)
k = _str2long(key.ljust(16, "\0"), False)
n = len(v) - 1
z = v[n]
y = v[0]
sum = 0
q = 6 + 52 // (n + 1)
while q > 0:
sum = (sum + _DELTA) & 0xffffffff
e = sum >> 2 & 3
for p in xrange(n):
y = v[p + 1]
v[p] = (v[p] + ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z))) & 0xffffffff
z = v[p]
y = v[0]
v[n] = (v[n] + ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[n & 3 ^ e] ^ z))) & 0xffffffff
z = v[n]
q -= 1
return _long2str(v, False)
def decrypt(str, key):
if str == '': return str
v = _str2long(str, False)
k = _str2long(key.ljust(16, "\0"), False)
n = len(v) - 1
z = v[n]
y = v[0]
q = 6 + 52 // (n + 1)
sum = (q * _DELTA) & 0xffffffff
while (sum != 0):
e = sum >> 2 & 3
for p in xrange(n, 0, -1):
z = v[p - 1]
v[p] = (v[p] - ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[p & 3 ^ e] ^ z))) & 0xffffffff
y = v[p]
z = v[n]
v[0] = (v[0] - ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[0 & 3 ^ e] ^ z))) & 0xffffffff
y = v[0]
sum = (sum - _DELTA) & 0xffffffff
return _long2str(v, True)

通过上图可以很直观的猜测出JJXX为加密标志,jjmatch为加密的key

1
2
3
4
5
data = 要解码的文件的数据
sign = 'JJXX'
key = 'jjmatch'
if data[:4]==sign:
data = decrypt(data[4:], key)

解压

解压就很简单了,直接用python的官方zlib库就好了。

1
2
3
4
import zlib
z = zlib.decompressobj()
dec = z2.decompress(解码出来的data)

小插曲

1
2
if dec[0]==chr(239) and dec[1]==chr(187) and dec[2]==chr(191):
dec=dec[3:]

不知道这3个字符是啥,不过跳过就跳过吧(看第二个图片里就有这个代码)。

0%