首页
社区
课程
招聘
[原创]第七届山东省大学生网络安全技能大赛. 安卓逆向 fake_func
发表于: 2024-1-8 18:49 8527

[原创]第七届山东省大学生网络安全技能大赛. 安卓逆向 fake_func

2024-1-8 18:49
8527

其实这个刚开始认为很简单,直接静态分析了安卓层的代码和so代码直接一看stcmp函数,天真的以为就是上下文中看到的base64的字符串,结果提交错误,把字符串解码提交也不对。

故认真搞了一下,学到了不少东西。

大家先不急着往下看,可以自己使用jadx和IDA分析下,看看会不会被绕进去。

后缀是apk,先拖入夜神模拟器看看。只有个输入框。随便输入些字符串,看看效果。木有什么按钮,一通tab加回车,居然可以触发逻辑,应该还有个按钮。

直接拖入jadx中,找到MainActivity,代码逻辑很清晰。

代码解读:

关键就是check.checkflag函数,双击checkflag函数,可以看到如下代码

从代码可以看出,真正的逻辑来自native层的so库。根据以往开发经验,so文件会打包在资源中。

so在资源文件->lib->armeabi-v7a文件夹下,保存libchecso.so到本地。

用IDA64分析so文件。通过导出表很快定位到函数。

checkflag函数如下

看到了strcmp函数感觉,找到关键点了。对arm的传参约定做了下功课,逻辑应该是这样strcmp(r0,r1)。十有八九字符串“c2RuaXNjc2RuaXNjYWJjZA==”就是flag,保存到txt文档,感觉很简单草草记录下过程,关闭文件夹,做下一题了。

闲暇之际,把flag提交了,把积分兑一下。其他2个顺利的拿到了积分。这个却翻了车,试了好次都不行,又分析了下,发现还有个base64的字符,又拿去试了也不行,看来不是这么简单。

把导出表又看了下,有几个和hook相关的函数,再结合下题名,应该用到了hook,开始追踪,这几个函数函数的调用,IDA中一通交叉引用,感觉都不对。

感觉IDA7.5对arm支持的不太好,没有f5的IDA看着真不好。既然IDA不行,试试其他反汇编工具,先后试了下ninja和Ghidra感觉c代码还原都有那么点意思。

Ninja

Ghidra,我标注了些函数名,还导入了些java的相关数据类型。安卓逆向之自动化JNI静态分析

继续上面的疑虑,在ninja和Ghidra中对hook相关函数进行引用追踪,ninja没有找到什么有用的线索和信息。

但是Ghidra中有了线索。

在Ghidra中查看导出函数时,无意间双击到了_INIT_0_函数,居然调用了registerInlineHook,然后gooogle了_INIT_0_,随后这一'__DT_INIT_ARRAY'关键字进入了我的视野,https://docs.oracle.com/cd/E19683-01/816-1386/6m7qcobks/index.html。

so在加载时会调用这些初始化函数。

通过代码可以猜测出,函数strcmp被劫持了,通过观察对strcmp和jstrcmp的引用,可以确定so中只有一处对strcmp的调用就是 checkflag函数。

那么这样就会改变函数流程,strcmp不是比较字符串这么简单了。

根据第二参数FUN_00010e28 + 1找到函数如下

对hook的回调进行深入分析后,感觉后面的代码看不明白了,感觉后面应该是加密算法,因为需要base64解码,不是对常规的字符串进行处理,应该就是对称算法。

几款PE的查加密算法都不好用,这里直接借助IDA8.3 findcrypt插件,找到了相关线索。

根据ADES提示,以及查阅《加密与解密》书籍,逐步确定了AES_SBOX相关数据。

根据AES_SBOX相关的引用确定应用了AES的加密运算。函数 FUN_00010f24 部分代码。

以上位4 x 4 的subBytes操作。

这样通过梳理都能说的过去了。

既然知道了结果和key,很容易解密出输入的字符串。

在线AES解密

