XCTF_MOBILE11_黑客精神

初见

附件为一个apk,先到模拟器中运行一下。

必须选择CPU为ARM的模拟器,因为app里面的native库只提供了ARM指令集的版本,没有提供x86指令集的版本:

模拟器启动后,将apk拖到模拟器中进行安装,安装好后,列表中app的图标是一个骚年:

 运行app,又见一个骚年:

 除了一个按钮啥也没有,点击这个按钮,弹出一个两按钮的对话框:

点击“不玩了”,app直接退出。

点击“注册”,进入新的界面。在新界面中,可以输入一串字符串,并有一个注册按钮。随便输入一个串,点击注册,弹框显示“您的注册码已保存”:

弹框后,进程立即退出。从行为上暂时看不出这个注册码的作用,反编译看看代码。

MainActivity分析

使用jadx打开apk,先看一下MainActivity的代码,在onCreate函数内可以找到,“自由定义分享”按钮的响应函数:

this.btn1.setOnClickListener(new View.OnClickListener() {      
            public void onClick(View v) {
                MyApp myApp2 = (MyApp) MainActivity.this.getApplication();
                if (MyApp.m == 0) {
                    MainActivity.this.doRegister();
                    return;
                }
                ((MyApp) MainActivity.this.getApplication()).work();
                Toast.makeText(MainActivity.this.getApplicationContext(), MainActivity.workString, 0).show();
            }
});

核心就是,MyApp.m为空时,调用doRegister()。随后调用work()。

先看看doRegister()函数:

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

可见,这个doRegister()函数就是弹出下图对话框并处理按钮点击:

 点击“不玩了”,直接killProcess。

点击“注册”,以类"com.gdufs.xman.RegActivity"创建新Activity并启动。

总结来看就是:

如果MyApp.m为空,创建RegActivity进行注册,使得MyApp.m不为空。然后调用work()。

如果MyApp.m不为空,直接调用work()。

而这个work是个native函数:

public native void work();

RegActivity

RegActivity对应如下界面:

重点在“注册”按钮的响应函数:

public void onClick(View v) {
     String sn = RegActivity.this.edit_sn.getText().toString().trim();
     if (sn == null || sn.length() == 0) {
           Toast.makeText(RegActivity.this, "您的输入为空", 0).show();
           return;
     }
     ((MyApp) RegActivity.this.getApplication()).saveSN(sn);
     new AlertDialog.Builder(RegActivity.this).setTitle("回复").setMessage("您的注册码已保存").setPositiveButton("好吧", new DialogInterface.OnClickListener() { 
            public void onClick(DialogInterface dialog, int which) {
                   Process.killProcess(Process.myPid());
            }
     }).show();
}

可见,点击“注册”按钮后:

  1. 调用saveSN()函数进行注册
  2. 弹框提示“您的注册码已保存”,如上图
  3. killProcess

到这里,已经分析出来的逻辑是:

        如果MyApp.m为空,调用saveSN()进行注册,然后调用work()。

        如果MyApp.m不为空,直接调用work()。

这个saveSN()也是native函数:

public native void saveSN(String str);

下面我们就分析分析这个两个native函数。

native库分析

将apk后缀名改为zip并解压,在解压后的lib目录下能得到native库文件:libmyjni.so。

用IDA加载libmyjni.so。

但在导出函数中没有找到work函数和saveSN函数。但能看到导出了JNI_OnLoad函数,这让人联想起JNI函数的动态注册方式:

  1. 利用结构体 JNINativeMethod 数组记录 java 方法与 JNI 函数的对应关系;

  2. 实现 JNI_OnLoad 方法,在加载动态库后,执行动态注册;

  3. 调用 FindClass 方法,获取 java 对象;

  4. 调用 RegisterNatives 方法,传入 java 对象,以及 JNINativeMethod 数组,以及注册数目完成注册;

这和该题的JNI_OnLoad函数过程一致:

jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
  if ( !(*vm)->GetEnv(vm, (void **)&g_env, 65542) )
  {
    j___android_log_print(2, "com.gdufs.xman", "JNI_OnLoad()");
    native_class = (*(int (__fastcall **)(int, const char *))(*(_DWORD *)g_env + 24))(g_env, "com/gdufs/xman/MyApp");
    if ( !(*(int (__fastcall **)(int, int, char **, int))(*(_DWORD *)g_env + 860))(g_env, native_class, off_5004, 3) )
    {
      j___android_log_print(2, "com.gdufs.xman", "RegisterNatives() --> nativeMethod() ok");
      return 65542;
    }
    j___android_log_print(6, "com.gdufs.xman", "RegisterNatives() --> nativeMethod() failed");
  }
  return -1;
}

可见,off_5004就是java函数和JNI函数的对应关系,据此得到对应关系为:

  • InitSN----n1
  • SaveSN----n2
  • Work----n3

下面分析native函数。

SaveSN

核心反编译代码为:

int __fastcall n2(int a1, int a2, int a3)
{
  v5 = j_fopen("/sdcard/reg.dat", "w+");
  strcpy(v13, "W3_arE_whO_we_ARE");
  v7 = input_string;
  v8 = v7;
  v12 = j_strlen(v7);
  v9 = 2016;
  while ( 1 )
  {
    v10 = v8 - v7;
    if ( v8 - v7 >= v12 )
      break;
    if ( v10 % 3 == 1 )
    {
      v9 = (v9 + 5) % 16;
      v11 = v13[v9 + 1];
    }
    else if ( v10 % 3 == 2 )
    {
      v9 = (v9 + 7) % 15;
      v11 = v13[v9 + 2];
    }
    else
    {
      v9 = (v9 + 3) % 13;
      v11 = v13[v9 + 3];
    }
    *v8++ ^= v11;
  }
  j_fputs(v7, v5);
  return j_fclose(v5);
}

这里的伪代码是我简化之后的,方便理解。这个里核心逻辑为:

  1. 在循环中,依据"W3_arE_whO_we_ARE"对输入字符串进行变形
  2. 将变形后的字符串存入文件"/sdcard/reg.dat"

InitSN

核心反编译代码为:

int __fastcall n1(int a1)
{
  v2 = j_fopen("/sdcard/reg.dat", "r+");
  j_fread(v6, v5, 1, v2);
  if ( !j_strcmp(v6, "EoPAoY62@ElRD") )
  {
    v8 = a1;
    v9 = 1;
  }
  else
  {
    v8 = a1;
    v9 = 0;
  }
  return j_fclose(v3);
}

这里的伪代码也是我简化过得,核心功能逻辑为:

  1. 读"/sdcard/reg.dat"文件的内容
  2. 比较内容是不是"EoPAoY62@ElRD"

到这里就清楚了,这道题就是要我们的输入字符串,在SaveSN中经过变形后为"EoPAoY62@ElRD"。

解题

SaveSN中对输入字符串进行变形的逻辑也很简单:

  1. 取输入字符串里的每个字符a
  2. 根据该字符在输入字符串里的下标,计算出一个值i
  3. 取"W3_arE_whO_we_ARE"中下标为i的字符b
  4. 计算a异或b的值,写入文件

根据变形过程,使用python实现解密代码:

seedstr = bytes('W3_arE_whO_we_ARE','utf-8')
seed = 2016;
result = bytes('EoPAoY62@ElRD','utf-8')
for i in range(13):
	if(i % 3 == 1):
		seed = (seed + 5) % 16
		tmp = seedstr[seed + 1]
		tmp = tmp ^ result[i]
		print(chr(tmp), end = '')
	elif(i % 3 == 2):
		seed = (seed + 7) % 15
		tmp = seedstr[seed + 2]
		tmp = tmp ^ result[i]
		print(chr(tmp), end = '')
	else:
		seed = (seed + 3) % 13
		tmp = seedstr[seed + 3]
		tmp = tmp ^ result[i]
		print(chr(tmp), end = '')

得到字符串:201608Am!2333。

输入该字符串进行注册,之后再次打开app,点击“自由正义分享”,提示:

 所以flag为:xman{201608Am!2333}

———————————————————————————————————————————

