首页
社区
课程
招聘
[旧帖] [原创]一个android的apk的注册码分析 0.00雪花
发表于: 2014-5-15 15:37 3029

[旧帖] [原创]一个android的apk的注册码分析 0.00雪花

2014-5-15 15:37
3029
新手一枚,为论坛增加学习氛围。帮同学找apk的注册码,发现其中的一些东西还是很值得分享的。

新人一枚。帮同学找apk的注册码,发现其中的一些东西还是很值得分享的。

首先,运行Crackme001.apk获取信息,了解大致运行过程。
将其安装到andorid虚拟机上,点击运行,发现闪退,无法运行,估计可能有保护机制。于是只能放到手机上去运行:


输入用户名和注册码,点击注册后没有反应。

利用apktool进行反编译。命令行中输入apktool d Crackme001.apk后,得到反编译文件。
打开MainActivity.smali文件,从onCreate函数看起:
.method protected onCreate(Landroid/os/Bundle;)V
    .locals 1
    .parameter "savedInstanceState"

    .prologue
    .line 20
    invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V

    .line 21
    const/high16 v0, 0x7f03

    invoke-virtual {p0, v0}, Lcom/syclover/crackme001/MainActivity;->setContentView(I)V

    .line 24
    invoke-static {}, Lcom/syclover/crackme001/MainActivity;->hasQEmuFiles()Z   
    move-result v0

    if-eqz v0, :cond_0   #等于0则不进行killProcess,否则killProcess

    .line 25
    invoke-static {}, Landroid/os/Process;->myPid()I

    move-result v0

    invoke-static {v0}, Landroid/os/Process;->killProcess(I)V     #终结进程

    .line 28
    :cond_0
    return-void
.end method
发现其中会调用hasQEmuFiles()函数,对其返回值进行判断,若返回非0,则终结进程,这说明hasQEmuFiles()函数是保护机制中的关键。现在来分析一下hasQEmuFiles(),过程如下:

