首页
社区
课程
招聘
[原创]看雪CTF2017 第十题 CM逆向分析
2017-6-20 21:34 4156

[原创]看雪CTF2017 第十题 CM逆向分析

2017-6-20 21:34
4156

1.PEID扫描

又是大数运算,IDA打开:

F5,好直观:

查找字符串:

有PEID和上边的字符串信息可知,是用GMP实现的大数运算,接着就是猜每个函数的意义了。

有MPN的官方文档可知:

1.所有对象使用前要初始化,通过调用函数 mpz_init来完成

2.一般的函数,第一个参数为输出函数,后边的为输入参数

刚开始,使用的默认值来计算,进展缓慢,后来把mpn_set_str_140007990 处的大数改成0x10之后,进展飞快。

__int64 sub_140006F50()
{
  signed __int64 v0; // rax@1
  __int64 v1; // r8@4
  __int64 v2; // rdx@4
  char v3; // cl@5
  char v4; // al@9
  int v5; // eax@14
  const char *v6; // rcx@14
  char v8; // [sp+20h] [bp-18h]@12
  printf_140006EA0("=========================================================================\n");
  printf_140006EA0("                     看雪CTF2017 CrackMe by 海风月影\n\n");
  printf_140006EA0("    请输入序列号:");
  scanf_140006F00("%s", &g_dwInput_140051F20);
  printf_140006EA0(&unk_14004BF30);
  v0 = -1i64;
  do
    ++v0;
  while ( *((_BYTE *)&g_dwInput_140051F20 + v0) );
  if ( (_DWORD)v0 == 70 )
  {
    v1 = 0i64;
    v2 = 0i64;
    while ( 1 )
    {
      v3 = *((_BYTE *)&g_dwInput_140051F20 + v2);
      // 大写字母加数字
      if ( (unsigned __int8)(v3 - 48) > 9u && (unsigned __int8)(v3 - 65) > 0x19u )
        break;
      if ( ++v2 >= 70 )
      {
        dword_140052F98 = g_dwInput_140051F20;
        word_140052F9C = word_140051F24;
        do
        {
          v4 = *((_BYTE *)&g_dwInput_140051F20 + v1 + 6);
          byte_140052F30[v1++] = v4;
        }
        while ( v4 );
        // 初始化
        mpn_set_str_140007990(&mpn_str_left_6_140051F10, &dword_140052F98, 16i64);
        mpn_set_str_140007990(&mpn_str_right_64_140052F20, byte_140052F30, 16i64);
        // 判断是否为质数
        if ( mpz_probab_prime_p_140007350((__int64)&mpn_str_right_64_140052F20, 500u) )
        {
          if ( mpz_probab_prime_p_140007350((__int64)&mpn_str_left_6_140051F10, 500u) )
          {
            // 初始化
            mpz_set_si_140007AD0(&unk_140051EF0, 0i64);
            mpn_set_str_140007990(
              &big_1,
              "6248BC3AB92A33B000FDB88568F19727F92F79EB68FF6AD73203EFD20A3E331BE941C7AA288095F33BC4B255FD983114D480EFFBEE"
              "2E313E6218A57F9CCC8189",
              16i64);
            mpn_set_str_140007990(
              &big_2,
              "2476A7F02588913F228923E1F36F963F29708C07B117396817A6B94C336FC77FF7D381925EB40CFED8FBE894570155E41569B4EC69"
              "B26CB0320105A29651CB4B",
              16i64);
            mpz_set_si_140007AD0(&v8, 0i64);
            mpz_mod_140007B10((__int64)&v8, (__int64)&big_1, (__int64)&mpn_str_right_64_140052F20);
            if ( !mpz_cmp_si_1400075D0(&v8, 0i64) )
            {
              // 除法
              // 参数1 结果
              // 参数2 被除数 
              // 参数3 除数
              // 参数1=参数2/参数3
              mpz_div_1400071E0(&unk_140051EF0, &big_1, &mpn_str_right_64_140052F20);
              if ( mpz_cmp_140007560(&mpn_str_right_64_140052F20, &unk_140051EF0) <= 0 )
              {
                mpn_sub_1400079F0(&mpn_str_right_64_140052F20, &mpn_str_right_64_140052F20, 1i64);
                mpn_sub_1400079F0(&unk_140051EF0, &unk_140051EF0, 1i64);
                mpn_mul_140007610(&v8, &mpn_str_right_64_140052F20, &unk_140051EF0);
                // 猜不出来
                sub_1400073B0((__int64)&v8, (__int64)&mpn_str_left_6_140051F10, (__int64)&v8);
                v5 = mpz_cmp_140007560(&big_2, &v8);
                v6 = "注册成功!!!\n\n";
                if ( !v5 )
                  goto LABEL_16;
              }
            }
          }
        }
        break;
      }
    }
  }
  v6 = "注册失败\n\n";
LABEL_16:
  printf_140006EA0(v6);
  sub_14002D1B4("pause");
  return 0i64;
}

