吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3611|回复: 12
收起左侧

[Android CTF] 学破解第183天,《实战远古帖子-简单的纯java层crackme》学习

[复制链接]
小菜鸟一枚 发表于 2022-2-6 14:00

前言:

  坛友们,年轻就是资本,和我一起逆天改命吧,我的学习过程全部记录及学习资源:https://www.52pojie.cn/thread-1582287-1-1.html

立帖为证!--------记录学习的点点滴滴

0x1 锁定目标

  1.「一个Crackme」地址:https://www.52pojie.cn/thread-469526-1-1.html

  2.作者:纯java编写,无混淆,无壳,什么阻碍都没有,不可以爆破,欢迎练手。

  3.通过作者描述,无壳无CM,不涉及so文件,应该很简单,适合练手。

0x2 静态分析

  1.Androidkiller看一下反编译后的apk源码

      protected void onCreate(Bundle paramBundle)
        {
          super.onCreate(paramBundle);
          setContentView(2130903065);
          ((Button)findViewById(2131296336)).setOnClickListener(new View.OnClickListener()
          {
            public void onClick(View paramAnonymousView)
            {
              paramAnonymousView = this.val$edt.getText().toString().trim();
              if (paramAnonymousView.length() == 15)
              {
                int i = 0;
                for (;;)
                {
                  if (i >= 15)
                  {
                    if (!MainActivity.check(paramAnonymousView)) {
                      break;
                    }
                    Toast.makeText(MainActivity.this, "注册成功", 1000).show();
                  }
                  while ((paramAnonymousView.charAt(i) < '0') || (paramAnonymousView.charAt(i) > '9')) {
                    return;
                  }
                  i += 1;
                }
              }
              Toast.makeText(MainActivity.this, "请输入正确的注册码", 1000).show();
              return;
              Toast.makeText(MainActivity.this, "注册失败", 1000).show();
            }
          });
        }
      }

  2.从入口函数可以看到:

if (paramAnonymousView.length() == 15)  //判断输入的字符串长度必须等于15
 while ((paramAnonymousView.charAt(i) < '0') || (paramAnonymousView.charAt(i) > '9'))  //从这里可以看到输入的必须是0-9数字。

  3.这两个校验过了后,就调用check函数了,看一看check函数的反编译代码:

  public static boolean check(String paramString)
  {
    long l2 = 0L;
    char[] arrayOfChar = new char[6];
    if (paramString.length() != 15) {
      return false;
    }
    long l1 = 10000L;
    int i = 1;
    long l3;
    long l4;
    do
    {
      l3 = l2 + (paramString.charAt(i - 1) - '0') * l1;
      i += 1;
      l4 = l1 / 10L;
      l2 = l3;
      l1 = l4;
    } while (l4 > 0L);
    paramString = String.valueOf(((0xFF52 ^ l3) + 1555L) / 3L - 1555L).trim();
    l1 = paramString.length();
    paramString.getChars(0, (int)l1, arrayOfChar, 0);
    if (l1 < 4L)
    {
      l2 = 4L - l1;
      i = (int)(l1 - 1L);
      if (i < 0)
      {
        i = 0;
        label136:
        if (i < (int)l2) {
          break label172;
        }
      }
    }
    else
    {
      i = 1;
    }
    for (;;)
    {
      if (i > 4)
      {
        return true;
        arrayOfChar[((int)(i + l2))] = arrayOfChar[i];
        i -= 1;
        break;
        label172:
        arrayOfChar[i] = '0';
        i += 1;
        break label136;
      }
      if (arrayOfChar[(i - 1)] != "104672819202".charAt(i - 1)) {
        return false;
      }
      i += 1;
    }
  }

  4.先从return true开始找,一个for循环,i=5就返回true,继续看同一个代码块false的地方。

if (arrayOfChar[(i - 1)] != "104672819202".charAt(i - 1))   //从字符数组取对应下标的值然后比较

  5.通过上述分析可以知道是从"104672819202"中取出值和我们的字符串经过处理后的字符做比较。

0x3 动态调试

  1.前面分析的差不多了,算法那一块不想硬着头皮啃,现在动态调试看看有没有什么线索,在check函数这里下断点,步骤如下:

//打开sdk目录下tool下ddms.bat工具

//显示顶层窗口
dumpsys activity top

//adb shell窗口,以debug模式启动
am start -D -n com.example.crackme/.MainActivity

//JEB附加程序

//继续跑起来
jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=8612 

  2.app启动后,输入15个1,成功断在了check函数这里。

https://s4.ax1x.com/2022/02/06/Hu1ofH.png

  3.继续单步往下走,不认识的指令百度一下,然后打个注释:

add-int/lit8        v10, v3, -1   //v3+(-1)的值给v10。

mul-long/2addr vx,vy   //计算 vx*vy 并将结果赋值到 vx

div-long/2addr vx, vy 计算 vx/vy 并将结果赋值到 vx

cmp-long vx, vy, vz 比较 long 值 vy 和 vz 并设置整数 赋值到 vx

  4.小菜鸟急性子,一路向下单步快走,就不慢慢看了,直接截图:

1)先经过一个循环,从我输入的字符串中取出前5位。

https://s4.ax1x.com/2022/02/06/Hu8eqP.png

2)if-gez v10这是一个跳转,v10大于0就跳转。

https://s4.ax1x.com/2022/02/06/Hu8qdf.png

3)通过下标取值然后比较,第一轮1和1比较。

https://s4.ax1x.com/2022/02/06/HuJR8e.png

4)可以看到最终取的是104672819202的前5位和我输入的字符串处理后做比较,return true后出现注册成功的提示。

