专栏名称: 吾爱破解论坛
吾爱破解论坛致力于软件安全与病毒分析的前沿,丰富的技术版块交相辉映,由无数热衷于软件加密解密及反病毒爱好者共同维护,留给世界一抹值得百年回眸的惊艳,沉淀百年来计算机应用之精华与优雅,任岁月流转,低调而奢华的技术交流与探索却
目录
相关文章推荐
看雪学苑  ·  Windows PE 文件签名的解析与验证 ·  2 天前  
学习强国  ·  月球上能上网吗?能种菜吗?最新回应! ·  4 天前  
学习强国  ·  月球上能上网吗?能种菜吗?最新回应! ·  4 天前  
嘶吼专业版  ·  Windows 漏洞利用盲文“空格”进行零日攻击 ·  6 天前  
看雪学苑  ·  SDC 安全训练营——8小时实战教学 ·  1 周前  
肖飒lawyer  ·  飒姐团队 | ... ·  6 天前  
51好读  ›  专栏  ›  吾爱破解论坛

【Android 原创】L-ctf 2016 两道安卓题浅析

吾爱破解论坛  · 公众号  · 互联网安全  · 2016-10-18 09:12

正文

这个比赛结束没多久,个人也就随便做了玩玩,两道题其实都不难,尤其是看上去比较难的第二道题,是需要靠投机取巧的

题目一:
不得不说界面真渣,不过现在真正搞安卓的都是c大牛,都不玩java的

接下来就需要自己去分析了,第一步当然看看有没有壳,没有就直接反编译看吧,个人比较喜欢jadx,下面都用jadx作为java反编译工具

   protected void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView((int) R.layout.activity_main);
        ApplicationInfo applicationInfo = getApplicationInfo();
        int i = applicationInfo.flags & 2;
        applicationInfo.flags = i;
        if (i != 0) {
            p();
            ((Button) findViewById(R.id.sureButton)).setOnClickListener(new d(this));   //d方法为触发点击事件的函数
        } else {
            p();
            ((Button) findViewById(R.id.sureButton)).setOnClickListener(new d(this));
        }
    }

    private void p() {
        try {
            InputStream open = getResources().getAssets().open("url.png"); //读取一张图片的值,从144位开始,读取16位数据,保存到全局变量v中
            int available = open.available();
            Object obj = new byte[available];
            open.read(obj, 0, available);
            Object obj2 = new byte[16];
            System.arraycopy(obj, 144, obj2, 0, 16);
            this.v = new String(obj2, "utf-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private boolean a(String str, String str2) {
        return new c().a(str, str2).equals(new String(new byte[]{(byte) 21, (byte) -93, (byte) -68, (byte) -94, (byte) 86, (byte) 117, (byte) -19, (byte) -68, (byte) -92, (byte) 33, (byte) 50, (byte) 118, (byte) 16, (byte) 13, (byte) 1, (byte) -15, (byte) -13, (byte) 3, (byte) 4, (byte) 103, (byte) -18, (byte) 81, (byte) 30, (byte) 68, (byte) 54, (byte) -93, (byte) 44, (byte) -23, (byte) 93, (byte) 98, (byte) 5, (byte) 59}));   //这里暂时看不出来什么东西,等分析完了就知道了
    }

看下d方法做了什么
public void onClick(View view) {
        if (this.a.a(this.a.v, ((EditText) this.a.findViewById(R.id.passCode)).getText().toString())) {
//拿到了输入框的内容,和之前的v变量,传入到a方法中进行了处理,这里的a方法就是上面的boolean类型函数
            TextView textView = (TextView) this.a.findViewById(R.id.textView);
            Toast.makeText(this.a.getApplicationContext(), "Congratulations!", 1).show();
            textView.setText(R.string.nice);
            return;
        }
        Toast.makeText(this.a.getApplicationContext(), "Oh no.", 1).show();
    }

返回到a函数中看到又调用了c类中的a方法,将两个值传入,跟入

public String a(String str, String str2) {
        String a = a(str); //调用a方法
        String str3 = "";
        a aVar = new a();  //实例化一个a类,跟入发现是个aes加密
        aVar.a(a.getBytes());
        try {
            return new String(aVar.b(str2.getBytes()), "utf-8");
        } catch (Exception e) {
            e.printStackTrace();
            return str3;
        }
    }

    private String a(String str) {
//这里就是将所有的字符以两位隔开,并交换两个字符的位置,通过for循环可以看出
        try {
            str.getBytes("utf-8");
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i                 stringBuilder.append(str.charAt(i + 1));
                stringBuilder.append(str.charAt(i));
            }
            return stringBuilder.toString();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return null;
        }
    }

aes加密代码:用用户输入数据作为加密数据,图片数据作为加密key
protected void a(byte[] bArr) {
        if (bArr == null) {
            try {
                this.a = new SecretKeySpec(MessageDigest.getInstance("MD5").digest("".getBytes("utf-8")), "AES");
                this.b = Cipher.getInstance("AES/ECB/PKCS5Padding");
                return;
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
                return;
            } catch (NoSuchAlgorithmException e2) {
                e2.printStackTrace();
                return;
            } catch (NoSuchPaddingException e3) {
                e3.printStackTrace();
                return;
            }
        }
        this.a = new SecretKeySpec(bArr, "AES");
        this.b = Cipher.getInstance("AES/ECB/PKCS5Padding");
    }

    protected byte[] b(byte[] bArr) {
        this.b.init(1, this.a);
        return this.b.doFinal(bArr);
    }

最后拿到加密后的数据与
new String(new byte[]{(byte) 21, (byte) -93, (byte) -68, (byte) -94, (byte) 86, (byte) 117, (byte) -19, (byte) -68, (byte) -92, (byte) 33, (byte) 50, (byte) 118, (byte) 16, (byte) 13, (byte) 1, (byte) -15, (byte) -13, (byte) 3, (byte) 4, (byte) 103, (byte) -18, (byte) 81, (byte) 30, (byte) 68, (byte) 54, (byte) -93, (byte) 44, (byte) -23, (byte) 93, (byte) 98, (byte) 5, (byte) 59})
进行对比,知道了key和加密算法,这里已经理清思路就知道怎么办了

写个解密方法,反响解密下或者比较省事的方法就是在先插桩打印出c类中对v变量进行位置交换的方法的返回值,这样拿到了aes的key,最后直接将上面的bytes数据直接解密就ok

题目二:
拿到后安装到模拟器上是无法打开的,判断是检测了模拟器
看了下也是没有壳的,直接反编译
入口处的主要代码如下:
((Button) findViewById(R.id.button)).setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                TextView textView = (TextView) MainActivity.this.findViewById(R.id.editText);
                MainActivity.this.this_is_your_flag = textView.getText().toString();
                if (MainActivity.this.this_is_your_flag.length()                     Process.killProcess(Process.myPid());
                } else if (MainActivity.this.this_is_your_flag.length() > 39) {判断用户数据是否大于35位
                    Process.killProcess(Process.myPid());
                }
//这里数据输入长度错误都会直接结束进程
                Format format = new Format();
                MainActivity.this.this_is_your_flag = format.form(MainActivity.this.this_is_your_flag); //实例化了Format类对字符串进行了截取,代码如下
                if (MainActivity.this.this_is_your_flag.length()                     Toast.makeText(MainActivity.this.getApplicationContext(), "No,more.", 1).show();
                } else if (new Check().check(MainActivity.this.this_is_your_flag)) {   //这里就是关键点了,调用了check方法
                    Toast.makeText(MainActivity.this.getApplicationContext(), "Congratulations!You got it.", 1).show();
                } else {
                    Toast.makeText(MainActivity.this.getApplicationContext(), "Oh no.Come on!", 1).show();
                }
            }
        });

