好友
阅读权限30
听众
最后登录1970-1-1
|
1.查壳脱壳
1.使用 PKID查壳,发现加了360的壳
2.我在逍遥模拟器中进行脱壳操作,先是运行genymotion,随后运行模拟器,在cmd中输入adb shell,会提示超过了一个设备,关闭genymotion,就能在cmd中操作模拟器。
3.接着使用drizzleDumper进行脱壳,下载好git软件,选中某个文件夹,右键Git bash here,输入git clone https://github.com/DrizzleRisk/drizzleDumper.git就可下载。
4.下好后再x86文件夹可以看到drizzleDumper文件,该文件就为脱壳的关键文件,在说它脱壳的原理前,先简单介绍一下Dex文件,apk程序第一次加载时,系统会把class.dex文件进行优化生成odex文件,这样做是为了提高程序的运行效率。
下面是dex的头部结构:
[Asm] 纯文本查看 复制代码 struct DexOptHeader {
u1 magic[8];
u4 dexOffset;
u4 dexLength;
u4 depsOffset;
u4 depsLength;
u4 optOffset;
u4 optLength;
u4 flags;
u4 checksum;
};
5.在它的头部结构中有一个magic[8]数组,这个数组的值是固定的,于是就可以当成特征值进行匹配,下面是find_magic_memory函数的代码,该函数是drizzleDumper工具的关键函数,在该函数中可以看出:先是读取整个内存文件,随后判断该文件是否为ELF文件和dex文件,然后与特征值进行匹配。
[C++] 纯文本查看 复制代码 int find_magic_memory(uint32_t clone_pid, int memory_fd, memory_region *memory , const char *file_name) {
......
while(fscanf(maps_file, "%[^\n]\n", mem_line) >= 0)
{
......
//获取内存文件的大小
memory->start = mem_start;
memory->end = strtoul(mem_address_end, NULL, 16);
int len = memory->end - memory->start;
......
//扫描内存
lseek64(memory_fd , 0 , SEEK_SET);
off_t r1 = lseek64(memory_fd , memory->start , SEEK_SET);
......
//判断内存文件是否为ELF文件
if(buffer[1] == 'E' && buffer[2] == 'L' && buffer[3] == 'F')
{
free(buffer);
continue;
}
//判断是否为dex文件
if(buffer[0] == 'd' && buffer[1] == 'e' && buffer[2] == 'x' && buffer[3] == '\n' && buffer[4] == '0' && buffer[5] == '3')
{
DexHeader header;
char real_lenstr[10]={0};
memcpy(&header , buffer ,sizeof(DexHeader));
//对dex文件进行dump
if(dump_memory(buffer , len , each_filename) == 1)
{
printf(" [+] dex dump into %s\n", each_filename);
free(buffer);
continue;
}
else
{
printf(" [+] dex dump error \n");
}
}
free(buffer);
}
}
那么脱壳的原理是:在root的权限下运行该工具时,使用ptrace附加到apk程序,接着使用find_magic_memory函数遍历读取内存中的文件并使用ODEX的magic对每个文件进行特征值匹配,匹配好后就找到了相应的dex文件并进行内存dump。
6.开启genymotion中的虚拟机,接下来进行如下脱壳操作:
[Asm] 纯文本查看 复制代码
C:\Users\Lenovo>cd E:\职业\github\drizzleDumper\libs\x86
C:\Users\Lenovo>e:
E:\职业\github\drizzleDumper\libs\x86>dir
驱动器 E 中的卷是 文档
卷的序列号是 B8A0-6DDD
E:\职业\github\drizzleDumper\libs\x86 的目录
2020/03/19 11:50 <DIR> .
2020/03/19 11:50 <DIR> ..
2020/03/19 11:50 9,532 drizzleDumper
1 个文件 9,532 字节
2 个目录 313,587,281,920 可用字节
E:\职业\github\drizzleDumper\libs\x86>adb push drizzleDumper /data/local/tmp
drizzleDumper: 1 file pushed. 0.1 MB/s (9532 bytes in 0.117s)
E:\职业\github\drizzleDumper\libs\x86>adb shell chmod 0777 /data/local/tmp/drizzleDumper
E:\职业\github\drizzleDumper\libs\x86>adb shell
root@android:/ # su
root@android:/ # pm list packages
...
package:top.phrack.ctf.crazyandroid
package:com.noshufou.android.su
...
root@android:/ # cd /data/local/tmp
root@android:/data/local/tmp # ls
...
CrazyAndroid.apk
drizzleDumper
...
root@android:/data/local/tmp #./drizzleDumper top.phrack.ctf.crazyandroid
...
[*] Try to Find top.phrack.ctf.crazyandroid
[*] pid is 1316
[*] clone pid is 1327
[*] ptrace [clone_pid] 1327
[*] Scanning dex ...
...
[+]Done
C:\Users\Lenovo>adb pull /data/local/tmp/top.phrack.ctf.crazyandroid_dumped_5746.dex E:\0ODprogram
/data/local/tmp/top.phrack.ctf.crazyandroid_dumped_5746.dex: 1 file pulled. 3.9 MB/s (868352 bytes in 0.213s)
接着top.phrack.ctf.crazyandroid_dumped_5746.dex放入反编译工具,就可以查看Java代码,把不过在下面的分析中,可能用不上脱壳后的dex文件,因为解题的逻辑都会在so层,而不会在activity层,不过脱壳后可以方便调式.在第二部分涉及到动态调式,输入的用户名为:pctf(题目要求),通过动态调式获取某些字符串的具体值,具体怎样动态调式,我就不说了,比较简单,网上有教程。
2.分析so文件
1.用ida加载so文件,没有找到JNI_OnLoad函数,接着寻找Java_类名_方法名的函数,找到Java_top_phrack_ctf_crazyandroid_MainActivity_CheckSerial,进去发现里面的流程图结构非常复杂,感觉代码混淆了。
2.接着摁f5分析伪代码,发现里面调用了hehe1、hehe2、hehe3、hehe4函数,感觉这四个函数应该是寻找突破的地方,从程序中可以看出当四个函数都返回1时,注册才能成功。
3.进入这四个函数,发现它们的流程结构非常复杂,感觉也加了混淆,但内部逻辑还是看得清楚,在hehe1函数中,发现把输入注册码的前40个字符赋值给某字符串,并且检验注册码前6位是否等于Pre6,从此看出:注册码的长度为40且前6位为Pre6。
4.在hehe2函数中,调用了crazy函数,该函数传入的参数为:进入crazy函数,找到关键代码处,看到一些混淆代码行,但这些混淆的代码不影响代码的逻辑分析,该函数主要是对某字符串进行处理新的字符串。
5.在下面可以看出hehe2函数,先是判断input[23]和input[6]是否为'-',接着对input[6:23]字串进行值变换。
6.在hehe3函数中,可以看出判断input[25:31]是否为v2,动态调式发现v2为Java代码传来的参数,为170501
7.在hehe4函数中,可以看到padding字符串,使用idapython脚本,可以看出该字符串的值,脚本见下。
[Python] 纯文本查看 复制代码 def Getpadding(str_addr):
tpadding=''
while(1):
#判断循环是否结束
if hex(Byte(str_addr))=='0x0':
break
#叠加字符串字符生成字符串
tpadding+=chr(Byte(str_addr))
str_addr+=1
return tpadding
print Getpadding(0x6046)
运行结果:
8.动态分析发现crazy(padding, 88)返回值为'075e191fe314c1e7917d9c71f7b6ed9842090f28f649bad384d0880d103b99a8',随后把返回拼接到input字符串的后面,通过代码可以看出截取input的长度为30,把拼接后的字符串进行运算操作,结果为2765405600。
9.最后把hehe4生成的序列值拼接到input后面,随后对input进行字符变换,生成的字符串为flag,生成的flag的代码如下:
[Python] 纯文本查看 复制代码 # -*- coding:utf-8 -*-
from random import choice
pre6='pctfef'
hehe3_8="Pctf2016"
hehe3_8_o=[]
javastr='170501'
base='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+'
#把hehe3_8中的字符转换成十进制数
def Hehe3_8ToOrd():
for i in hehe3_8:
hehe3_8_o.append(ord(i))
'''
def GetBase(str_addr):
base=''
while(1):
#判断循环是否结束
if hex(Byte(str_addr))=='0x0':
break
#叠加字符串字符生成字符串
base+=chr(Byte(str_addr))
str_addr+=1
return base
'''
def Getpadding(str_addr):
tpadding=''
while(1):
#判断循环是否结束
if hex(Byte(str_addr))=='0x0':
break
#叠加字符串字符生成字符串
tpadding+=chr(Byte(str_addr))
str_addr+=1
return tpadding
def getlast():
#base=GetBase(0x6004)
last=base[-12:]
return last
def crazy(a,b):
t1=a<<4
t2=~b+1
t2=0xD0-t2
ans=(t2 ^ t1) | (t2 &t1 )
return ans & 0xFF
def hehe2():
AnsT=''
AllT=[]
AnsT=''
Hehe3_8ToOrd()
last=getlast()
for i in range(0,len(hehe3_8_o)):
t= []
for j in last:
for k in last:
if crazy(ord(j), ord(k)) == hehe3_8_o[i]:
t.append(j + k)
AllT.append(t)
for i in AllT:
#在列表I中,找出任意值
AnsT += choice(i)
AnsTL = list(AnsT)
index=[5,6,9,11,12,13,14,15]
i=0
#对列表值进行交换
while i <len(index):
i1=index[i]
i2=index[i+1]
i+=2
t1=AnsTL[i1]
t2=AnsTL[i2]
AnsTL[i1]=t2
AnsTL[i2]=t1
str1=''.join(AnsTL)
return str1
def hehe4():
padding = '075e191fe314c1e7917d9c71f7b6ed9842090f28f649bad384d0880d103b99a8'
str1=hehe2()
#AnsCode相当于input,len为30
AnsCode=pre6+'-'+str1+'-'+javastr
enstr = AnsCode + padding
ans = 0
for i in enstr:
t = ord(i) - (~(0x28 * ans) + 1)
ans = t & 0xFFFFFFFF
return AnsCode+str(ans)
keyTokey={}
def GetFlag():
str1=hehe4()
key1=base[26:52]+base[0:26]+base[-12:]
key2=key1[13:26]+key1[0:13]+key1[39:52]+key1[26:39]+key1[57:62]+key1[52:57]+key1[-2:]
#生成key的值对应字典
for i in range(0,len(key2)):
keyTokey[key2[i]] = key1[i]
flag=''
for i in str1:
flag+=keyTokey[i]
print 'flag: ',flag
GetFlag()
运行结果:
|
免费评分
-
参与人数 3 | 威望 +2 |
吾爱币 +103 |
热心值 +3 |
收起
理由
|
qtfreet00
| + 2 |
+ 100 |
+ 1 |
感谢发布原创作品,吾爱破解论坛因你更精彩! |
笙若
| |
+ 1 |
+ 1 |
感谢发布原创作品,吾爱破解论坛因你更精彩! |
为海尔而战
| |
+ 2 |
+ 1 |
我很赞同! |
查看全部评分
|
发帖前要善用【论坛搜索】功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。 |
|
|
|
|