public class MainActivity extends AppCompatActivity {
    /* JADX INFO: Access modifiers changed from: protected */
    @Override // android.support.v7.app.AppCompatActivity, android.support.v4.app.FragmentActivity, android.support.v4.app.SupportActivity, android.app.Activity
    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView(R.layout.activity_main);
        ((Button) findViewById(R.id.button)).setOnClickListener(new View.OnClickListener() { // from class: com.example.p7xxtmx_g.fakefunc.MainActivity.1
            @Override // android.view.View.OnClickListener
            public void onClick(View view) {
                if (check.checkflag(((EditText) MainActivity.this.findViewById(R.id.editText)).getText().toString())) {
                    Toast.makeText(MainActivity.this, "you are right~!", 1).show();
                } else {
                    Toast.makeText(MainActivity.this, "wrong!", 1).show();
                }
            }
        });
    }
}
public class MainActivity extends AppCompatActivity {
    /* JADX INFO: Access modifiers changed from: protected */
    @Override // android.support.v7.app.AppCompatActivity, android.support.v4.app.FragmentActivity, android.support.v4.app.SupportActivity, android.app.Activity
    public void onCreate(Bundle bundle) {
        super.onCreate(bundle);
        setContentView(R.layout.activity_main);
        ((Button) findViewById(R.id.button)).setOnClickListener(new View.OnClickListener() { // from class: com.example.p7xxtmx_g.fakefunc.MainActivity.1
            @Override // android.view.View.OnClickListener
            public void onClick(View view) {
                if (check.checkflag(((EditText) MainActivity.this.findViewById(R.id.editText)).getText().toString())) {
                    Toast.makeText(MainActivity.this, "you are right~!", 1).show();
                } else {
                    Toast.makeText(MainActivity.this, "wrong!", 1).show();
                }
            }
        });
    }
}
public class check {
    public static native boolean checkflag(String str);
 
    static {
        System.loadLibrary("checkso");
    }
}
public class check {
    public static native boolean checkflag(String str);
 
    static {
        System.loadLibrary("checkso");
    }
}
Name Address Ordinal
JNI_OnLoad 0000000000000E84
Java_com_example_p7xxtmx_1g_fakefunc_check_checkflag 0000000000000E54
inlineHook 0000000000001DCC
inlineUnHook 0000000000001A98
registerInlineHook 00000000000018BC
relocateInstruction 0000000000001FA8
inlineUnHookAll 0000000000001D60
inlineHookAll 0000000000001ED8
00000e54  uint32_t Java_com_example_p7xxtmx_1g_fakefunc_check_checkflag(int32_t* arg1)
00000e62      char* r0 = (*(*arg1 + 0x2a4))()
00000e66      sub_e08()
00000e76      int32_t temp0 = 0
00000e76      int32_t i = strcmp(r0, data_6004)
00000e76      while (i != 0)
00000e76          i = i u>> 1
00000e76          temp0 = temp0 + 1
00000e7c      return (0x20 - temp0) u>> 5
00000e54  uint32_t Java_com_example_p7xxtmx_1g_fakefunc_check_checkflag(int32_t* arg1)
00000e62      char* r0 = (*(*arg1 + 0x2a4))()
00000e66      sub_e08()
00000e76      int32_t temp0 = 0
00000e76      int32_t i = strcmp(r0, data_6004)
00000e76      while (i != 0)
00000e76          i = i u>> 1
00000e76          temp0 = temp0 + 1
00000e7c      return (0x20 - temp0) u>> 5
void Java_com_example_p7xxtmx_1g_fakefunc_check_checkflag(JNIEnv *env,jclass clazz,jstring a1)
 
{
  char *__s1;
  int iVar1;
   
  __s1 = (*(*env)->GetStringUTFChars)((JNIEnv *)env,a1,(jboolean *)0x0);
  base64De_16004();
  iVar1 = strcmp(__s1,PTR_DAT_00016004);
  count_leading_zeroes(iVar1);
  return;
}
void Java_com_example_p7xxtmx_1g_fakefunc_check_checkflag(JNIEnv *env,jclass clazz,jstring a1)
 