protected String form(String input) {
        return input.substring(5, 38);
    }

    protected String fo1m(String input) {
        return input.substring(5, 36);
    }

    protected String forn(String input) {
        return input.substring(5, 39);
    }

    protected String f0rm(String input) {
        return input.substring(5, 37);
    }
//这里都是简单的对字符串进行截取,只是长度不一样

   private static String[] known_pipes = new String[]{"/dev/socket/qemud", "/dev/qemu_pipe"};
    String emulator = checkPipes();

    private native boolean checkPasswd(String str);

    protected native boolean checkEmulator(String str);

    public static String checkPipes() {
        for (String pipes : known_pipes) {
            if (new File(pipes).exists()) {
                return "true";
            }
        }
        return "false";
    }

    boolean check(String pass) {
        if (checkEmulator(this.emulator)) {
            return false;
        }
        return checkPasswd(pass);
    }

主要的check方法中第一步调用了checkEmulator,emulator在checkPipes中进行了初始化,这里简单的判断了下是否是模拟器,是的话直接返回false,所以做这类东西的时候还是在手机上做比较好,坑比较少
当然checkEmulator是个native方法,还需要看下so,如果过掉模拟器检测,最后就会真正的检测密码

ida打开下so

.text:00005074                 EXPORT Java_com_example_ring_wantashell_Check_checkEmulator
.text:00005074 Java_com_example_ring_wantashell_Check_checkEmulator
.text:00005074                 PUSH    {R4,R6,R7,LR}
.text:00005076                 ADD     R7, SP, #8
.text:00005078                 MOVS    R1, #0x2A4
.text:0000507C                 LDR     R3, [R0]
.text:0000507E                 LDR     R3, [R3,R1]
.text:00005080                 MOVS    R4, #0
.text:00005082                 PUSH    {R2}
.text:00005084                 POP     {R1}
.text:00005086                 PUSH    {R4}
.text:00005088                 POP     {R2}
.text:0000508A                 BLX     R3
.text:0000508C                 LDR     R1, =(aTrue - 0x5092)
.text:0000508E                 ADD     R1, PC          ; "true"
.text:00005090                 BL      j_j_strcmp  //这里就简单的判断下传入值和true是否相等,相等等于0,否则等于1
.text:00005094                 PUSH    {R0}
.text:00005096                 POP     {R1}
.text:00005098                 MOVS    R0, #1
.text:0000509A                 CMP     R1, #0
.text:0000509C                 BEQ     locret_50A2
.text:0000509E                 PUSH    {R4}
.text:000050A0                 POP     {R0}


由于原文较长,微信中无法全部发布,剩下内容请点击左下角“原文”进行阅读。

--官方论坛

www.52pojie.cn

--推荐给朋友

公众微信号:吾爱破解论坛

或搜微信号:pojie_52