Enigma_G 发表于 2017-4-27 12:11

一道CrackMe的分析

本帖最后由 Enigma_G 于 2017-4-27 12:41 编辑

0x00 闲言碎语

之前自己写的分析文章,分析的时候也遇到了一点问题,然后参考了一些文章得以解决。如有错误,请指出:Dweeqw

----------

0x01 APK获取
apk下载地址


http://gslab.qq.com/competition/firstTurn.shtml


----------

0x02 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);
            this.editText2.setText(v0_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类进行查看:


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

那么思路很明确了,要下面条件成立:

    1 == this.a.NativeCheckRegister(v0, v1))


----------------------


0x03 so文件分析
用IDA打开so文件:

F5大法好
    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;
    }

手动修复一下参数和方法和变量名,如下:

    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方法,这里一段一段来分析:

    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; // @1
      int v12; // @7
      char v13; // @7
      char s; // @3
      char v15; // @5
      int v16; // @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;
    v7 = v11 * (v5 + 20160126) * v3;
    ++v5;
    *(_DWORD *)v6 += v7;
      }
      while ( v5 != 16 );

稍作优化后,便于分析:

    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; // @1
      int FinName; // @7
      int FinPass; // @7
      char Vname; // @3
      char passEncode; // @5
      int v16; // @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; // 取址
    v7 = Ename * (j + 20160126) * v3;
    ++j;
    *(_DWORD *)ps += v7;
      }
      while ( j != 16 );

那么下面直接贴出全部修改后的:

    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; // @1
      int FinName; // @7
      int FinPass; // @7
      char Vname; // @3
      char passEncode; // @5
      int v16; // @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; // 取址
    v7 = Ename * (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;
      FinName = *(_DWORD *)&Vname / 10;
      FinPass = Vpass;
      ++i;
    }
    while ( i != 5 );
    result = 0;
    if ( FinPass + FinName == FinPass
      && FinPass + FinName + FinName == 2 * FinPass
      && FinName + FinPass == FinPass
      && FinName + FinPass + FinName == 2 * FinPass )
    {
      result = (unsigned int)(FinName + FinPass - 3 * FinName) <= 0;// 要让result ==1
    }
      }
      if ( v16 != _stack_chk_guard )// 栈不平衡
    j_j___stack_chk_fail(result);
      return result;
    }
   


这里有一个地方需要注意,那就是IDA的参数识别问题:



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

那么双击v13,



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



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

修复完后



然后还有就是一些参数类型识别的问题,char识别为int,int识别为char,分析一下修改就好了

从刚刚给出的修改后的代码可以分析:

我们要求Passwd,首先根据这个求出FinPass:

    if ( FinPass + FinName == FinPass
      && FinPass + FinName + FinName == 2 * FinPass
      && FinName + FinPass == FinPass
      && FinName + FinPass + FinName == 2 * FinPass )

然后根据FinPass求出passEncode

    Vpass = *(_DWORD *)&passEncode;
      FinName = *(_DWORD *)&Vname / 10;
      FinPass = Vpass;
      ++i;

最后,根据这个函数,求出我们输入的EPass



跟进分析:


    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] >> 4) | 4 * a456789[(unsigned __int8)*EPass];
    passEncode = ((unsigned __int8)a456789[(unsigned __int8)EPass] >> 2) | 16 * a456789[(unsigned __int8)EPass];
    v7 = (unsigned __int8)EPass;
    v8 = (unsigned __int8)EPass;
    EPass += 4;
    passEncode = (a456789 << 6) | a456789;
    passEncode += 3;
      }
      if ( v4 > 1 )
      {
    *passEncode = ((unsigned __int8)a456789[(unsigned __int8)EPass] >> 4) | 4 * a456789[(unsigned __int8)*EPass];
    if ( v4 == 2 )
    {
      v6 = passEncode + 1;
    }
    else
    {
      passEncode = ((unsigned __int8)a456789[(unsigned __int8)EPass] >> 2) | 16
   * a456789[(unsigned __int8)EPass];
      if ( v4 == 4 )
      {
    v6 = passEncode + 3;
    passEncode = (a456789[(unsigned __int8)EPass] << 6) | a456789[(unsigned __int8)EPass];
      }
      else
      {
    v6 = passEncode + 2;
      }
    }
      }
      *v6 = 0;
      return v5 - (-v4 & 3);
    }

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



这个其实就是编码表的起始位置,双击进去查看

然后在Hex窗口查看

用WinHex打开:



右键->Edit->Copy block->C source

然后根据条件"<=0x3f"
,写一个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;
      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相似,都是转换后码表索引

*passEncode = ((unsigned __int8)a456789[(unsigned __int8)EPass] >> 4) | 4 * a456789[(unsigned __int8)*EPass];
passEncode = ((unsigned __int8)a456789[(unsigned __int8)EPass] >> 2) | 16 * a456789[(unsigned __int8)EPass];
v7 = (unsigned __int8)EPass;
v8 = (unsigned __int8)EPass;
EPass += 4;
passEncode = (a456789 << 6) | a456789;
passEncode += 3;
}
if ( v4 > 1 )
{
*passEncode = ((unsigned __int8)a456789[(unsigned __int8)EPass] >> 4) | 4 * a456789[(unsigned __int8)*EPass];
if ( v4 == 2 )
{
v6 = passEncode + 1;
}
else
{
passEncode = ((unsigned __int8)a456789[(unsigned __int8)EPass] >> 2) | 16
* a456789[(unsigned __int8)EPass];
if ( v4 == 4 )
{
v6 = passEncode + 3;
passEncode = (a456789[(unsigned __int8)EPass] << 6) | a456789[(unsigned __int8)EPass];
}
else
{
v6 = passEncode + 2;
}
}
}
思路:
name -> Vname->FinName

