安卓逆向思路
easyeasy-200
安卓逆向
用Android Killer直接反汇编,接着看程序java层
1 | ApplicationInfo是android.content.pm包下的一个实体类,用于封装应用的信息,flags是其中的一个成员变量public int flags = 0;用于保存应用的标志信息。 |
这些信息没用
接着看下面的判断
发现要求输入的字符串的长度要在35-39之间,之后再调用Format().form函数
1 | paramString.substring是用来截取字符串的 |
调用Format().form 所以返回的是字符串的[5,38],因此字符串长度需要大于38
接着看主函数得知返回的字符串需要大于32,满足就是调用Check模块中的check
1 | exists()方法 |
Check模块中先是复制了两个路径,再调用了checkPipes,判断路径是否存在,两个路径中存在任何一个就会被赋值为ture并返回。当两个路径都不存在时赋值为false。
check函数先将emulator传入,判断其结果,结果为真返回false,否则就调用后面的checkPasswd传入paramString
关键就在这个checkPasswd中
现在需要将apk编译后再用ida调试so文件(再看看native层)
(要用apktool反编译,so在生成的文件夹/lib/选择你的IDA对应版本文件的so打开,我是android Killer一键就有了)
用ida打开
可以看出先v7先是申请空间,再存着V6里的字符串,之后进行了字符串的反转
接着进入sub_6ED0(&v15, v7, v11);
通过
可以推断出这应该是将字符串复制给v15
之后就是 encrypt((const char *)&v14, v15); 这一加密了
然后将加密后的字符给了 v12
sub_69A4(&v14);
sub_69A4(&v15);
里面进行了delete,释放空间
接着是sub_7834((int)&secret, v12)
这里是比较了两个字符串是否相同
那现在就是找secret的值,通过按X,找使用过他的函数
找到了secret的字符串,和dword_1D09C的字符串,看着就像base64
再回头看看encrypt那个函数
先base64解码
再反转就可以到到答案了
flag{iwantashellbecauseidonthaveitttt}
easycrack-100
题目给了提示:在Java层没有太多逻辑,对输入框进行监听,当有输入的时候调用native层的函数进行校验
查查看
1 | Android的分4层,java应用程序,java框架,本地框架和java运行环境,Linux内核空间 |
刚刚那题Check模块的check就是用到了native层函数
这题我们不用Android killer,基本的操作来一次
先将文件用zip解压
将classes.dex文件放到dex2jar工具下,运行
1 | d2j-dex2jar.bat classes.dex |
生成了classes_dex2jar.jar文件
用jd-gui-1.6.6.jar打开分析
看到主函数
System.loadLibrary调用SO源码
MessageMe方法首先获取程序包名称,其中出现了一些新的函数
1 | getPackageName是Android中Context中用于得到包名的函数 |
i从51(’3’)开始,将包名给了str
又做了一些异或处理,但是目前看不到localObject字符串,就先放着
这是建立按钮、文本界面之类的
parseText为native层函数,传入的参数为输入的字符串。等等可以用ida反汇编so看看
主要就是这个监听窗口
如果字符串变动,将会执行具体的实现,这里是调用了native层的parseText函数。
现在利用apktool在该目录下执行
1 | apktool.bat d [-s] -f xxx.apk -o appdebug |
目录下此时多了一个appdebug文件夹,里面内容是apk的xml文件、AndroidManifest.xml和图片等
我们找到parseText函数进行分析
这里还有一个知识点关于JNI的
1 | 出于效率的问题,很多情况下,我们需要在上层的Java代码中调用底层 C或C++实现,这时jni就可以大显身手了。jni(Java Native Interface)允许Java代码和其他语言写的代码进行交互,使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样 做是可以接受的,甚至是必须的。例如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。 |
1 | JNIEXPORT void JNICALL Java_com_example_jnitest_JniTest_hello (JNIEnv *, jclass); |
JNIEnv *是任意一个本地方法的第一个参数
因此我们对a1按Y,改成JNIEnv *a1
接着来分析
找到相应的地址了”com/njctf/mobile/easycrack” ,同时将messageMe函数的返回值存到了v7
返回Java层,查看messageMe函数,将其直接赋值java包的名字,然后运行
1 | public class messageMe{ |
得到字符串”V7D=^,M.E”
在接着看ida中的反汇编
又将v7赋值给v8,v8赋值给了v9,将输入的字符串存在了v10
v11存了输入字符串的长度,v12存了字符串”V7D=^,M.E”的长度(9)
这就是将输入的字符串与”V7D=^,M.E”进行异或,若输入的字符串还有剩,”V7D=^,M.E”就从头开始异或,结果存到了v13中
接着将v29数据初始化,将v28赋值”I_am_the_key”,v17存着v28的长度(12),接着进入init函数
简单算法,写个脚本就行
1 | a1 = [] |
得到key
接着看ida发现加密函数
v29现在是key,v13是异或后的字符串,v11是字符串的长度
又是一些异或操作,返回看看加密之后做了什么
将加密后的数据转成16进制,在与compare字符串比较一样就正确,点开compare
得到C8E4EF0E4DCCA683088134F8635E970EEAD9E27
这就是最终加密后的16进制字符串,那我们直接逆推回去就好了,上脚本
1 | result = [0x39,0xa9,0x72,0x2d,0xe8,0x58,0x26,0x32,0x81,0xd,0xac,0x49,0xbb,0x10,0x46,0x65,0xb3,0x92,0xf,0x84,0xb8,0xbf,0xf2,0x52,0xe3,0x5b,0xfc,0xd5,0x59,0x6a,0xf0,0x5d,0x60,0x69,0x16,0x8e,0xfb,0x94,0x48,0xbc,0x71,0x36,0x57,0xad,0x44,0x7c,0x95,0xda,0xb7,0x47,0xdb,0x35,0x3c,0xd2,0x23,0xc5,0xa8,0xb,0x9f,0x31,0xd8,0x1f,0x3f,0xb0,0x2e,0xe1,0x5a,0x4a,0xf9,0x1,0x54,0xa7,0xa5,0xee,0x8,0x99,0x63,0x9b,0x50,0xbd,0x5,0xf7,0xcb,0xab,0x22,0xc2,0x8a,0x38,0x7d,0x6,0xb1,0xc0,0x4e,0x74,0x3a,0xe5,0x67,0x2b,0xa3,0x73,0x89,0x9e,0xba,0x88,0x3d,0x28,0x62,0x8f,0xfd,0x43,0x98,0x4d,0x56,0xb2,0xc,0x29,0x6e,0x78,0x25,0xe0,0xe9,0xf6,0x9c,0x13,0xed,0xf8,0xc4,0x20,0x87,0x2,0x7b,0xf1,0x6d,0xc7,0x8c,0x9d,0x86,0x3b,0x66,0xfa,0xb6,0x42,0x6f,0x14,0xd0,0x19,0xaf,0x11,0x21,0x96,0x85,0x91,0xb5,0xa0,0x1b,0x18,0xa6,0xa2,0x4b,0x40,0xd4,0x8d,0x2a,0x8b,0x5c,0x2c,0xe6,0xfe,0xa4,0x30,0xe7,0xff,0xc8,0x5f,0xe2,0x1c,0xdf,0xae,0x7f,0xc3,0x61,0xef,0x90,0x6c,0x51,0x2f,0xec,0x12,0x7a,0xaa,0xdd,0x77,0xf5,0x4,0xd9,0x83,0x33,0xeb,0x80,0x27,0x3,0xb4,0x9,0x37,0x6b,0x41,0x4f,0x7e,0xf3,0x24,0xf4,0xc9,0x7,0xd1,0x45,0x70,0xa1,0xd7,0x34,0x93,0x15,0xca,0x4c,0xcd,0x97,0xb9,0xea,0x0,0x5e,0x1a,0x9a,0xcf,0x79,0xa,0x3e,0x82,0xd3,0x68,0x75,0x64,0xce,0x55,0xe,0xbe,0x1d,0xe4,0xc1,0xc6,0xde,0xcc,0x1e,0x17,0xd6,0xdc,0x53,0x76] |
结果为It_s_a_easyCrack_for_beginners
fake-func
apk文件,用Android Killer直接处理,看java层
发现判断使用check模块的checkflag
这里用了native层函数
直接到so里看看
将输入的字符串传到v3
进入sub_E08函数后与off_6004字符串(”c2RuaXNjc2RuaXNjYWJjZA==”)比较
看看sub_E08函数看看
将off_6004字符串赋值给v0,在将v1存入off_6004字符串的长度
接着看sub_16D8函数,发现
可知是base64解密
将字符串解密得到新的字符串”sdniscsdniscabcd”
对 sub_E08()函数按X看看还有谁调用过
拿到”K4/7/faihmk9/WEMlfuFdpgrP86ckd4oQQ/UeAiZdx8=”
在看看sub_1388函数
在看看sub_F24函数
byte_4255
查看发现256个
加密的文本结尾有一个等号,又不是base,怀疑是AES,直接爆破
flag{fake_func_3nfxvs}