首页 > 其他分享 >【Android 逆向】【攻防世界】黑客精神

【Android 逆向】【攻防世界】黑客精神

时间:2023-03-20 17:12:54浏览次数:33  
标签:攻防 v21 int v13 snKey 黑客 env v8 Android

1. apk 安装到手机,提示输入注册码

2. jadx打开apk

MainActivity.java

 @Override // android.app.Activity
    public void onCreate(Bundle savedInstanceState) {
        String str2;
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d("com.gdufs.xman m=", "Xman");
        MyApp myApp = (MyApp) getApplication();
        int m = MyApp.m;
        if (m == 0) {
            str2 = "未注册";
        } else if (m == 1) {
            str2 = "已注册";
        } else {
            str2 = "已混乱";
        }
        setTitle("Xman" + str2);
        this.btn1 = (Button) findViewById(R.id.button1);
        this.btn1.setOnClickListener(new View.OnClickListener() { // from class: com.gdufs.xman.MainActivity.1
            @Override // android.view.View.OnClickListener
            public void onClick(View v) {
                MyApp myApp2 = (MyApp) MainActivity.this.getApplication();
                int m2 = MyApp.m;
                if (m2 == 0) {
                    MainActivity.this.doRegister();
                    return;
                }
                ((MyApp) MainActivity.this.getApplication()).work();
                Toast.makeText(MainActivity.this.getApplicationContext(), MainActivity.workString, 0).show();
            }
        });
    }

    public void doRegister() {
        new AlertDialog.Builder(this).setTitle("注册").setMessage("Flag就在前方!").setPositiveButton("注册", new DialogInterface.OnClickListener() { // from class: com.gdufs.xman.MainActivity.3
            @Override // android.content.DialogInterface.OnClickListener
            public void onClick(DialogInterface dialog, int which) {
                Intent intent = new Intent();
                ComponentName cpname = new ComponentName(BuildConfig.APPLICATION_ID, "com.gdufs.xman.RegActivity");
                intent.setComponent(cpname);
                MainActivity.this.startActivity(intent);
                MainActivity.this.finish();
            }
        }).setNegativeButton("不玩了", new DialogInterface.OnClickListener() { // from class: com.gdufs.xman.MainActivity.2
            @Override // android.content.DialogInterface.OnClickListener
            public void onClick(DialogInterface dialog, int which) {
                Process.killProcess(Process.myPid());
            }
        }).show();
    }

    public void work(String str) {
        workString = str;

MyApp.java

public class MyApp extends Application {
    public static int m = 0;

    public native void initSN();

    public native void saveSN(String str);

    public native void work();

    static {
        System.loadLibrary("myjni");
    }

    @Override // android.app.Application
    public void onCreate() {
        initSN();
        Log.d("com.gdufs.xman m=", String.valueOf(m));
        super.onCreate();
    }
}

大概理了一下逻辑,App 先执行 initSN ,然后点击界面按钮,按钮根据APP.m的值判断是走RegActivity还是往下执行App.work(),然后弹出toast

3. IDA 打开so看看

函数都是JNI_OnLoad中注册的

int __fastcall initSN(JNIEnv *env)
{
  FILE *v2; // r0
  FILE *v3; // r4
  JNIEnv *t_env; // r0
  int file_len; // r7
  void *v6; // r5
  JNIEnv *tt_env; // r0
  int v9; // r1

  v2 = fopen("/sdcard/reg.dat", "r+");
  v3 = v2;
  if ( !v2 )
  {
    t_env = env;
    return setValue(t_env, 0);
  }
  fseek(v2, 0, 2);
  file_len = ftell(v3);
  v6 = malloc(file_len + 1);
  if ( !v6 )
  {
    fclose(v3);
    t_env = env;
    return setValue(t_env, 0);
  }
  fseek(v3, 0, 0);
  fread(v6, file_len, '\x01', v3);
  *((_BYTE *)v6 + file_len) = 0;
  if ( !strcmp((const char *)v6, "EoPAoY62@ElRD") )
  {
    tt_env = env;
    v9 = 1;
  }
  else
  {
    tt_env = env;
    v9 = 0;
  }
  setValue(tt_env, v9);
  return j_fclose(v3);
}

initSn 大概逻辑,就是读一个文件,看文件的值是不是EoPAoY62@ElRD,是就反射写App.m = 1 其他情况写0;(得到关键信息EoPAoY62@ElRD)

jmethodID __fastcall n3Work(JNIEnv *env)
{
  jint Value; // r0
  JNIEnv *t_env; // r0
  const char *v4; // r1
  bool v5; // zf

  initSN(env);
  Value = getValue(env);
  if ( Value )
  {
    v5 = Value == 1;
    t_env = env;
    if ( v5 )
      v4 = "输入即是flag,格式为xman{……}!";
    else
      v4 = "状态不太对。。";
  }
  else
  {
    t_env = env;
    v4 = "还不行呢!";
  }
  return callWork(t_env, (int)v4);
}

work的逻辑就是执行以下initSN 然后就判断m的值是不是1,如果是1,就输出输入即是flag,格式为xman{……}!

int __fastcall n2_saveSn(JNIEnv *env, jobject a2, jstring snKey)
{
  FILE *v5_fp; // r7
  _DWORD *p_v21; // r4
  const char *v8; // r3
  int v9; // r0
  int v10; // r1
  _WORD *v11; // r5
  JNIEnv *v12; // r0
  int v13; // r4
  JNIEnv v14; // r3
  signed int v15_i; // r6
  const char *t_snKey_chars; // r9
  char *p_snKey_chars; // r5
  signed int v18_len; // r10
  char v19; // r2
  char v20; // r3
  _BYTE v21[56]; // [sp+0h] [bp-38h] BYREF

  v5_fp = fopen("/sdcard/reg.dat", "w+");
  if ( !v5_fp )
    return j___android_log_print(3, "com.gdufs.xman", byte_D0C96DCA);
  p_v21 = v21;
  v8 = "W3_arE_whO_we_ARE";
  do
  {
    v9 = *(_DWORD *)v8;
    v8 += 8;
    v10 = *((_DWORD *)v8 - 1);
    *p_v21 = v9;
    p_v21[1] = v10;
    v11 = p_v21 + 2;
    p_v21 += 2;
  }
  while ( v8 != "E" );
  v12 = env;
  v13 = 2016;
  *v11 = *(_WORD *)v8;
  v14 = *env;
  v15_i = 0;
  t_snKey_chars = v14->GetStringUTFChars(v12, snKey, 0);
  p_snKey_chars = (char *)t_snKey_chars;
  v18_len = strlen(t_snKey_chars);
  while ( v15_i < v18_len )
  {
    if ( v15_i % 3 == 1 )
    {
      v13 = (v13 + 5) % 16;
      v19 = v21[v13 + 1];                       // v21[v13 - 23]
    }
    else if ( v15_i % 3 == 2 )
    {
      v13 = (v13 + 7) % 15;
      v19 = v21[v13 + 2];
    }
    else
    {
      v13 = (v13 + 3) % 13;
      v19 = v21[v13 + 3];                       // v21[v13 -21]
    }
    v20 = *p_snKey_chars;
    ++v15_i;
    *p_snKey_chars++ = v20 ^ v19;
  }
  fputs(t_snKey_chars, v5_fp);
  return j_fclose(v5_fp);
}

里面有两个重要逻辑,

  1. 算出初始密码
  v8 = "W3_arE_whO_we_ARE";
 do
 {
   v9 = *(_DWORD *)v8;
   v8 += 8;
   v10 = *((_DWORD *)v8 - 1);
   *p_v21 = v9;
   p_v21[1] = v10;
   v11 = p_v21 + 2;
   p_v21 += 2;
 }
 while ( v8 != "E" );
 v12 = env;
 v13 = 2016;
这里有个重要细节,这里卡了我很久: (_DWORD *)v8 相当于把v8 转为一个 _DWORD * 指针,对这种指针取值时 一次取四个字节的数据, *((_DWORD *)v8 - 1) 相当于指针值 - 4 后再取四个字节的数据

转换位python为

v8 = 'W3_arE_whO_we_ARE'

i = 0
key_v8 = v8[0]
ret = ''
while (v8[i] != 'E'):
   v9 = v8[i:i+4]
   i += 8
   v10 = v8[i-4:i]
   ret += v9
   ret += v10

ret+='E'
print(ret)

# 输出还是 W3_arE_whO_we_ARE,开始觉得搞错了,其实时障眼法

然后时加密计算逻辑

v18_len = strlen(t_snKey_chars);
 while ( v15_i < v18_len )
 {
   if ( v15_i % 3 == 1 )
   {
     v13 = (v13 + 5) % 16;
     v19 = v21[v13 + 1];                       // v21[v13 - 23]
   }
   else if ( v15_i % 3 == 2 )
   {
     v13 = (v13 + 7) % 15;
     v19 = v21[v13 + 2];
   }
   else
   {
     v13 = (v13 + 3) % 13;
     v19 = v21[v13 + 3];                       // v21[v13 -21]
   }
   v20 = *p_snKey_chars;
   ++v15_i;
   *p_snKey_chars++ = v20 ^ v19;
 }

从算法上看时不用细看时怎么操作的,可以发现输入和输出时一一对应的,且密钥也是一一对应的,那么把输出当作输入,就可以得到原来的输入
python还原

v8 = 'W3_arE_whO_we_ARE'

i = 0
key_v8 = v8[0]
ret = ''
while (v8[i] != 'E'):
   v9 = v8[i:i+4]
   i += 8
   v10 = v8[i-4:i]
   ret += v9
   ret += v10

ret+='E'
print(ret)

in_str = 'EoPAoY62@ElRD'
v13 = 2016
result = ''
for i in range(0, len(in_str)):
   if (i % 3 == 1):
       v13 = (v13 + 5) % 16
       v19 = ret[v13+1]
   elif i % 3 == 2:
       v13 = (v13 + 7) % 15
       v19 = ret[v13+2]
   else:
       v13 = (v13 + 3) % 13
       v19 = ret[v13+3]
   v20 = in_str[i]
   result += chr(ord(v20) ^ ord(v19))

print(result)

# 日志
W3_arE_whO_we_ARE
201608Am!2333

成功得到flag

标签:攻防,v21,int,v13,snKey,黑客,env,v8,Android
From: https://www.cnblogs.com/gradyblog/p/17236980.html

相关文章

  • Android 关于列表ListView
    一、ListViewandroid-pulltorefresh一个强大的拉动刷新开源项目,支持各种控件下拉刷新,ListView、ViewPager、WebView、ExpandableListView、GridView、ScrollView、Horiz......
  • android Gallery
    AdvancedPagerSlidingTabStrip一个完美兼容ViewPager的导航栏组件;可以自定义TabView;能动态加载Tab上的Icon图片;能显示Tab的消息数量和提示小圆点;支持自定义为微博形式的......
  • Android RecyclerView的notify方法和动画的刷新详解
    前些天发现了一个蛮有意思的人工智能学习网站,8个字形容一下"通俗易懂,风趣幽默",感觉非常有意思,忍不住分享一下给大家。......
  • Android GridLayoutManager.setSpanSizeLookup的使用介绍
    前些天发现了一个蛮有意思的人工智能学习网站,8个字形容一下"通俗易懂,风趣幽默",感觉非常有意思,忍不住分享一下给大家。......
  • Android 讀取.txt文件出現亂碼的解決方案
    其實就一句話:保證你文件的編碼方式和解碼方式一致就可以了。如果你僅僅是讀取外部的文件,那麼接下來這麼用:try{InputStreammInputStream=......
  • Android TextView中显示彩虹字
    一.實現很簡單,就是對顏色進行線性的漸變。用到的類是LinearGradient.直接貼代碼:publicclassTestextendsActivity{privateTextViewTestmTex......
  • Android View 拖动&插入
    View拖动&插入即:支持拖动图标然后把之插入2个View之间为了降低难度选用了若干ImageView放入ViewGroup:verticalLinearLayout......
  • Android预定义样式
    字体大小对于能够显示文字的控件(如TextViewEditTextRadioButtonButtonCheckBoxChronometer等等),你有时需要控制字体的大小。Android平台定义了......
  • android开发之gallery 实现滚动一张且短距离滑动实现滚动
    首先gallery的特点就不用多说了吧,惯性滚动、半屏翻页,但是很多时候我们不需要它的这些特性。我今天就介绍一下去掉惯性滚动以及短距离翻页的实现:......
  • android上一些方法的区别和用法的注意事项
    onMeasure()和onLayout()1.onMeasurea.属于View的方法,用来测量自己和内容的来确定宽度和高度b.view的measure方法体中会调用onMeasure2.onLayouta.属于ViewG......