0000003C  move-result         v2
0000003E  if-eqz              v2, :98
:42
00000042  iget-object         v2, p0, MainActivity$1->this$0:MainActivity
00000046  const-string        v3, "注册成功"

  5.现在知道最终比较的字符串是10467,但是我输入15个1,最终变成17071,也就是说字符串会经过一定处理。

0x4 flag跟踪

  1.为了方便调试,准备一个java开发工具:https://www.eclipse.org/downloads/,vs code不太会用,调试app,不想用vs2015写代码,用java开发工具直接将代码复制粘贴就能跑,不用看懂代码。

  2.先把上面那段代码复制进去,打上断点,debug跑起来,鼠标放在变量上就可以看到值,也可以点变量watch监视。

package ctf;

public class test01 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String paramString = "111111111111111";

        long l2 = 0L;
        char[] arrayOfChar = new char[6];
        if (paramString.length() != 15) {
          return;
        }
        long l1 = 10000L;
        int i = 1;
        long l3;
        long l4;
        do
        {
          l3 = l2 + (paramString.charAt(i - 1) - '0') * l1;
          i += 1;
          l4 = l1 / 10L;
          l2 = l3;
          l1 = l4;
        } while (l4 > 0L);
        paramString = String.valueOf(((0xFF52 ^ l3) + 1555L) / 3L - 1555L).trim();
        System.out.println(paramString);
    }

}

  3.开始分析:

1)l2的值是0,然后初始化了arrayOfChar字符数组,然后长度15校验。

2)l1赋值10000,i = 1,l3和l4默认值0,接下来是一个do while循环,退出条件是l4>0,l4在循环体内每次除以10,也就是说循环体共执行5次。

3)paramString的值最后只有l3参与了运算,l3是我们需要关注点,l3每次循环的值:

第一轮:l3的值是我们输入字符串中的第1个字符(下标为0)数字化乘以10000。

第二轮:l3的值是我们输入字符串中的第2个字符(下标为1)数字化乘以1000,再加上l2(也就是上一轮的l3)。

......

https://s4.ax1x.com/2022/02/06/HuwISK.png

  4.接下来就是要分解这一句代码:String.valueOf(((0xFF52 ^ l3) + 1555L) / 3L - 1555L).trim();

1)设x为falg,x=(0xFF52 ^ l3) + 1555) / 3 - 1555

2)现在已知x是10467,求l3的值。

3)变换一元一次方程:(0xFF52 ^ l3)+1555 = 3*(x+1555),再推(0xFF52 ^ l3)=3x+3110,再推l3 = (3x+3110)^ 0xFF52。

4)解得l3 = 31133。

  5.按照前面的分析思路逆推do while循环,可得的flag就是31133开头的字符串即可。

万位是第一个字符乘以10000,所以第一个字符是3

千位是第二个字符乘以1000,所以第二个字符是1

百位是第三个字符乘以100,所以第三个字符是1

个位是第四个字符乘以10,所以第四个字符是3

十位是第五个字符乘以1,所以第五个字符是3

  6.输入31133开头的字符串,然后验证一下,注册成功。

https://s4.ax1x.com/2022/02/06/HuBa80.png

0x5 总结

  1.看了下,坛友分享的过程,取出输入的前五位进行次方运算,然后扔掉后两位,只是比较前4位,本题存在多解。

  2.后面if大于4,i从1开始,比较前四位,这个我理解了,次方运算有点没明白,0xff52对应十进制是65362。

  3.把前面的思路汇总整理一下,写一下穷举代码:

package ctf;

public class test01 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        int temp;
        for(int i =10000;i<100000;i++)//写一个for循环,穷举5位数
        {
            temp = ((0xFF52 ^ i) + 1555) / 3 - 1555;

            if(temp>10000)//如果大于5位去掉1位
            temp = temp/10;

            if((temp) == 1046)//如果前4位等于1046,就输出
            {
                System.out.println(i);
            }
        }   
    }
}

  4.运行输出,和坛友分析的一样,下列5位数开头的15位字符串都能注册成功。

31104
31105
31106
31107
31108
31109
31110
31111
31120
31121
31122
31123
31124
31125
31126
31127
31128
31129
31130
31131
31132
31133
31134
31135
31208
31209
31212
31213
31214
31215
59192
59194
59195

0x6 参考资料

  1.smali语法 便于自己记住

  2.坛友分析过程

免费评分

参与人数 4威望 +1 吾爱币 +24 热心值 +4 收起 理由
qtfreet00 + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
wildbloom + 1 + 1 热心回复!
debug_cat + 2 + 1 谢谢@Thanks!
zjun777 + 1 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

为了明天666 发表于 2022-2-6 16:35
楼主厉害学到了
景寒杏 发表于 2022-2-6 16:38
我爆破了……

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
小菜鸟一枚 + 1 + 1 你的方法简单,欢迎交流

查看全部评分

封狼居胥l7l 发表于 2022-2-6 21:55
头像被屏蔽
86618513 发表于 2022-2-7 02:33
提示: 作者被禁止或删除 内容自动屏蔽
dazhujianhui 发表于 2022-2-8 11:08

厉害真的学到了
yuanyxh 发表于 2022-2-8 11:16
感谢大佬分享
1101902800 发表于 2022-2-8 12:51
NB, 感谢大佬分享
李佑辰 发表于 2022-2-8 15:39
分析的不错  学到啦 学到啦!!
pojie6033 发表于 2022-2-8 22:35
分析得很清晰,学习了,感谢楼主
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-5-13 22:23

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表