本帖最后由 Enigma_G 于 2017-4-27 12:41 编辑
0x00 闲言碎语
之前自己写的分析文章,分析的时候也遇到了一点问题,然后参考了一些文章得以解决。如有错误,请指出
----------
0x01 APK获取
apk下载地址
http://gslab.qq.com/competition/firstTurn.shtml
----------
0x02 java层分析
[Java] 纯文本查看 复制代码 package com.tencent.tencent2016a;
import android.app.AlertDialog$Builder;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.a.f;
import android.widget.Button;
import android.widget.EditText;
import com.a.a.e.a.a;
import com.a.a.e.a.b;
public class MainActivity extends f {
Button o;
Button p;
EditText editText;
EditText editText2;
AlertDialog$Builder s;
static {
System.loadLibrary("CheckRegister");//加载类库
}
public MainActivity() {
super();
this.o = null;
this.p = null;
this.editText = null;
this.editText2 = null;
this.s = null;
}
public native int NativeCheckRegister(String arg1, String arg2) {
}
protected void onActivityResult(int requestCode, int resultCode, Intent arg6) {
b v0 = a.a(requestCode, resultCode, arg6);
if(v0 == null) {
super.onActivityResult(requestCode, resultCode, arg6);
}
else if(v0.a() != null) {
String[] v0_1 = v0.a().split(":");
this.editText.setText(v0_1[0]);
this.editText2.setText(v0_1[1]);
}
}
protected void onCreate(Bundle arg4) {
super.onCreate(arg4);
this.setContentView(2130968601);
this.editText = this.findViewById(2131558472);
this.editText2 = this.findViewById(2131558473);
this.o = this.findViewById(2131558474); // button
this.s = new AlertDialog$Builder(((Context)this)); //新建一个对话框
this.p = this.findViewById(2131558475); // btnScan
this.s.setMessage("Check Result"); //对话框设置信息
this.s.setTitle("Check Result"); //对话框设置标题
this.s.setPositiveButton("OK", new com.tencent.tencent2016a.a(this)); //对话框设置按钮和点击事件
if(this.p != null) {
this.p.setOnClickListener(new com.tencent.tencent2016a.b(this));
}
if(this.o != null) {
this.o.setOnClickListener(new c(this));//关键事件
}
}
}
分析完后,跟进c类进行查看:
[Java] 纯文本查看 复制代码 package com.tencent.tencent2016a;
import android.view.View$OnClickListener;
import android.view.View;
class c implements View$OnClickListener {
c(MainActivity arg1) {
this.a = arg1;
super();
}
public void onClick(View arg5) {
String v0 = this.a.editText.getText().toString(); // Name
String v1 = this.a.editText2.getText().toString(); // Passwd
if(v0.length() < 6 || v0.length() > 20) { //Name在6-20之间
this.a.s.setMessage("Name too short(<6) or too long(>20)!");
}
else if(1 == this.a.NativeCheckRegister(v0, v1)) {
this.a.s.setMessage("Check Success!");
}
else {
this.a.s.setMessage("Check Fail!");
}
this.a.s.create().show();
}
}
那么思路很明确了,要下面条件成立:
[Java] 纯文本查看 复制代码 1 == this.a.NativeCheckRegister(v0, v1))
----------------------
0x03 so文件分析
用IDA打开so文件:
F5大法好
[C] 纯文本查看 复制代码 int __fastcall Java_com_tencent_tencent2016a_MainActivity_NativeCheckRegister(int a1, int a2, int a3, int a4)
{
int v4; // r7@1
int v5; // r5@1
int v6; // r4@1
int v7; // r6@1
int v8; // ST00_4@1
int v9; // ST04_4@1
v4 = a3;
v5 = a4;
v6 = a1;
v7 = (*(int (__cdecl **)(int, int, _DWORD))(*(_DWORD *)a1 + 676))(a1, a3, 0);
v8 = (*(int (__fastcall **)(int, int, _DWORD))(*(_DWORD *)v6 + 676))(v6, v5, 0);
v9 = sub_1634(v7, v8);
(*(void (__fastcall **)(int, int, int))(*(_DWORD *)v6 + 680))(v6, v4, v7);
(*(void (__fastcall **)(int, int, int))(*(_DWORD *)v6 + 680))(v6, v5, v8);
return v9;
}
手动修复一下参数和方法和变量名,如下:
[C] 纯文本查看 复制代码 int __fastcall Java_com_tencent_tencent2016a_MainActivity_NativeCheckRegister(JNIEnv *a1, jclass a2, jstring Name, jstring Passwd)
{
jstring v4; // r7@1
jstring v5; // r5@1
JNIEnv *v6; // r4@1
const char *Name_I; // r6@1
const char *Pass_I; // ST00_4@1
int v9; // ST04_4@1
v4 = Name;
v5 = Passwd;
v6 = a1;
Name_I = (*a1)->GetStringUTFChars(a1, Name, 0);
Pass_I = (*v6)->GetStringUTFChars(v6, v5, 0);
v9 = sub_1634(Name_I, (int)Pass_I);
(*v6)->ReleaseStringUTFChars(v6, v4, Name_I);
(*v6)->ReleaseStringUTFChars(v6, v5, Pass_I);
return v9;// 要让v9==1
}
那么,继续跟进sub_1634方法,这里一段一段来分析:
[C] 纯文本查看 复制代码 int __fastcall sub_1634(const char *a1, int a2) //这里参数识别应该是char*a2
{
int v2; // r6@1
signed int v3; // r5@1
int result; // r0@2
signed int v5; // r4@3
char *v6; // r7@4
int v7; // r3@4
int v8; // r4@6
int v9; // r4@7
int v10; // r1@8
const char *v11; // [sp+Ch] [bp-464h]@1
int v12[5]; // [sp+18h] [bp-458h]@7
char v13[20]; // [sp+2Ch] [bp-444h]@7
char s[20]; // [sp+40h] [bp-430h]@3
char v15[936]; // [sp+54h] [bp-41Ch]@5
int v16; // [sp+454h] [bp-1Ch]@1
v2 = a2;
v11 = a1;
v16 = _stack_chk_guard; //这是一种栈平衡的验证,因为如果栈不平衡,程序会发生异常
v3 = j_j_strlen(a1);
if ( (unsigned int)(v3 - 6) > 0xE )
goto LABEL_18;
j_j_memset(s, 0, 0x14u); //进行s数组的初始化
v5 = 0;
do
{
v6 = &s[v5];
v7 = v11[v5 % v3] * (v5 + 20160126) * v3;
++v5;
*(_DWORD *)v6 += v7;
}
while ( v5 != 16 );
稍作优化后,便于分析:
[C] 纯文本查看 复制代码 int __fastcall sub_1634(const char *Name, char *Passwd)
{
char *EPass; // r6@1
signed int v3; // r5@1
int result; // r0@2
signed int j; // r4@3
char *ps; // r7@4
int v7; // r3@4
int v8; // r4@6
int i; // r4@7
int Vpass; // r1@8
const char *Ename; // [sp+Ch] [bp-464h]@1
int FinName[5]; // [sp+18h] [bp-458h]@7
int FinPass[5]; // [sp+2Ch] [bp-444h]@7
char Vname[20]; // [sp+40h] [bp-430h]@3
char passEncode[936]; // [sp+54h] [bp-41Ch]@5
int v16; // [sp+454h] [bp-1Ch]@1
EPass = Passwd;
Ename = Name;
v16 = _stack_chk_guard;
v3 = j_j_strlen(Name);
if ( (unsigned int)(v3 - 6) > 0xE ) // 名字长度在6-20之间
goto LABEL_18;
j_j_memset(Vname, 0, 0x14u); // 初始化
j = 0;
do
{
ps = &Vname[j]; // 取址
v7 = Ename[j % v3] * (j + 20160126) * v3;
++j;
*(_DWORD *)ps += v7;
}
while ( j != 16 );
那么下面直接贴出全部修改后的:
[C] 纯文本查看 复制代码 int __fastcall sub_1634(const char *Name, char *Passwd)
{
char *EPass; // r6@1
signed int v3; // r5@1
int result; // r0@2
signed int j; // r4@3
char *ps; // r7@4
int v7; // r3@4
int v8; // r4@6
int i; // r4@7
int Vpass; // r1@8
const char *Ename; // [sp+Ch] [bp-464h]@1
int FinName[5]; // [sp+18h] [bp-458h]@7
int FinPass[5]; // [sp+2Ch] [bp-444h]@7
char Vname[20]; // [sp+40h] [bp-430h]@3
char passEncode[936]; // [sp+54h] [bp-41Ch]@5
int v16; // [sp+454h] [bp-1Ch]@1
EPass = Passwd;
Ename = Name;
v16 = _stack_chk_guard;
v3 = j_j_strlen(Name);
if ( (unsigned int)(v3 - 6) > 0xE ) // 名字长度在6-20之间
goto LABEL_18;
j_j_memset(Vname, 0, 0x14u); // 初始化
j = 0;
do
{
ps = &Vname[j]; // 取址
v7 = Ename[j % v3] * (j + 20160126) * v3;
++j;
*(_DWORD *)ps += v7;
}
while ( j != 16 );// 对Ename进行一种转换
j_j_memset(passEncode, 0, 0x400u);// 初始化
if ( EncodePass1((int)EPass) > 1024 || (v8 = EncodePass2((int)passEncode, (int)EPass), v8 != 20) )
{
LABEL_18: // if不满足
result = 0;
}
else // 继续
{
j_j_memset(FinName, 0, 0x14u); // 初始化
j_j_memset(FinPass, 0, 0x14u); // 初始化
i = 0;
do
{
Vpass = *(_DWORD *)&passEncode[i * 4];
FinName[i] = *(_DWORD *)&Vname[i * 4] / 10;
FinPass[i] = Vpass;
++i;
}
while ( i != 5 );
result = 0;
if ( FinPass[4] + FinName[0] == FinPass[2]
&& FinPass[4] + FinName[0] + FinName[1] == 2 * FinPass[4]
&& FinName[2] + FinPass[3] == FinPass[0]
&& FinName[2] + FinPass[3] + FinName[3] == 2 * FinPass[3] )
{
result = (unsigned int)(FinName[4] + FinPass[1] - 3 * FinName[2]) <= 0;// 要让result ==1
}
}
if ( v16 != _stack_chk_guard )// 栈不平衡
j_j___stack_chk_fail(result);
return result;
}
这里有一个地方需要注意,那就是IDA的参数识别问题:
这里分析下v13,他的每一个元素都是通过v10进行传递,总共传递了5次,也就是说,v13其实大小只有5,那么这里识别的v13[16]其实相对应的是v13[4],那么我们修改下v13的大小,方便审读
那么双击v13,
将它改为和上面一样,右键set type(或者快捷键Y)
改为int类型数组5个长度即可:
修复完后
然后还有就是一些参数类型识别的问题,char识别为int,int识别为char,分析一下修改就好了
从刚刚给出的修改后的代码可以分析:
我们要求Passwd,首先根据这个求出FinPass:
[C] 纯文本查看 复制代码 if ( FinPass[4] + FinName[0] == FinPass[2]
&& FinPass[4] + FinName[0] + FinName[1] == 2 * FinPass[4]
&& FinName[2] + FinPass[3] == FinPass[0]
&& FinName[2] + FinPass[3] + FinName[3] == 2 * FinPass[3] )
然后根据FinPass求出passEncode
[C] 纯文本查看 复制代码 Vpass = *(_DWORD *)&passEncode[i * 4];
FinName[i] = *(_DWORD *)&Vname[i * 4] / 10;
FinPass[i] = Vpass;
++i;
最后,根据这个函数,求出我们输入的EPass
跟进分析:
[C] 纯文本查看 复制代码 int __fastcall EncodePass2(char *passEncode, char *EPass)
{
char *v2; // r3@1
int v3; // r3@3
int v4; // r2@3
int v5; // r5@3
char *v6; // r3@4
int v7; // r3@5
int v8; // r6@5
v2 = EPass;
do
++v2;
while ( (unsigned __int8)a456789[(unsigned __int8)*(v2 - 1)] <= 0x3Fu );// a456789 == "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@>@@@?456789:;<=@@@@@@
v3 = v2 - EPass;
v4 = v3 - 1;
v5 = 3 * ((v3 + 2) / 4);
while ( 1 )
{
v6 = passEncode;
if ( v4 <= 4 )
break;
v4 -= 4;
*passEncode = ((unsigned __int8)a456789[(unsigned __int8)EPass[1]] >> 4) | 4 * a456789[(unsigned __int8)*EPass];
passEncode[1] = ((unsigned __int8)a456789[(unsigned __int8)EPass[2]] >> 2) | 16 * a456789[(unsigned __int8)EPass[1]];
v7 = (unsigned __int8)EPass[2];
v8 = (unsigned __int8)EPass[3];
EPass += 4;
passEncode[2] = (a456789[v7] << 6) | a456789[v8];
passEncode += 3;
}
if ( v4 > 1 )
{
*passEncode = ((unsigned __int8)a456789[(unsigned __int8)EPass[1]] >> 4) | 4 * a456789[(unsigned __int8)*EPass];
if ( v4 == 2 )
{
v6 = passEncode + 1;
}
else
{
passEncode[1] = ((unsigned __int8)a456789[(unsigned __int8)EPass[2]] >> 2) | 16
* a456789[(unsigned __int8)EPass[1]];
if ( v4 == 4 )
{
v6 = passEncode + 3;
passEncode[2] = (a456789[(unsigned __int8)EPass[2]] << 6) | a456789[(unsigned __int8)EPass[3]];
}
else
{
v6 = passEncode + 2;
}
}
}
*v6 = 0;
return v5 - (-v4 & 3);
}
然后分析一下,其实发现就是一张编码表索引
这个其实就是编码表的起始位置,双击进去查看
然后在Hex窗口查看
用WinHex打开:
右键->Edit->Copy block->C source
然后根据条件"<=0x3f"
,写一个c脚本:
[C] 纯文本查看 复制代码 // keygen.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include<iostream>
using namespace std;
unsigned table[] = {
0x3E, 0x40, 0x40, 0x40, 0x3F, 0x34,
0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40,
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x41,
0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51,
0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
0x78, 0x79, 0x7A, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2B, 0x2F, 0x00,
0x00, 0x00, 0x00, 0x00
};
int main()
{
unsigned a;
for (int i = 0; i <= 0xff; i++) {
a = table[i];
if (a <= 0x3f) {
cout << "input" << hex << i << " ,value:" << a << endl;
}
}
system("pause");
return 0;
}
编码表的对应关系:
0x04 Base64算法
这里谈谈Base64加密,其实也算不上加密,应该说是一种编码转换,然后在一张表上进行索引,百度一下会有很多介绍的文章,这里就大概提一下转换的步骤
Base64编码要求把3个8位字节(3*8=24)转化为4个6位的字节(4*6=24),之后在6位的前面补两个0,形成8位一个字节的形式。 如果剩下的字符不足3个字节,则用0填充,输出字符使用'=',因此编码后输出的文本末尾可能会出现1或2个'='。
举个简单的例子,我要对mask这个字符串进行base64编码转换,那么请看下面步骤:
1.将 'mask' 这个字符串进行二进制编码转换
m->01101101
a ->01100001
s ->01110011
k ->01101011
2.进行base64分组,将这四个合并一起,每6位一组
(00)011011 (00)010110 (00)000101 (00)110011 (00)011010 (00)110000(不足6位,用0补) 0000000 00000000 (补全)
3.转换为10进制
00011011 -> 27
00010110 -> 22
00000101 -> 5
00110011 ->51
00011010 ->26
00110000 ->48
4.参照编码表
bWFzaw==(最后两个‘=’是补全的)
0x05 算法分析
本题算法和Base64相似,都是转换后码表索引
[C] 纯文本查看 复制代码 *passEncode = ((unsigned __int8)a456789[(unsigned __int8)EPass[1]] >> 4) | 4 * a456789[(unsigned __int8)*EPass];
passEncode[1] = ((unsigned __int8)a456789[(unsigned __int8)EPass[2]] >> 2) | 16 * a456789[(unsigned __int8)EPass[1]];
v7 = (unsigned __int8)EPass[2];
v8 = (unsigned __int8)EPass[3];
EPass += 4;
passEncode[2] = (a456789[v7] << 6) | a456789[v8];
passEncode += 3;
}
if ( v4 > 1 )
{
*passEncode = ((unsigned __int8)a456789[(unsigned __int8)EPass[1]] >> 4) | 4 * a456789[(unsigned __int8)*EPass];
if ( v4 == 2 )
{
v6 = passEncode + 1;
}
else
{
passEncode[1] = ((unsigned __int8)a456789[(unsigned __int8)EPass[2]] >> 2) | 16
* a456789[(unsigned __int8)EPass[1]];
if ( v4 == 4 )
{
v6 = passEncode + 3;
passEncode[2] = (a456789[(unsigned __int8)EPass[2]] << 6) | a456789[(unsigned __int8)EPass[3]];
}
else
{
v6 = passEncode + 2;
}
}
}
思路:
name -> Vname -> FinName
pass -> passEncode -> Vpass -> FinPass
FinName和FinPass之间的关系,通过这个算出FinPass,然后再根据算法,推出pass
FinPass[4] + FinName[0] == FinPass[2] && FinPass[4] + FinName[0] + FinName[1] == 2 * FinPass[4] && FinName[2] + FinPass[3] == FinPass[0] && FinName[2] + FinPass[3] + FinName[3] == 2 * FinPass[3]
然后注册机我参考的是看雪的一个分享,自己写的时候思路出了点问题,就拿了别人的过来分析
[Java] 纯文本查看 复制代码 package com.cx.tencent20161.utils;
/**
* Created by cx on 16/3/9.
*/
public class KeyGen {
private byte[] name = null;
private String code = null;
private int[] nameCrypt = null;
private int[] nameCrypt2 = null;
private int[] codeCrypt = null;
private byte[] codeCrypt2 = null;
private byte[] resByte = null;
private byte[] table = {0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x3E,0x40,0x40,0x40,0x3F,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0,1,2,3,4,5,6,7,8,9,0xA,0xB,0xC,0xD,0xE,0xF,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x40,0x40,0x40,0x40,0x40,0x40,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40};
public KeyGen(String n) throws Exception{
this.name = n.getBytes();
if (this.name.length < 6 || this.name.length > 20) {
throw new Exception();
}
nameCrypt = new int[23];
nameCrypt2 = new int[5];
codeCrypt = new int[5];
resByte = new byte[50];
codeCrypt2 = new byte[30];
}
public void nameProc() {
int tmp = 0;
int i = 0, j = 0;
for (; i < 16; i++, j = i % name.length) {
nameCrypt[i] = ((name[j] * (0x1339e7e + i) * name.length) + tmp) & 0x0ff;
tmp = (((name[j] * (0x1339e7e + i) * name.length) + tmp) >> 0x8) & 0x00ffffff;
}
nameCrypt[i++] = tmp & 0x0ff;
nameCrypt[i++] = (tmp & 0x0ff00) >> 8;
nameCrypt[i++] = (tmp & 0x0ff0000) >> 16;
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];
}
for (i = 0; i < 5; i++) {
nameCrypt2[i] = nameCrypt2[i] / 10;
}
}
public String getCode() {
StringBuilder sb = 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);
sb.append((char) lookupTableRev(tmp));
tmp = (byte) (((codeCrypt2[i * 3] & 0x03) << 4) | ((codeCrypt2[i * 3 + 1] >> 4)) & 0x0f);
sb.append((char) lookupTableRev(tmp));
tmp = (byte) (((codeCrypt2[i * 3 + 1] & 0xf) << 2) | ((codeCrypt2[i * 3 + 2] & 0x0ff) >> 6));
sb.append((char) lookupTableRev(tmp));
} else {
tmp = (byte) ((codeCrypt2[i * 3] & 0x0ff) >> 2);
sb.append((char) lookupTableRev(tmp));
tmp = (byte) (((codeCrypt2[i * 3] & 0x03) << 4) | ((codeCrypt2[i * 3 + 1] >> 4)) & 0x0f);
sb.append((char) lookupTableRev(tmp));
tmp = (byte) (((codeCrypt2[i * 3 + 1] & 0xf) << 2) | ((codeCrypt2[i * 3 + 2] & 0x0ff) >> 6));
sb.append((char) lookupTableRev(tmp));
tmp = (byte) (codeCrypt2[i * 3 + 2] & 0x3f);
sb.append((char) lookupTableRev(tmp));
}
}
return sb.toString();
}
private int lookupTableRev(byte content) {
for (int i = 0; i < 256; i++) {
if (table[i] == content) {
return i;
} else {
continue;
}
}
return -1;
}
}
附上原文链接
http://bbs.pediy.com/thread-208933.htm
这篇文章是直接分析的arm汇编的
-------------
0x06 总结
主要学习到了IDA修复参数的一些小技巧,以及一些算法的分析。都是新手路上的总结而已,师傅们不要见笑
apk以及注册机打包好了
http://pan.baidu.com/s/1dFgRNZb
|