完整函数如上,流程如下:

1.假设输入的字符串为szInput
2.首先判断szInput的长度是否为70个字节
  v0 = -1i64;
  do
    ++v0;
  while ( *((_BYTE *)&g_dwInput_140051F20 + v0) );
  if ( (_DWORD)v0 == 70 )
3.接着判断这些字符是否由 0-9 和A-Z的字符组成:

 v3 = *((_BYTE *)&g_dwInput_140051F20 + v2);
      // 大写字母加数字
      if ( (unsigned __int8)(v3 - 48) > 9u && (unsigned __int8)(v3 - 65) > 0x19u )
        break;
4.将输入字符串分成两段,第一段为原字符串的0-6自己,第二段为后64字节
  dword_140052F98 = g_dwInput_140051F20;
        word_140052F9C = word_140051F24;
        do
        {
          v4 = *((_BYTE *)&g_dwInput_140051F20 + v1 + 6);
          byte_140052F30[v1++] = v4;
        }
        while ( v4 );
5.用这两个字符串来初始化两个mpz结构题:
// 初始化
        mpn_set_str_140007990(&mpn_str_left_6_140051F10, &dword_140052F98, 16i64);
        mpn_set_str_140007990(&mpn_str_right_64_140052F20, byte_140052F30, 16i64);

结构体定义:

typedef unsigned long mp_limb_t
struct __mpz_struct
{
int _mp_alloc;     //申请的大小  _mp_alloc=申请字节数/8
int _mp_size;      //当前占用的大小  _mp_size=使用字节数/8
mp_limb_t * _mp_d; //数据指针
};

初始化前6个字符的例子:

内存中,rcx地址的值:

运行之后:

6.判断是否为素数

        if ( mpz_probab_prime_p_140007350((__int64)&mpn_str_right_64_140052F20, 500u) )
        {
          if ( mpz_probab_prime_p_140007350((__int64)&mpn_str_left_6_140051F10, 500u) )

这里花了点时间,后来通过连续测试0-0x10基本可以确定是判断素数。

跟官方文档似乎不太一样。

7.初始化几个值

            // 初始化
            mpz_set_si_140007AD0(&unk_140051EF0, 0i64);
            mpn_set_str_140007990(
              &big_1,
              "6248BC3AB92A33B000FDB88568F19727F92F79EB68FF6AD73203EFD20A3E331BE941C7AA288095F33BC4B255FD983114D480EFFBEE"
              "2E313E6218A57F9CCC8189",
              16i64);
            mpn_set_str_140007990(
              &big_2,
              "2476A7F02588913F228923E1F36F963F29708C07B117396817A6B94C336FC77FF7D381925EB40CFED8FBE894570155E41569B4EC69"
              "B26CB0320105A29651CB4B",
              16i64);
            mpz_set_si_140007AD0(&v8, 0i64);

8.用大数0x62..... 模上我们输入的64位然后与0比较:

mpz_mod_140007B10((__int64)&v8, (__int64)&big_1, (__int64)&mpn_str_right_64_140052F20);
if ( !mpz_cmp_si_1400075D0(&v8, 0i64) )

9.用大数0x62..... 除以我们输入的64位然后与商做比较,商大于我们输入的数:

mpz_div_1400071E0(&unk_140051EF0, &big_1, &mpn_str_right_64_140052F20);
if ( mpz_cmp_140007560(&mpn_str_right_64_140052F20, &unk_140051EF0) <= 0 )

10.最后一段,p=input[6:64] 商q=big_1/p  v8=(p-1)(q-1)

mpn_sub_1400079F0(&mpn_str_right_64_140052F20, &mpn_str_right_64_140052F20, 1i64);
mpn_sub_1400079F0(&unk_140051EF0, &unk_140051EF0, 1i64);
mpn_mul_140007610(&v8, &mpn_str_right_64_140052F20, &unk_140051EF0);
// 猜不出来
sub_1400073B0((__int64)&v8, (__int64)&mpn_str_left_6_140051F10, (__int64)&v8);
 v5 = mpz_cmp_140007560(&big_2, &v8);
 v6 = "注册成功!!!\n\n";
 if ( !v5 )
    goto LABEL_16;

有了以上信息,结合上一题的坑,基本猜到是RSA,不然还能有啥。。。。。。。。

RSA 算法:

RSA密钥生成步骤:

根据以上信息,以及计算过程:

mpz_mod_140007B10((__int64)&v8, (__int64)&big_1, (__int64)&mpn_str_right_64_140052F20);
if ( !mpz_cmp_si_1400075D0(&v8, 0i64) )
mpz_div_1400071E0(&unk_140051EF0, &big_1, &mpn_str_right_64_140052F20);
if ( mpz_cmp_140007560(&mpn_str_right_64_140052F20, &unk_140051EF0) <= 0 )
{
 mpn_sub_1400079F0(&mpn_str_right_64_140052F20, &mpn_str_right_64_140052F20, 1i64);
mpn_sub_1400079F0(&unk_140051EF0, &unk_140051EF0, 1i64);
mpn_mul_140007610(&v8, &mpn_str_right_64_140052F20, &unk_140051EF0);

猜测:

big_1即为N
big_2可能是D
N=0x6248BC3AB92A33B000FDB88568F19727F92F79EB68FF6AD73203EFD20A3E331BE941C7AA288095F33BC4B255FD983114D480EFFBEE2E313E6218A57F9CCC8189L
D=0x2476A7F02588913F228923E1F36F963F29708C07B117396817A6B94C336FC77FF7D381925EB40CFED8FBE894570155E41569B4EC69B26CB0320105A29651CB4B

在线分解N,达到P,Q:

可知:

p =64111581030502014729907148975807553274150008894301323553363399183151805372611
q =80290597658186981463023471970341877717671071586519890660723213036500314978243

把P转成十六进制作为后64位,果然可以直接运行到:

// 猜不出来
 sub_1400073B0((__int64)&v8, (__int64)&mpn_str_left_6_140051F10, (__int64)&v8);
 v5 = mpz_cmp_140007560(&big_2, &v8);

那么肯定是要根据以下公式求解E

ed - 1 = kφ(n)
e:不知道,待求
d:私钥,似乎是big_2 0x 2476.....
K:为整数
φ(n)=(p-1)*(q-1)

根据以上条件,写python,计算:

def calc_e()
    ola_n=(p-1)*(q-1
    for x in range(1,10000000):
        e=(ola_n*x+1)/big_num_2
        if ola_n*x+1==big_num_2*e:  #验证e是否为整数(是否舍弃了小数部分 10/3=3  3×3!=10)
            print e,hex(e)

输出:
16077491 0xf552b3L

完整注册码:

F552B38DBDDE72E2E693B2AED5C769C0DCB3DA83534480A80E652FFE53544CD91A18C3


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
免费 1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回