#此函数用来初始化静态类成员know_file(字符串数组)
# direct methods
.method static constructor <clinit>()V
    .locals 3

    .prologue
    .line 30
    const/4 v0, 0x3

    new-array v0, v0, [Ljava/lang/String;

    const/4 v1, 0x0

    .line 31
    const-string v2, "/system/lib/libc_malloc_debug_qemu.so"  #第一个数组元素为“/system/lib/libc_malloc_debug_qemu.so”

    aput-object v2, v0, v1

    const/4 v1, 0x1

    const-string v2, "/sys/qemu_trace"   #第二个数组元素为“/sys/qemu_trace”

    aput-object v2, v0, v1

    const/4 v1, 0x2

    .line 32
    const-string v2, "/system/bin/qemu-props"  #第三个数组元素"/system/bin/qemu-props"

    aput-object v2, v0, v1

    .line 30
    sput-object v0, Lcom/syclover/crackme001/MainActivity;->known_file:[Ljava/lang/String;

    .line 32
    return-void
.end method

#通过检测特定的文件是否存在于文件系统中来判断是否在android虚拟机中
.method public static hasQEmuFiles()Z
    .locals 7

    .prologue
    const/4 v2, 0x0

    .line 35
    sget-object v4, Lcom/syclover/crackme001/MainActivity;->known_file:[Ljava/lang/String;  #获取存放文件名的静态字符串数组

    array-length v5, v4    #求得数组长度,存放在v5中

    move v3, v2

    .local v0, pipe:Ljava/lang/String;
    .local v1, qemu_files:Ljava/io/File;
    :goto_0
    if-lt v3, v5, :cond_0   #小于则跳转到cond_0继续执行

    .line 41
    :goto_1
    return v2         #若有一个文件存在于文件系统,返回1,说明程序运行在android虚拟机中;若文件均不存在,返回0,说明程序运行在真机中

    .line 35
    :cond_0
    aget-object v0, v4, v3    #读出文件名数组中的第V3个元素,放到V0中
  
    .line 36
    new-instance v1, Ljava/io/File;

    .end local v1           #qemu_files:Ljava/io/File;
    invoke-direct {v1, v0}, Ljava/io/File;-><init>(Ljava/lang/String;)V   #利用v0中的文件名字符串初始化File对象

    .line 37
    .restart local v1       #qemu_files:Ljava/io/File;
    invoke-virtual {v1}, Ljava/io/File;->exists()Z   #查找文件系统中是否存在相应的文件,若存在,返回true,否则返回false

    move-result v6

    if-eqz v6, :cond_1    #不存在则跳转到cond_1

    .line 38
    const/4 v2, 0x1   

    goto :goto_1        #存在,跳转到goto_1(继而返回1)

    .line 35
    :cond_1
    add-int/lit8 v3, v3, 0x1

    goto :goto_0
.end method

这样,从hasQEmuFiles()函数里可以发现,程序通过检查运行环境的文件系统中是否存在:
1. /system/lib/libc_malloc_debug_qemu.so
2. /sys/qemu_trace
3. /system/bin/qemu-props
这三个文件来判断运行环境是否是android虚拟机,若至少存在其中一个文件,则说明运行环境是android虚拟机,而非真机。
我自己去查看了一下android虚拟机里头的文件,发现确实是有这几个文件:



我觉得这个保护机制还是挺值得借鉴的。
分析完了保护机制,再来继续找注册码。
在MainActivity.smali文件中发现了关键函数,分析如下:
# virtual methods
.method public OnMySelfClick(Landroid/view/View;)V
    .locals 6
    .parameter "v"

    .prologue
    .line 46
    const v3, 0x7f080002

    invoke-virtual {p0, v3}, Lcom/syclover/crackme001/MainActivity;->findViewById(I)Landroid/view/View;

    move-result-object v2   #获取username的EditText对象

    check-cast v2, Landroid/widget/EditText;

    .line 47
    .local v2, username:Landroid/widget/EditText;
    const v3, 0x7f080003

    invoke-virtual {p0, v3}, Lcom/syclover/crackme001/MainActivity;->findViewById(I)Landroid/view/View;

    move-result-object v0   #获取password的EditText对象
    check-cast v0, Landroid/widget/EditText;

    .line 50
    .local v0, password:Landroid/widget/EditText;
    :try_start_0
    invoke-virtual {v2}, Landroid/widget/EditText;->getText()Landroid/text/Editable;

    move-result-object v3

    invoke-interface {v3}, Landroid/text/Editable;->toString()Ljava/lang/String;

    move-result-object v3   #获取username字符串放在v3

    .line 51
    invoke-virtual {v2}, Landroid/widget/EditText;->getText()Landroid/text/Editable;

    move-result-object v4

    invoke-interface {v4}, Landroid/text/Editable;->toString()Ljava/lang/String;

    move-result-object v4   #获取username字符串放在v4

    .line 50
    invoke-static {v3, v4}, Lcom/syclover/crackme001/DES;->encode(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;

    move-result-object v3     #调用DES类中的encode()进行加密,返回值存放在v3

    .line 51
    const/4 v4, 0x0

    const/4 v5, 0x6

    invoke-virtual {v3, v4, v5}, Ljava/lang/String;->substring(II)Ljava/lang/String;

    move-result-object v1    #截取v3中字符串的0~6部分存放在v1

    .line 54
    .local v1, pwdString:Ljava/lang/String;
    invoke-virtual {v0}, Landroid/widget/EditText;->getText()Landroid/text/Editable;

    move-result-object v3

    invoke-interface {v3}, Landroid/text/Editable;->toString()Ljava/lang/String;

    move-result-object v3     #获取password字符串

    .line 55
    invoke-static {}, Ljava/util/Locale;->getDefault()Ljava/util/Locale;

    move-result-object v4      #获取locale

    invoke-virtual {v1, v4}, Ljava/lang/String;->toLowerCase(Ljava/util/Locale;)Ljava/lang/String; #toLowerCase的功能:Converts this string to lower case, using the rules of locale.

    move-result-object v4    #结果存放在v4

    invoke-virtual {v3, v4}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z    #关键,比较v3和v4是否相等

    move-result v3

    if-eqz v3, :cond_0      #不相等则跳转到cond_0

    .line 56
    const-string v3, "\u6ce8\u518c\u6210\u529f"

    const/4 v4, 0x0

    invoke-static {p0, v3, v4}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;

    move-result-object v3

    .line 57
    invoke-virtual {v3}, Landroid/widget/Toast;->show()V       #显示注册成功
    :try_end_0
    .catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0

    .line 63
    .end local v1           #pwdString:Ljava/lang/String;
    :cond_0
    :goto_0
    return-void
分析完成后,为了获取到注册码,我想可以利用代码注入法将正确的注册码输出来,但是在重新编译时候出了一些问题,感觉这个方法行不通。考虑之后自己决定顺着apk的思路来,写出它的注册机。
要写注册机,就得对注册码的生成过程进行还原,我想这时可以利用dex2jar将apk反编译得到java源码,看java源码来写apk定是极好的。
用jd-gui将.jar打开,找到关键部分:(感觉好简洁)


除此之外,DES类中的相关函数也是必要的:


所有的函数都在这儿了,只需要粘贴复制,修修改改,就可以写出一个生成注册码的apk来了:


测试发现,这个注册码的生成还是有缺陷的:
用户名至少得8位,而且注册码只和用户名的前八位有关联,和后面的位均无关。

求各位看雪朋友交流,共同进步!

[课程]FART 脱壳王!加量不加价!FART作者讲授!

上传的附件:
收藏
免费 1
支持
分享
最新回复 (3)
雪    币: 35
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
不错的东西,谢谢分享!
2014-5-15 15:51
0
雪    币: 270
活跃值: (239)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
3
第一次写,,,很高兴有人看!!
2014-5-15 15:53
0
雪    币: 37
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
不错不错,受教了!
2014-5-16 21:42
0
游客
登录 | 注册 方可回帖
返回
//