pass -> passEncode -> Vpass -> FinPass

FinName和FinPass之间的关系,通过这个算出FinPass,然后再根据算法,推出pass
FinPass + FinName == FinPass&& FinPass + FinName + FinName == 2 * FinPass&& FinName + FinPass == FinPass&& FinName + FinPass + FinName == 2 * FinPass

然后注册机我参考的是看雪的一个分享,自己写的时候思路出了点问题{:1_924:},就拿了别人的过来分析

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;
      nameCrypt2 = new int;
      codeCrypt = new int;
      resByte = new byte;
      codeCrypt2 = new byte;
    }

    public void nameProc() {
      int tmp = 0;

      int i = 0, j = 0;
      for (; i < 16; i++, j = i % name.length) {
            nameCrypt = ((name * (0x1339e7e + i) * name.length) + tmp) & 0x0ff;
            tmp = (((name * (0x1339e7e + i) * name.length) + tmp) >> 0x8) & 0x00ffffff;
      }

      nameCrypt = tmp & 0x0ff;
      nameCrypt = (tmp & 0x0ff00) >> 8;
      nameCrypt = (tmp & 0x0ff0000) >> 16;

      for (i = 0; i < 5; i++) {
            nameCrypt2 = nameCrypt;
            nameCrypt2 = nameCrypt2 << 8 | nameCrypt;
            nameCrypt2 = nameCrypt2 << 8 | nameCrypt;
            nameCrypt2 = nameCrypt2 << 8 | nameCrypt;
      }

      for (i = 0; i < 5; i++) {
            nameCrypt2 = nameCrypt2 / 10;
      }
    }

    public String getCode() {
      StringBuilder sb = new StringBuilder();

      codeCrypt = nameCrypt2 + nameCrypt2;
      codeCrypt = codeCrypt + nameCrypt2;

      codeCrypt = 3 * nameCrypt2 - nameCrypt2;

      codeCrypt = nameCrypt2 + nameCrypt2;
      codeCrypt = codeCrypt + nameCrypt2;


      for (int i = 0; i < 5; i++) {
            codeCrypt2 = (byte) (codeCrypt & 0x0ff);
            codeCrypt2 = (byte) ((codeCrypt >> 8) & 0x0ff);
            codeCrypt2 = (byte) ((codeCrypt >> 16) & 0x0ff);
            codeCrypt2 = (byte) ((codeCrypt >> 24) & 0x0ff);
      }

      for (int i = 0; i <= 6; i++) {
            byte tmp = 0;

            if (i == 6) {
                tmp = (byte) ((codeCrypt2 & 0x0ff) >> 2);
                sb.append((char) lookupTableRev(tmp));
                tmp = (byte) (((codeCrypt2 & 0x03) << 4) | ((codeCrypt2 >> 4)) & 0x0f);
                sb.append((char) lookupTableRev(tmp));
                tmp = (byte) (((codeCrypt2 & 0xf) << 2) | ((codeCrypt2 & 0x0ff) >> 6));
                sb.append((char) lookupTableRev(tmp));
            } else {
                tmp = (byte) ((codeCrypt2 & 0x0ff) >> 2);
                sb.append((char) lookupTableRev(tmp));
                tmp = (byte) (((codeCrypt2 & 0x03) << 4) | ((codeCrypt2 >> 4)) & 0x0f);
                sb.append((char) lookupTableRev(tmp));
                tmp = (byte) (((codeCrypt2 & 0xf) << 2) | ((codeCrypt2 & 0x0ff) >> 6));
                sb.append((char) lookupTableRev(tmp));
                tmp = (byte) (codeCrypt2 & 0x3f);
                sb.append((char) lookupTableRev(tmp));
            }
      }
      return sb.toString();
    }

    private int lookupTableRev(byte content) {
      for (int i = 0; i < 256; i++) {
            if (table == content) {
                return i;
            } else {
                continue;
            }
      }

      return -1;
    }
}







附上原文链接


http://bbs.pediy.com/thread-208933.htm


这篇文章是直接分析的arm汇编的
-------------

0x06 总结

主要学习到了IDA修复参数的一些小技巧,以及一些算法的分析。都是新手路上的总结而已,师傅们不要见笑:Dweeqw

apk以及注册机打包好了
http://pan.baidu.com/s/1dFgRNZb





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 编辑

:lol自己占沙发

Oo不弃 发表于 2017-4-27 12:47

感谢楼主

Enigma_G 发表于 2017-4-27 13:00

Oo不弃 发表于 2017-4-27 12:47
感谢楼主

共同进步::lol

挥汗如雨 发表于 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

谢谢分享
页: [1] 2 3 4 5
查看完整版本: 一道CrackMe的分析