[XCTF-Reverse] 83 2019_UNCTF_easyvm

到了后边就都不会了,只有这个VM还可以磨磨

主程序非常短,输入32个字节然后就去比较了

signed __int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  unsigned int (__fastcall ***v3)(_QWORD, void *, void *, char *); // rbx
  char s; // [rsp+10h] [rbp-80h]
  int v6; // [rsp+70h] [rbp-20h]
  unsigned __int64 v7; // [rsp+78h] [rbp-18h]

  v7 = __readfsqword(0x28u);
  memset(&s, 0, 0x60uLL);
  v6 = 0;
  v3 = (unsigned int (__fastcall ***)(_QWORD, void *, void *, char *))operator new(0x28uLL);
  sub_400C1E((__int64)v3);
  puts("please input your flag:");
  scanf("%s", &s);
  if ( strlen(&s) != 32 )
  {
    puts("The length of flag is wrong!");
    puts("Please try it again!");
  }
  if ( (**v3)(v3, &unk_602080, &unk_6020A0, &s) != 0 )
  {
    puts("Congratulations!");
    printf("The flag is UNCTF{%s}", &s);
  }
  return 1LL;
}

v3在sub_400C1E 给了一个地址,然后其它的运算都在这个位置上偏移

__int64 __fastcall sub_400C1E(__int64 a1)
{
  __int64 result; // rax

  *(_QWORD *)a1 = off_4010A8;
  *(_QWORD *)(a1 + 8) = 0LL;
  *(_BYTE *)(a1 + 16) = 0;
  *(_BYTE *)(a1 + 17) = 0;
  *(_BYTE *)(a1 + 18) = 0;
  *(_DWORD *)(a1 + 20) = 0;
  *(_QWORD *)(a1 + 24) = 0LL;
  result = a1;
  *(_QWORD *)(a1 + 32) = 0LL;
  return result;
}

偏移多少是哪个函数在数据off_4010A8这有,为方便专门都改了个名字,对应switch里的编号。刚一开始跳转表看串行了,困惑好久,像九阴真经练反了的感觉。

.rodata:00000000004010A8 off_4010A8      dq offset A__main_switch
.rodata:00000000004010A8                                         ; DATA XREF: sub_400C1E+8↑o
.rodata:00000000004010B0                 dq offset A0_16add1
.rodata:00000000004010B8                 dq offset A1_17add1
.rodata:00000000004010C0                 dq offset A2_18add1
.rodata:00000000004010C8                 dq offset A3_16sub18
.rodata:00000000004010D0                 dq offset A4_16xor17
.rodata:00000000004010D8                 dq offset A5_17xor16
.rodata:00000000004010E0                 dq offset A6_16sub51
.rodata:00000000004010E8                 dq offset A7_17in16
.rodata:00000000004010F0                 dq offset A8_18_cd
.rodata:00000000004010F8                 dq offset A9_16e32add18
.rodata:0000000000401100                 dq offset AA_17e32add18
.rodata:0000000000401108                 dq offset AB_16cmp24add18
.rodata:0000000000401110                 dq offset AC_17cmp24add18
.rodata:0000000000401118                 dq offset AD_18gt0x1f

偏移0的时候是主程序。开始的时候从a2[9]开始也就是A9。然后变换*(a1+8)实现了一个循环。+8这可以看成rip,a3指向校验数据在*(a1+24),a4是输入的数据在*(a1+32),另外有模拟3个寄存器+16,+17,+18和一个标志位+20如果处理后的输入字符校验未通过会置标志然后退出。

