首先,用jeb将apk逆向,发现程序是通过NativeCheckRegister(String arg1)函数来判断输入
进一步看这个函数:
调用本地native 函数,而这类函数一般存在apk的lib目录里面的.so文件当中。
使用IDA6.6 导出so文件找到NativeCheckRegister(String arg1)函数
看方框里面的sub_1634()函数
这个程序的功能是根据传入的Name字符串,然后生成一个Code,与输入的code 对比。
首先 ,分析上面一段算法。转换成Java代码:
for (; i < 16; i++, j = i % name.length) {
nameCrypt[i] = ((name[j] * (20160126 + i) * name.length) + tmp) & 0x0ff;
tmp = (((name[j] * (20160126 + i) * name.length) + tmp) >> 8) & 0x0ffffffff;
}
这里主要功能是把输入的name进行一个加密转换;
这里有几点值得注意的v11 是char类型,占用1个字节,所以后面要和 0x0ff相与 保留后两个字节,v6强制类型转换成dword类型,占用8个字节,因此结果和0x0ffffffff相与。
再看这个:
for (i = 0; i < 5; i++) {
nameCrypt2[i] = nameCrypt[i * 4 + 3];
nameCrypt2[i] = nameCrypt2[i] << 8 | nameCrypt[i * 4 + 2];
nameCrypt2[i] = nameCrypt2[i] << 8 | nameCrypt[i * 4 + 1];
nameCrypt2[i] = nameCrypt2[i] << 8 | nameCrypt[i * 4];
nameCrypt2[i] = nameCrypt2[i] / 10;
}
进一步对转换过的nameCrypt转换(有点绕)
同道理也是字节位运算,V10是int类型,V15转换成dword类型,所以先算出来的字节往左移8位作高位字节,后八位作低位。
然后这边是sub_1498()函数,这个函数主要将输入的Code字符串Code进行验证:
根据上面的伪代码,用java代码表示:
public String getCode() {
StringBuilder str = new StringBuilder();
codeCrypt[3] = nameCrypt2[2] + nameCrypt2[3];
codeCrypt[0] = codeCrypt[3] + nameCrypt2[2];
codeCrypt[1] = 3 * nameCrypt2[2] - nameCrypt2[4];
codeCrypt[4] = nameCrypt2[0] + nameCrypt2[1];
codeCrypt[2] = codeCrypt[4] + nameCrypt2[0];
for (int i = 0; i < 5; i++) {
codeCrypt2[i * 4 ] = (byte) (codeCrypt[i] & 0x0ff);
codeCrypt2[i * 4 + 1] = (byte) ((codeCrypt[i] >> 8) & 0x0ff);
codeCrypt2[i * 4 + 2] = (byte) ((codeCrypt[i] >> 16) & 0x0ff);
codeCrypt2[i * 4 + 3] = (byte) ((codeCrypt[i] >> 24) & 0x0ff);
}
for (int i = 0; i <= 6; i++) {
byte tmp = 0;
if (i == 6) {
tmp = (byte) ((codeCrypt2[i * 3] & 0x0ff) >> 2);
str.append((char) lookupTableRev(tmp));
tmp = (byte) (((codeCrypt2[i * 3] & 0x03) << 4) | ((codeCrypt2[i * 3 + 1] >> 4)) & 0x0f);
str.append((char) lookupTableRev(tmp));
tmp = (byte) (((codeCrypt2[i * 3 + 1] & 0xf) << 2) | ((codeCrypt2[i * 3 + 2] & 0x0ff) >> 6));
str.append((char) lookupTableRev(tmp));
} else {
tmp = (byte) ((codeCrypt2[i * 3] & 0x0ff) >> 2);
str.append((char) lookupTableRev(tmp));
tmp = (byte) (((codeCrypt2[i * 3] & 0x03) << 4) | ((codeCrypt2[i * 3 + 1] >> 4)) & 0x0f);
str.append((char) lookupTableRev(tmp));
tmp = (byte) (((codeCrypt2[i * 3 + 1] & 0xf) << 2) | ((codeCrypt2[i * 3 + 2] & 0x0ff) >> 6));
str.append((char) lookupTableRev(tmp));
tmp = (byte) (codeCrypt2[i * 3 + 2] & 0x3f);
str.append((char) lookupTableRev(tmp));
}
}
return str.toString();
}
上面的a456789[] 在汇编下是这么一个字符串,
转换成java代码:
private int lookupTableRev(byte content) {
for (int i = 0; i < 256; i++) {
if (table[i] == content) {
return i;
} else {
continue;
}
}
return -1;
}
查表,使经过转换的code根据其在表中的位置得到0xff内的可见字符。
结果有:
总结一下代码的验证流程:
用户输入的name和code;
首先对输入的name进行一系列加密处理,然后根据加密后的nameCrypt,生成codeCrypt加密后的code,最后根据表得到相应字符串值,然后输出比较。