【分析】【吾爱破解2014CrackMe大赛】【第十三组】
android逆向什么的几乎没接触过啊{:1_906:},都是些基本的操作(看着大大们的文章完成的),分析也是简单的分析,感兴趣的可以看看。写的有什么问题就请见谅了。一. 简单分析
先用apktools解开看,发现java层获取name和password后直接调用的libverify.so中的verify();
然后用ida打开libverify.so
unsigned __int64 __fastcall verify(unsigned int a1, int a2, int a3, unsigned int a4)
{
unsigned int v4; // r5@1
int v5; // r6@1
int v6; // r7@1
unsigned __int64 v8; // @1
v8 = __PAIR__(a4, a1);
v4 = a1;
v5 = a2;
v6 = a3;
if ( !sub_12A0() )
((void (__fastcall *)(unsigned int, int, int, _DWORD))*off_4F64)(v4, v5, v6, HIDWORD(v8));
return v8;
}
看见他先调用了sub_12A0,通过之才去真正验证的地方
signed int __fastcall sub_12A0()
{
void *v0; // r6@1
signed int v1; // r4@1
__pid_t v2; // r0@1
FILE *v3; // r5@1
char *v4; // r0@3
signed int result; // r0@9
int v6; // @4
char s; // @1
int v8; // @1
v0 = off_4F60;
v1 = 1;
v8 = *(_DWORD *)off_4F60;
memset(&s, 0, 0x64u);
v2 = getpid();
sprintf(&s, "/proc/%d/status", v2);
v3 = fopen(&s, "r");
if ( v3 )
{
while ( fgets(&s, 99, v3) )
{
v4 = strstr(&s, "TracerPid:");
if ( v4 )
{
sscanf(v4 + 10, "%d", &v6);
v1 = v6;
if ( !v6 )
goto LABEL_8;
break;
}
}
v1 = 1;
LABEL_8:
fclose(v3);
}
result = v1;
if ( v8 != *(_DWORD *)v0 )
_stack_chk_fail(v1);
return result;
}
我对linux了解的也不多,但是看见TracerPid就知道是在检测调试器了(跟踪的进程嘛)。
然后去看看真正验证的地方,发现什么都没有。想想也知道,肯定是加密了。需要调试安卓,想想就头疼。
查了几篇看雪、吾爱上的文章,还是决定要试试,虽然没有可以用的手机,但虚拟机也完全没问题。
二.ida调试
需要下载android sdk(就用个虚拟机),还有就是看雪前辈给的ida6.5
用到的adb命令在sdk/platform-tools上有,调试用的android_server在ida的dbgsrv文件夹下。
先打开虚拟机,安装apk,可以用adb install CrackMe.apk 来安装。
之后打开cmd具体操作如下:
开启监听后,不要关闭cmd。打开CrackMe程序,打开ida,Debugger->Attcah->Remote ARMLinux/Android debugger
选择运行着的CrackMe52进程
ok,到这里就可以调试了。
三.动态跟踪
打开Modules选择libverify.so,找到verify,并下好断点。
运行到此处不让它跳(前面讲得检测调试器)
之后就进入真正验证的地方了
四.算法分析(截图或者代码是分几次搞的,地址不一样)
验证部分:
int __fastcall sub_492EECD4(int a1, int a2, int a3, int a4)
{
int v4; // r5@1
int v5; // r6@1
int result; // r0@1
int v7; // r4@3
int Name; // r0@3
int v9; // r3@3
char *v10; // r3@8
int v11; // r6@11
int Password; // r7@11
signed int v13; // r4@11
int v14; // r1@12
char v15; // cf@13
int v16; // r6@14
int v17; // r4@15
int (__fastcall *v18)(_DWORD, _DWORD, _DWORD, _DWORD); // r7@16
int v19; // r7@16
int v20; // r0@17
int jName; // @1
int jPassword; // @1
int v23; // @1
unsigned int v24; // @1
char v25; // @1
char NamePad; // @4
char charTable; // @1
int v28; // @1
v23 = a2;
jPassword = a4;
v4 = a1;
jName = a3;
v28 = _stack_chk_guard;
memcpy_0(charTable, "0123456789QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm", 63);
v5 = 0;
v24 = 0x213F2E2Cu;
v25 = 0;
result = (*(int (__fastcall **)(int, int))(*(_DWORD *)v4 + 672))(v4, jPassword);// GetStringLength
if ( result == 12 )
{
result = (*(int (__fastcall **)(int, int))(*(_DWORD *)v4 + 672))(v4, jName) - 8;// GetStringLength
if ( (unsigned int)result <= 0xC )
{
v7 = (*(int (__fastcall **)(int, int))(*(_DWORD *)v4 + 672))(v4, jName);// GetStringLength
Name = (*(int (__fastcall **)(int, int, _DWORD))(*(_DWORD *)v4 + 676))(v4, jName, 0);// GetStringUTFChars
v9 = 0;
if ( v7 <= 12 )
{
while ( v9 < v7 )
{
NamePad = *(_BYTE *)(Name + v9);
++v9;
}
v10 = (char *)&v24 - v7;
while ( v7 != 12 ) // ,.?! pad
{
NamePad = v10;
++v7;
}
}
else
{
do
{
NamePad = *(_BYTE *)(Name + v5);
++v5;
}
while ( v5 != 12 );
}
v11 = 0;
Password = (*(int (__fastcall **)(int, int, _DWORD))(*(_DWORD *)v4 + 676))(v4, jPassword, 0);// GetStringUTFChars
v13 = 11;//从最后开始循环
while ( 1 )
{
v11 += (unsigned __int8)NamePad;//累加
v14=62;
result = ((int (__fastcall *)(int, signed int))sub_492EEF2C)(v11, v14);
if ( (unsigned __int8)charTable != *(_BYTE *)(Password + v13) )
break;
v15 = (unsigned int)v13-- >= 1;
if ( !v15 )
{
result = (*(int (__fastcall **)(int, _DWORD))(*(_DWORD *)v4 + 24))(v4, "android/widget/Toast");
v16 = result;
if ( result )
{
result = (*(int (__fastcall **)(int, int, _DWORD, _DWORD))(*(_DWORD *)v4 + 452))(
v4,
result,
"makeText",
"(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;");
v17 = result;
if ( result )
{
v18 = *(int (__fastcall **)(_DWORD, _DWORD, _DWORD, _DWORD))(*(_DWORD *)v4 + 456);
(*(void (__fastcall **)(int, _DWORD))(*(_DWORD *)v4 + 668))(v4, "Congratulation! You crack it!");
result = v18(v4, v16, v17, v23);
v19 = result;
if ( result )
{
v20 = (*(int (__fastcall **)(int, int, _DWORD, _UNKNOWN *))(*(_DWORD *)v4 + 132))(
v4,
v16,
"show",
&unk_492F053C);
result = (*(int (__fastcall **)(int, int, int))(*(_DWORD *)v4 + 244))(v4, v19, v20);
}
}
}
break;
}
}
}
}
if ( v28 != _stack_chk_guard )
result = ((int (__fastcall *)(int))unk_492EEC68)(result);
return result;
}
其中需要知道sub_492EEF2C是干什么的,就跟进去看了下,里面还会调用sub_492EEE58
int __fastcall sub_492EEE58(int result, unsigned int a2)
{
char v2; // nf@0
int v3; // r12@1
int v4; // r3@4
char v5; // r0@8
unsigned int v6; // r1@8
unsigned int v7; // r2@8
char v8; // zf@17
v3 = result ^ a2;
if ( v2 )
a2 = -a2;
if ( a2 == 1 )
{
if ( (v3 ^ result) < 0 )
result = -result;
}
else
{
v4 = result;
if ( result < 0 )
v4 = -result;
if ( v4 <= a2 )
{
if ( v4 < a2 )
result = 0;
if ( v4 == a2 )
result = (v3 >> 31) | 1;
}
else if ( a2 & (a2 - 1) )
{
v5 = __clz(a2) - __clz(v4);
v6 = a2 << v5;
v7 = 1 << v5;
result = 0;
while ( 1 )
{
if ( v4 >= v6 )
{
v4 -= v6;
result |= v7;
}
if ( v4 >= v6 >> 1 )
{
v4 -= v6 >> 1;
result |= v7 >> 1;
}
if ( v4 >= v6 >> 2 )
{
v4 -= v6 >> 2;
result |= v7 >> 2;
}
if ( v4 >= v6 >> 3 )
{
v4 -= v6 >> 3;
result |= v7 >> 3;
}
v8 = v4 == 0;
if ( v4 )
{
v7 >>= 4;
v8 = v7 == 0;
}
if ( v8 )
break;
v6 >>= 4;
}
if ( v3 < 0 )
result = -result;
}
else
{
result = (unsigned int)v4 >> (31 - __clz(a2));
if ( v3 < 0 )
result = -result;
}
}
return result;
}
好吧,这个怎么看都想是稍微有点复杂的算法。可是...
分析了下发现这个就是个a/b,tm就是个除法,在百度了下ARM汇编没有除,就是这么干的...
之后就知道了sub_492EEF2C也就是个除法,返回之后R1是商,R2是余数。
所以这个算法就是
int sum=0;
bool ret=true;
for(i = 11 to 0)
{
sum+=name;
if(charTable!=password)
{
ret=false;
break;
}
}
注册机也就好些了:
#include<iostream>
using namespace std;
char *charTable="0123456789QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm";
char *pad=",.?!";
char name;
char password;
int main()
{
memset(name,0,13);
memset(password,0,13);
cout<<"请输入用户名:"<<endl;
cin>>name;
int len=strlen(name);
if(len>=8 && len<=12)
{
int i=0,sum=0;
for(i=len;i<12;i++)
{
name=pad;
}
name=0;
for(i=11;i>=0;i--)
{
sum+=name;
password=charTable;
}
cout<<"注册码为:"<<endl<<password<<endl;
}
else
{
cout<<"长度必须为8~12"<<endl;
}
system("pause");
return 0;
}
两组key:
12345678
dmWDVyfx0ONB
52pojie0
4RHwg6Hd0ONB
以上仅是简单的分析,对什么加壳脱壳的还一概不了解(题主大大在看雪有几篇精华文章,可惜还看不懂)。
@〇〇木一 用IDA打开libverify.so文件,之后你写的代码是你自己分析写出来的,还是IDA反编译出来的,我知道IDA可以进行反编译,但是这种LOAD:000012A0 PUSH {R4-R6,LR}也可以进行反编译吗?我的F5都不可以进行这个的反编译,求指导 bess 发表于 2014-11-13 11:00
@〇〇木一 用IDA打开libverify.so文件,之后你写的代码是你自己分析写出来的,还是IDA反编译出来的,我知 ...
http://bbs.pediy.com/showthread.php?t=191169 楼主好强大,学习一下 感谢分享!!!!!!! 多看看,再学学 验证的那一段不是太懂 楼主好强大,学习一下 膜拜大神 活到老,学到老!!加油。
页:
[1]
2