吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 15486|回复: 44
收起左侧

[Android 原创] 一道CrackMe的分析

[复制链接]
Enigma_G 发表于 2017-4-27 12:11
本帖最后由 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的参数识别问题:

1.png

这里分析下v13,他的每一个元素都是通过v10进行传递,总共传递了5次,也就是说,v13其实大小只有5,那么这里识别的v13[16]其实相对应的是v13[4],那么我们修改下v13的大小,方便审读

那么双击v13,

2.png

将它改为和上面一样,右键set type(或者快捷键Y)

3.png

改为int类型数组5个长度即可:

修复完后

4.png

然后还有就是一些参数类型识别的问题,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

5.png

跟进分析:


   
[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);
    }


然后分析一下,其实发现就是一张编码表索引

6.png

这个其实就是编码表的起始位置,双击进去查看
7.png
然后在Hex窗口查看

用WinHex打开:

9.png

右键->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;


    }


编码表的对应关系:

8.png


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





免费评分

参与人数 20吾爱币 +19 热心值 +19 收起 理由
Franz + 1 + 1 我很赞同!
尕可 + 1 很详细,除了表那有点瑕疵其他都和完美!
会飞的丑小鸭 + 1 + 1 热心回复!
小蜗牛1116 + 1 + 1 666 可是看不懂
diting789 + 1 + 1 我很赞同!
watchdoge + 1 + 1 分析过程很详尽
helloword121 + 1 + 1 谢谢@Thanks!
逢年过节不喝酒 + 1 + 1 谢谢@Thanks!
fu90 + 1 + 1 已答复!
xyuetao + 1 + 1 谢谢@Thanks!
文可う润心 + 1 + 1 谢谢@Thanks!
lumou + 1 + 1 用心讨论,共获提升!
lapop + 1 + 1 热心回复!
po1200 + 1 + 1 我很赞同!
mihacker + 1 热心回复!
独一无② + 1 + 1 热心回复!
Sen + 1 + 1 所以要招一些,写的代码自己都看不懂的程序员。
守护神艾丽莎 + 1 + 1 我很赞同!
yanshifei + 1 + 1 我很赞同!
挥汗如雨 + 1 + 1 我很赞同!

查看全部评分

本帖被以下淘专辑推荐:

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

 楼主| Enigma_G 发表于 2017-4-27 13:17
挥汗如雨 发表于 2017-4-27 13:09
怎么手动修改变量名啊,楼主能不能详细说一下

IDA里修改变量名吗?可以快捷键N或者你在变量上右键,然后rename
xssn31l 发表于 2017-7-24 10:31
v10 = (JNIEnv *)j_j_strlen(v7);
JNIEnv *v10;
请问下楼主,我修复变量还是有这些JNIEnv*存在 ,求教如何修复这些?
 楼主| Enigma_G 发表于 2017-4-27 12:16
本帖最后由 Enigma_G 于 2017-4-27 12:42 编辑

自己占沙发
Oo不弃 发表于 2017-4-27 12:47
感谢楼主
 楼主| Enigma_G 发表于 2017-4-27 13:00

共同进步:
挥汗如雨 发表于 2017-4-27 13:09
怎么手动修改变量名啊,楼主能不能详细说一下
挥汗如雨 发表于 2017-4-27 13:55
Enigma_G 发表于 2017-4-27 13:17
IDA里修改变量名吗?可以快捷键N或者你在变量上右键,然后rename

好的,谢谢
yanshifei 发表于 2017-4-27 14:19
感谢分享...
d3mon 发表于 2017-4-27 15:52
我就说啊,公司天天说招不到逆向,这里不是一堆逆向的吗?
一只可爱的萌新 发表于 2017-4-27 15:57
谢谢分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2024-11-15 13:43

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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