signed __int64 __fastcall s01_main_switch(__int64 a1, __int64 a2, __int64 a3, __int64 a4)
{
  *(_QWORD *)(a1 + 8) = a2 + 9;                 // A0-AF
  *(_QWORD *)(a1 + 24) = a3;                    // code
  *(_QWORD *)(a1 + 32) = a4;                    // input
  while ( 2 )
  {
    switch ( **(unsigned __int8 **)(a1 + 8) )
    {
      case 0xA0u:
        (*(void (__fastcall **)(__int64))(*(_QWORD *)a1 + 8LL))(a1);
        continue;
      case 0xA1u:
        (*(void (__fastcall **)(__int64))(*(_QWORD *)a1 + 0x10LL))(a1);
        continue;
      case 0xA2u:
        (*(void (__fastcall **)(__int64))(*(_QWORD *)a1 + 0x18LL))(a1);// 9
        *(_QWORD *)(a1 + 8) += 11LL;
        continue;
      case 0xA3u:
        (*(void (__fastcall **)(__int64))(*(_QWORD *)a1 + 0x20LL))(a1);// 2
        *(_QWORD *)(a1 + 8) += 2LL;
        continue;
      case 0xA4u:
        (*(void (__fastcall **)(__int64))(*(_QWORD *)a1 + 0x28LL))(a1);// 5
        *(_QWORD *)(a1 + 8) += 7LL;
        continue;
      case 0xA5u:
        (*(void (__fastcall **)(__int64))(*(_QWORD *)a1 + 0x30LL))(a1);// 3
        ++*(_QWORD *)(a1 + 8);
        continue;
      case 0xA6u:
        (*(void (__fastcall **)(__int64))(*(_QWORD *)a1 + 0x38LL))(a1);// 4
        *(_QWORD *)(a1 + 8) -= 2LL;
        continue;
      case 0xA7u:
        (*(void (__fastcall **)(__int64))(*(_QWORD *)a1 + 0x40LL))(a1);// 7
        *(_QWORD *)(a1 + 8) += 7LL;
        continue;
      case 0xA8u:
        (*(void (__fastcall **)(__int64))(*(_QWORD *)a1 + 0x48LL))(a1);
        continue;
      case 0xA9u:
        (*(void (__fastcall **)(__int64))(*(_QWORD *)a1 + 0x50LL))(a1);// 1
        *(_QWORD *)(a1 + 8) -= 6LL;
        continue;
      case 0xAAu:
        (*(void (__fastcall **)(__int64))(*(_QWORD *)a1 + 0x58LL))(a1);
        continue;
      case 0xABu:
        (*(void (__fastcall **)(__int64))(*(_QWORD *)a1 + 0x60LL))(a1);// 6
        *(_QWORD *)(a1 + 8) -= 4LL;
        continue;
      case 0xACu:
        (*(void (__fastcall **)(__int64))(*(_QWORD *)a1 + 0x68LL))(a1);
        continue;
      case 0xADu:
        (*(void (__fastcall **)(__int64))(*(_QWORD *)a1 + 0x70LL))(a1);// 10
        *(_QWORD *)(a1 + 8) += 2LL;
        continue;
      case 0xAEu:                               // 8
        if ( *(_DWORD *)(a1 + 20) )
          return 0LL;
        *(_QWORD *)(a1 + 8) -= 12LL;
        continue;
      case 0xAFu:                               // out   11
        if ( *(_DWORD *)(a1 + 20) != 1 )
        {
          *(_QWORD *)(a1 + 8) -= 6LL;
          continue;
        }
        return 1LL;
      default:
        puts("cmd execute error");
        return 0LL;
    }
  }
}

这里面有向个是没有rip变动的,比如A0,A1,A8,AA,AC这如果运行到这就会是死循环,所以这些运行不到。还有几个没有运行函数,是判断跳转语句AE,AF。其它在a1后边的偏移就是前面跳转表上偏移,表示运行哪个函数。

每个函数都很简单,明确以后就可以捋捋加密过程了。

+18这个位置放的是个计数器,相当于for(i=0;i<=31;i++)里的i

先读入一个输入的字符减去序号然后和r2异或(这个异或的r2后边有)。然后再与0xcd异或。然后和校验数据比较不同则退出

成功后会把这个数据(与检验同,逆向时使用检验数据)存入r2,循环下次时使用。

然后就是i++,判断是否结束。

'''
rip: *(a1+8)  A0-AF
r1:  *(a1+16)
r2:  *(a1+17)     
i:   *(a1+18)  *(ptr+i) = flag[i]
code:*(a1+24)  code
ptr: *(a1+32) input flag

1,A9: r1= *[ptr + i]
2,A3: r1-=i 
3,A5: r2^=r1
4,A6: r1 =0xcd
5,A4: r1^=r2
6,AB: cmp(r1,code[i])  #(flag[i]-i)^code[i-1]^0xcd = code[i]
7,A7: r2 =r1 
8,AE: jnz A2           # *(a1+20) == 0
9,A2: i++
10,AD: cmp(i,0x1f)     #结束标志,总行0x1f
11,AF: jmp A9     
'''

所以解码程序就有了(由于用到r2的结果,所以解码要从后向前)

code = bytes.fromhex('00'+'F40AF76499789E7DEA7B9E7B9F7EEB71E800E8079819F425F321A42FF42FA67C')
flag = bytes([(code[i+1]^code[i]^0xcd) + i for i in range(0x1f, -1, -1)])[::-1]
print(flag)    
#942a4115be2359ffd675fa6338ba23b6
#UNCTF{942a4115be2359ffd675fa6338ba23b6}        

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值