{
  char *__s1;
  int iVar1;
   
  __s1 = (*(*env)->GetStringUTFChars)((JNIEnv *)env,a1,(jboolean *)0x0);
  base64De_16004();
  iVar1 = strcmp(__s1,PTR_DAT_00016004);
  count_leading_zeroes(iVar1);
  return;
}
在将控制权转移到应用程序之前,运行时链接器会处理应用程序及其依赖项中找到的任何初始化部分。.preinit_array 、.init_array和.init节是在构建动态对象时由链接编辑器创建的。这些部分分别标有.dynamic标签DT_PREINIT_ARRAY、DT_INIT_ARRAY和DT_INIT。请参阅“初始化和终止部分”。
 
地址包含在DT_PREINIT_ARRAY和DT_INIT_ARRAY指定的数组中的函数由运行时链接器按照其地址在数组中出现的顺序执行。如果对象同时包含DT_INIT和DT_INIT_ARRAY条目,则DT_INIT条目引用的函数将在该对象的DT_INIT_ARRAY条目引用的函数之前处理。
 
动态可执行文件可以在.preinit_array部分提供预初始化函数。这些函数在运行时链接器构建进程映像并执行重定位之后但在任何其他初始化函数之前执行。共享对象中不允许使用预初始化函数
在将控制权转移到应用程序之前,运行时链接器会处理应用程序及其依赖项中找到的任何初始化部分。.preinit_array 、.init_array和.init节是在构建动态对象时由链接编辑器创建的。这些部分分别标有.dynamic标签DT_PREINIT_ARRAY、DT_INIT_ARRAY和DT_INIT。请参阅“初始化和终止部分”。
 
地址包含在DT_PREINIT_ARRAY和DT_INIT_ARRAY指定的数组中的函数由运行时链接器按照其地址在数组中出现的顺序执行。如果对象同时包含DT_INIT和DT_INIT_ARRAY条目,则DT_INIT条目引用的函数将在该对象的DT_INIT_ARRAY条目引用的函数之前处理。
 
动态可执行文件可以在.preinit_array部分提供预初始化函数。这些函数在运行时链接器构建进程映像并执行重定位之后但在任何其他初始化函数之前执行。共享对象中不允许使用预初始化函数
int _INIT_0(void)
{
  int iVar1;
   
  iVar1 = registerInlineHook(strcmp,FUN_00010e28 + 1,&DAT_00016008_orgFun);
  if (iVar1 != 0) {
    return 0xffffffff;
  }
  iVar1 = inlineHook(strcmp);
  if (iVar1 != 0) {
    iVar1 = -1;
  }
  return iVar1;
}
int _INIT_0(void)
{
  int iVar1;
   
  iVar1 = registerInlineHook(strcmp,FUN_00010e28 + 1,&DAT_00016008_orgFun);
  if (iVar1 != 0) {
    return 0xffffffff;
  }
  iVar1 = inlineHook(strcmp);
  if (iVar1 != 0) {
    iVar1 = -1;
  }
  return iVar1;
}
void Proc_00010e28(undefined4 strKey)
 
{
  undefined4 uVar1;
   
  uVar1 = base64De_16004();
  uVar1 = EncryptEncode(strKey,uVar1);
                    /* WARNING: Could not recover jumptable at 0x00010e48. Too many branches */
                    /* WARNING: Treating indirect jump as call */
  (*strcmp_jmp)(uVar1,"K4/7/faihmk9/WEMlfuFdpgrP86ckd4oQQ/UeAiZdx8=");
  return;
}
void Proc_00010e28(undefined4 strKey)
 
