【PoRE】Lab4: Smali2Java

回到目录

内容总结

  • 这个Lab与Lab 3“相反”:Lab 3是根据提供的或自己写的Java代码改写为Smali代码;而本Lab是根据Smali代码重写为Java代码。总的来说,这个Lab更符合期末考试的真实情境。
  • 如课件的Reverse Smali中展示的一样,课程内容大致如下:

控制流图(CFG, Control Flow Graph),能够清晰地展现出一个函数中的控制流的转换与流程。其中需要注意如循环结构、分支结构、以及错误处理(try-catch)结构的处理。期末考试虽然并不要求画出正确的CFG,但按照个人经历来说,由于不少循环和分支的存在,画出一个大概的CFG确实能给解题带来很大的帮助。

伪代码生成(Pseudo-code Generation),在本Lab和课程的实践中,需要按照Smali代码以及对应的CFG整理和构建一段正确的代码。其中需要注意几点:
  一个是变量的命名及处理,尤其对于那些临时变量。由于Smali代码自身的特点之一即允许寄存器复用,因此在使用和引用每个寄存器的时候,最重要的就是确定是什么类型。此外,中间变量也建议进行处理,譬如传参时的处理,以避免生成过于冗长的代码。
  goto结构的处理,尤其当使用到循环和分支结构时。虽然Java本身并无法使用goto语句,但出于控制流的转换,Smali语法中有goto语句以及对应的标签。借由这个Lab的实践,可以掌握循环和分支在Smali中的大概的结构,也可以通过绘画CFG验证是否是循环或分支。
  Java语句结构。对于循环,Java内可不止有while结构,还有for循环……

Lab简介与参考

  • 这个Lab一共分成两个Task,分别是Checker.java和Encoder.java。由于大部分都是Smali和CFG的直译,因此仅记录了成果。
  • Task1: Checker.java
public class Checker {
    //instance-field
    private byte[] bytes;

    //constructor
    public Checker() {
        bytes=new byte[]{0x70, 0x64, 0x64, 0x44, 0x1f, 0x5, 0x72, 0x78};
    }

    //private method: charToByteAscii
    private static byte charToByteAscii(char c) {
        return (byte)c;
    }

    //private method: checkStr1
    private boolean checkStr1(String s) {
        int i=0, j;
        while (i<s.length()) {
            int bi=charToByteAscii(s.charAt(i));
            j=i*11;
            bi=bi^j;
            if (bytes[i] == bi) {
                ++i;
            }
            else {
                return false;
            }
        }
        return true;
    }

    //private method: checkStr2
    private boolean checkStr2(String s) {
        try {
            Integer i=Integer.valueOf(Integer.parseInt(s));
            int ii=i.intValue();
            if (ii>=1000) {
                if (ii%16==0 || ii%27==0 || ii%10==8) {
                    return true;
                }
                else {
                    return false;
                }
            }
            else {
                return false;
            }
        } catch (NumberFormatException e) {
            return false;
        }
    }

    //virtual methods: check
    public boolean check(String s) {
        if (s.length()==12) {
            String s1=s.substring(0,8);
            String s2=s.substring(8,12);
            if (checkStr1(s1)&&checkStr2(s2)) {
                return true;
            }
            else {
                return false;
            }
        }
        else {
            return false;
        }
    }
}
  • Task2: Encoder.java
import java.util.Random;

public class Encoder {
    //Constructor
    public Encoder() {
        //Only call Object's constructor.
        //There is no need to call it manually.
    }

    //private method: convertHexToString
    private String convertHexToString(String s) {
        StringBuilder sb = new StringBuilder();
        int i=0;
        while (i<s.length()-1) {
            String ss=s.substring(i,i+2);
            int j=Integer.parseInt(ss,0x10);
            j=j^0xff;
            sb.append((char)j);
            i+=2;
        }
        return sb.toString();
    }

    //private method: convertStringToHex
    private String convertStringToHex(String s) {
        char[] chars=s.toCharArray();
        StringBuffer sb=new StringBuffer();
        int i=0;
        while (i<chars.length) {
            char c=chars[i];
            int j=((int)c)^0xff;
            sb.append(Integer.toHexString(j));
            ++i;
        }
        return sb.toString();
    }

    //private method: getSalt
    private byte[] getSalt() {
        byte[] bytes=new byte[]{0,0,0,0,0,0};
        Random random=new Random();
        int i=0;
        while (i<bytes.length) {
            bytes[i]=(byte)random.nextInt(0xf);
            ++i;
        }
        return bytes;
    }

    //virtual method: decode
    public String decode(String s) {
        if (s.length()==0) {
            return "";
        }
        StringBuffer stringBuffer = new StringBuffer();
        int i=0;
        while (i<s.length()) {
            String s1=s.substring(i,i+1);
            int j=Integer.parseInt(s1,0x10);
            j=j%4;
            j=4-j;
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append(s.substring(i+j+1,i+5));
            stringBuffer.append(stringBuilder.append(s.substring(i+1,i+j+1)).toString());
            i+=5;
        }
        String ss=convertHexToString(stringBuffer.toString());
        return ss.substring(0,11);
    }

    //virtual method: encode
    public String encode(String s) {
        if (s.length()==11) {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append(s);
            stringBuilder.append("a");
            String ss=stringBuilder.toString();
            byte[] bytes=getSalt();
            String sc=convertStringToHex(ss);
            StringBuffer stringBuffer = new StringBuffer();
            int i=0;
            while (i<sc.length()) {
                int j=i/4;
                j=bytes[j];
                int k=j%4;
                stringBuffer.append(Integer.toHexString(j));
                StringBuilder stringBuilder1 = new StringBuilder();
                stringBuilder1.append(sc.substring(i+k,i+4));
                stringBuilder1.append(sc.substring(i,i+k));
                stringBuffer.append(stringBuilder1.toString());
                i+=4;
            }
            return stringBuffer.toString();
        }
        else {
            System.out.println("input error!");
            return "";
        }
    }
}

  其中其实还有不少的细节可以修改,例如中间变量的命名与简化并不太清晰。如果嫌这些操作不必要或麻烦的,也可以如同我室友一样,遇到一个新的寄存器使用就定义一个新的,最后都命名成例如v1、v11、v111……也算是别树一帜的书写风格了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值