欢迎关注我的微博:大雄_RE。专注软件逆向,分享最新的好文章、好工具,追踪行业大佬的研究成果。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: pure_color xctf是一个CTF比赛平台,致力于举办和推广网络安全竞赛。 pure_color xctf的目标是为安全爱好者和专业人士提供一个学习、切磋和交流的平台。这个平台上举办的比赛覆盖了各种网络安全领域,包括但不限于网络攻防、密码学、逆向工程等。通过参与这些比赛,参赛者可以提升自己的技能,了解最新的安全威胁和攻击技术,锻炼解决问题的能力。 pure_color xctf的比赛模式多样,可以是个人或团队参与。参赛者需要在限定的时间内完成一系列的题目,这些题目可能包含漏洞分析、编程挑战、数据分析等。比赛过程中,参赛者需要运用各种技术手段,如渗透测试、代码审计、漏洞利用等,解决题目的要求。参赛者不仅需要具备网络安全相关的知识,还需要具备良好的团队合作和解决问题的能力。 此外,pure_color xctf也为参赛者提供了一个交流平台。比赛期间,参赛者可以在平台上与其他选手交流经验、讨论解题思路。参赛者也可以通过竞赛结果来评估自己的能力,并与其他选手进行切磋比拼。 总之,pure_color xctf是一个举办网络安全竞赛的平台,旨在为安全爱好者和专业人士提供学习和交流的机会,促进网络安全技术的发展。 ### 回答2: pure_color xctf 是一项竞技性的网络安全挑战赛。XCCTF 是全球知名的网络安全竞赛组织之一,而 pure_color 是该竞赛组织内的一项赛事。该赛事旨在提升参赛者的网络安全技能和知识,让他们在一个仿真的网络环境中进行攻防对抗。 在 pure_color xctf 中,参赛队伍将根据题目要求进行网络攻击和防御。这些题目有不同的难度级别,并涵盖了各种不同的网络安全技术和攻击类型。参赛队伍将需要利用他们的知识和技能,像真实的黑客一样去攻击系统漏洞,获取目标系统内的敏感信息或是直接控制目标系统。同时,他们也需要通过搭建防御策略和系统来保护自己的系统免受攻击。 pure_color xctf 不仅仅是一个交流学习的平台,也是一个展示能力的舞台。优胜的参赛队伍将会被邀请参加更高级别的网络安全竞赛和会议,进一步提升他们的技能并扩展职业网络。此外,pure_color xctf 也为参赛者提供了一个找到职业机会的平台,很多安全公司和组织会在赛事期间招聘优秀的选手。 总而言之,pure_color xctf 是一个有挑战性的网络安全竞赛,旨在通过攻击和防御对抗提升参赛者的技能,并为他们提供职业发展和展示的机会。同时,这个赛事也在促进网络安全领域的交流和合作,为提升整个网络安全行业的水平做出贡献。 ### 回答3: pure_color xctf 是一个CTF(Capture The Flag,夺旗赛)竞赛平台。CTF是一种网络安全竞赛,旨在让参赛者通过解决一系列的密码学、逆向工程、漏洞利用等问题来获取旗标,比赛者可以通过这些旗标来获取积分并竞争排名。 pure_color xctf 平台是一个为CTF竞赛提供的一个在线环境,类似于一个实验室,用于举办CTF比赛。在这个平台上,参赛者可以注册账号并加入已经发布的CTF赛事中。赛事中的题目被分为不同的难度级别,涵盖了各种安全领域的知识。参赛者需要通过解决题目中的任务来获取旗标,并将其提交到平台上进行验证。在比赛结束后,将根据参赛者解决的题目数量、用时、积分等因素来计算最终排名,并颁发奖励给获胜者。 pure_color xctf 提供了逼真的仿真环境,使得参赛者能够在一个安全的网络环境下进行实时的攻防演练。平台上的题目多样化且具有挑战性,旨在让参赛者将所学的理论知识应用到实际场景中去,并培养解决问题和团队合作的能力。同时,pure_color xctf 还为参赛者提供了交流平台,方便他们在比赛过程中与其他参赛者交流经验、技巧以及解题思路。 总之,pure_color xctf 是一个提供CTF竞赛平台的在线环境,旨在鼓励参赛者在安全领域中深入学习和实践,并通过解决各种挑战赛题目来提升技能和知识水平。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值