{
  undefined4 uVar1;
   
  uVar1 = base64De_16004();
  uVar1 = EncryptEncode(strKey,uVar1);
                    /* WARNING: Could not recover jumptable at 0x00010e48. Too many branches */
                    /* WARNING: Treating indirect jump as call */
  (*strcmp_jmp)(uVar1,"K4/7/faihmk9/WEMlfuFdpgrP86ckd4oQQ/UeAiZdx8=");
  return;
}
Address Rules file Name String Value
.rodata:00004255 global RijnDael_AES_CHAR_4255 $c0 b'c|w{\xf2ko\xc50\x01g+\xfe\xd7\xabv\xca\x82\xc9}\xfaYG\xf0\xad\xd4\xa2\xaf\x9c\xa4r\xc0'
.rodata:00004255 global RijnDael_AES_LONG_4255 $c0 b'c|w{\xf2ko\xc50\x01g+\xfe\xd7\xabv\xca\x82\xc9}\xfaYG\xf0\xad\xd4\xa2\xaf\x9c\xa4r\xc0'
.rodata:00004355 global RijnDael_AES_LONG_inv_4355 $c0 b'R\tj\xd506\xa58\xbf@\xa3\x9e\x81\xf3\xd7\xfb
.rodata:00004156 global RijnDael_AES_RCON_4156 $c0 b'\x8d\x01\x02\x04\x08\x10 @\x80\x1b6l\xd8\xabM\x9a'
.rodata:00004189 global RijnDael_AES_RCON_4189 $c0 b'\x8d\x01\x02\x04\x08\x10 @\x80\x1b6l\xd8\xabM\x9a'
.rodata:000041BC global RijnDael_AES_RCON_41BC $c0 b'\x8d\x01\x02\x04\x08\x10 @\x80\x1b6l\xd8\xabM\x9a'
.rodata:000041EF global RijnDael_AES_RCON_41EF $c0 b'\x8d\x01\x02\x04\x08\x10 @\x80\x1b6l\xd8\xabM\x9a'
.rodata:00004222 global RijnDael_AES_RCON_4222 $c0 b'\x8d\x01\x02\x04\x08\x10 @\x80\x1b6l\xd8\xabM\x9a'
.rodata:00004455 global BASE64_table_4455 $c0 b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
                     AES_SBOX                                        XREF[5]:     FUN_00010f24:00010ff2(R),
                                                                                  FUN_0001105c:000110d2(R),
                                                                                  FUN_0001105c:000110d8(R),
                                                                                  FUN_0001105c:000110dc(R),
                                                                                  FUN_0001105c:000110e0(R) 
00014255 63              ??         63h    c
00014256 7c              ??         7Ch    |
00014257 77              ??         77h    w
00014258 7b              ??         7Bh    {
00014259 f2              ??         F2h
0001425a 6b              ??         6Bh    k
0001425b 6f              ??         6Fh    o
0001425c c5              ??         C5h
0001425d 30              ??         30h    0
0001425e 01              ??         01h
0001425f 67              ??         67h    g
00014260 2b              ??         2Bh    +
00014261 fe              ??         FEh
                     AES_SBOX                                        XREF[5]:     FUN_00010f24:00010ff2(R),
                                                                                  FUN_0001105c:000110d2(R),
                                                                                  FUN_0001105c:000110d8(R),
                                                                                  FUN_0001105c:000110dc(R),
                                                                                  FUN_0001105c:000110e0(R) 
00014255 63              ??         63h    c
00014256 7c              ??         7Ch    |
00014257 77              ??         77h    w
00014258 7b              ??         7Bh    {
00014259 f2              ??         F2h
0001425a 6b              ??         6Bh    k
0001425b 6f              ??         6Fh    o
0001425c c5              ??         C5h
0001425d 30              ??         30h    0
0001425e 01              ??         01h
0001425f 67              ??         67h    g
00014260 2b              ??         2Bh    +
00014261 fe              ??         FEh
while (iVar12 != 4) {
  iVar16 = 0;
  while (iVar16 != 4) {
    *(undefined *)(iVar15 + iVar16 * 4) = (&AES_SBOX)[*(byte *)(iVar15 + iVar16 * 4)];
    iVar16 = iVar16 + 1;
  }
  iVar15 = iVar15 + 1;
  iVar12 = iVar12 + 1;
}
while (iVar12 != 4) {
  iVar16 = 0;
  while (iVar16 != 4) {
    *(undefined *)(iVar15 + iVar16 * 4) = (&AES_SBOX)[*(byte *)(iVar15 + iVar16 * 4)];
    iVar16 = iVar16 + 1;
  }
  iVar15 = iVar15 + 1;
  iVar12 = iVar12 + 1;
}
void Proc_00010e28(undefined4 strInput)
 
{
  undefined4 uVar1;
   
  uVar1 = base64De_16004();
  uVar1 = EncryptEncode(strInput,uVar1);
                    /* WARNING: Could not recover jumptable at 0x00010e48. Too many branches */
                    /* WARNING: Treating indirect jump as call */
  (*strcmp_jmp)(uVar1,"K4/7/faihmk9/WEMlfuFdpgrP86ckd4oQQ/UeAiZdx8=");
  return;
}
void Proc_00010e28(undefined4 strInput)
 
{
  undefined4 uVar1;
   
  uVar1 = base64De_16004();
  uVar1 = EncryptEncode(strInput,uVar1);
                    /* WARNING: Could not recover jumptable at 0x00010e48. Too many branches */
                    /* WARNING: Treating indirect jump as call */
  (*strcmp_jmp)(uVar1,"K4/7/faihmk9/WEMlfuFdpgrP86ckd4oQQ/UeAiZdx8=");
  return;
}
undefined4 EncryptEncode(char *param_1,undefined4 param_2)
 
{
  size_t sVar1;
  void *__ptr;
  int iVar2;
  void *__ptr_00;
  undefined4 uVar3;
  char *pcVar4;
  char cVar5;
  int iVar6;
  size_t __size;
   
  sVar1 = strlen(param_1);
  if ((int)sVar1 < 0x10) {
    __ptr = malloc(0x10);
    iVar2 = 0;
    while (iVar2 != 0x10) {
      pcVar4 = &UNK_00014156 + -sVar1;
      if (iVar2 < (int)sVar1) {
        pcVar4 = param_1 + iVar2;
      }
      *(char *)((int)__ptr + iVar2) = *pcVar4;
      iVar2 = iVar2 + 1;
    }
    __size = 0x10;
  }
  else {
    __size = sVar1 + 0x10 & 0xfffffff0;
    __ptr = malloc(__size);
    iVar2 = 0;
    while (iVar2 < (int)__size) {
      if (iVar2 < (int)sVar1) {
        pcVar4 = param_1 + iVar2;
LAB_00011402:
        cVar5 = *pcVar4;
      }
      else {
        pcVar4 = &DAT_00014146 + (__size - sVar1);
        if ((sVar1 & 0xf) != 0) goto LAB_00011402;
        cVar5 = '\x10';
      }
      *(char *)((int)__ptr + iVar2) = cVar5;
      iVar2 = iVar2 + 1;
    }
  }
  __ptr_00 = malloc(__size);
  iVar6 = 0;
  iVar2 = 0;
  while (iVar2 < (int)(__size + ((uint)((int)__size >> 0x1f) >> 0x1c)) >> 4) {
    AesEncrypt((int)__ptr + iVar6,param_2,(int)__ptr_00 + iVar6);
    iVar6 = iVar6 + 0x10;
    iVar2 = iVar2 + 1;
  }
  uVar3 = base64Encode(__ptr_00,__size);
  free(__ptr);
  free(__ptr_00);
  return uVar3;
}
undefined4 EncryptEncode(char *param_1,undefined4 param_2)
 
{
  size_t sVar1;
  void *__ptr;
  int iVar2;
  void *__ptr_00;

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 3
支持
分享
最新回复 (1)
雪    币: 3573
活跃值: (31026)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2024-1-9 10:10
1
游客
登录 | 注册 方可回帖
返回
//