吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4253|回复: 31
上一主题 下一主题
收起左侧

[CTF] DASCTF2024-金秋十月-REEZ-WP-特详细

  [复制链接]
跳转到指定楼层
楼主
落尘大大和你呢 发表于 2024-10-20 13:45 回帖奖励

DASCTF2024 金秋十月-REEZ-WP
EZRE
知识点

魔改RC4

魔改XTEA

分析思路

DIE分析程序发现是PE32位程序且加了:Themida/Winlicense(3.XX)壳。

这个壳的资料较少,但好在在GitHub有相应的脱壳工具程序是什么版本就下载相应的版本就好了,脱壳的话直接把要脱壳的程序拉到脱壳工具等待一会就好了。

IDA打开分析程序

IDA分析完毕之后停在了这里,没有停在流程图是因为汇编中有花指令,IDA无法正确分析程序的执行流程。

这个是典型的JZ/JNZ花指令,去除也不难,在JNZ处按U识别为数据

把这个0x75给NOP掉后按C识别为代码,再在main函数的开始PUSH的地方按一下P识别为函数就好了。

F5反汇编分析伪代码

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4[44]; // [esp+14h] [ebp-90h]
  int i; // [esp+40h] [ebp-64h]
  int j; // [esp+44h] [ebp-60h]
  char v7[44]; // [esp+48h] [ebp-5Ch] BYREF
  char v8[44]; // [esp+74h] [ebp-30h] BYREF

  v4[0] = 80;
  v4[1] = -44;
  v4[2] = -56;
  v4[3] = -60;
  v4[4] = -113;
  v4[5] = -124;
  v4[6] = 64;
  v4[7] = -21;
  v4[8] = 50;
  v4[9] = -127;
  v4[10] = -113;
  v4[11] = -123;
  v4[12] = 108;
  v4[13] = -78;
  v4[14] = 43;
  v4[15] = 6;
  v4[16] = -65;
  v4[17] = 5;
  v4[18] = 53;
  v4[19] = 93;
  v4[20] = 46;
  v4[21] = -29;
  v4[22] = 125;
  v4[23] = 70;
  v4[24] = -115;
  v4[25] = 53;
  v4[26] = 1;
  v4[27] = 112;
  v4[28] = 58;
  v4[29] = 0x80;
  v4[30] = -127;
  v4[31] = -59;
  v4[32] = -26;
  v4[33] = 113;
  v4[34] = -45;
  v4[35] = -42;
  v4[36] = 80;
  v4[37] = 105;
  v4[38] = 111;
  v4[39] = -30;
  v4[40] = 110;
  v4[41] = 120;
  v4[42] = 20;
  v4[43] = -40;
  sub_121020(Format, (char)aPleaseEnterYou);
  sub_121050("%s", v7);
  ((void (__cdecl *)(char *, char *))loc_121290)(v7, v8);
  for ( i = 0; i <= 36; ++i )
    ((void (__cdecl *)(char *))loc_1213C0)(&v8[i]);
  for ( j = 0; j < 44; ++j )
  {
    if ( v4[j] != v8[j] )
    {
      sub_121020(Format, (char)aWrong);
      break;
    }
  }
  if ( j == 44 )
    sub_121020(Format, (char)aCongratulation);
  return 0;
}

逻辑不是很难,在第一个IF判断中就拿V4和V8进行比较,V4很明显就是密文了。接下来就是分析V8是如何来的。

V7接受用户输入字符串,调用loc_121290函数传入V7和V8,在这之前V8并没有被赋值,显然是这个函数在对V7进行操作后存储到了V8。

分析这个函数

还是经典的JZ/JNZ花指令,与上一个去除方式一样

char __cdecl sub_121290(char *a1, int a2)
{
  char result; // al
  unsigned int i; // [esp+18h] [ebp-10h]
  char *v4; // [esp+1Ch] [ebp-Ch]
  unsigned __int8 v6; // [esp+26h] [ebp-2h]
  unsigned __int8 v7; // [esp+27h] [ebp-1h]

  v7 = 0;
  v6 = 0;
  ((void (*)(void))loc_1210C0)();
  v4 = a1;
  do
    result = *v4;
  while ( *v4++ );
  for ( i = 0; i < v4 - (a1 + 1); ++i )
  {
    v6 += byte_1243B0[++v7];
    ((void (__cdecl *)(char *, char *))loc_121080)(&byte_1243B0[v7], &byte_1243B0[v6]);
    *(_BYTE *)(i + a2) = (byte_1243B0[(unsigned __int8)(byte_1243B0[v6] + byte_1243B0[v7])] ^ 0x33) + a1[i];
    result = i + 1;
  }
  return result;
}

分析loc_1210C0函数,这个函数同样有花指令,去除方式都一样。

void *sub_1210C0()
{
  void *result; // eax
  int v1; // [esp+18h] [ebp-128h]
  int j; // [esp+1Ch] [ebp-124h]
  char *v3; // [esp+20h] [ebp-120h]
  unsigned int i; // [esp+24h] [ebp-11Ch]
  int k; // [esp+28h] [ebp-118h]
  char v6[256]; // [esp+30h] [ebp-110h] BYREF
  char v7[12]; // [esp+130h] [ebp-10h] BYREF

  strcpy(v7, "th0s_i0_ke9");
  for ( i = 0; i < 0x100; ++i )
    byte_1243B0[i] = i;
  v3 = &v7[strlen(v7) + 1];
  result = memset(v6, 0, sizeof(v6));
  for ( j = 0; j < 256; ++j )
  {
    result = (void *)j;
    v6[j] = v7[j % (unsigned int)(v3 - &v7[1])];
  }
  v1 = 0;
  for ( k = 0; k < 256; ++k )
  {
    v1 = ((unsigned __int8)v6[k] + v1 + (unsigned __int8)byte_1243B0[k]) % 256;
    result = (void *)((int (__cdecl *)(char *, char *))loc_121080)(&byte_1243B0[k], &byte_1243B0[v1]);
  }
  return result;
}

这个是RC4的密钥流,使用特定的密钥生成一个大小为256的盒子,之后在用这个盒子对数据进行加密。

回去接着分析sub_121290函数

首先是循环取到V4的每个字符,V4就是a1就是输入的字符串

分析一下loc_121080函数

int __cdecl sub_121080(unsigned __int8 *a1, unsigned __int8 *a2)
{
  int result; // eax
  unsigned __int8 v3; // [esp+Fh] [ebp-1h]

  v3 = *a1;
  *a1 = *a2;
  result = v3;
  *a2 = v3;
  return result;
}

就是把两个值交换一下。

主要加密逻辑在这个循环中,该循环执行了44次。与标准RC4不同,这里修改加密逻辑了。

 for ( i = 0; i < v4 - (a1 + 1); ++i )
  {
    v6 += byte_1243B0[++v7];
    ((void (__cdecl *)(char *, char *))loc_121080)(&byte_1243B0[v7], &byte_1243B0[v6]);
    *(_BYTE *)(i + a2) = (byte_1243B0[(unsigned __int8)(byte_1243B0[v6] + byte_1243B0[v7])] ^ 0x33) + a1[i];
    result = i + 1;
  }

byte_1243B0就是RC4中的S盒子,根据V7的值在S中取到一个元素赋值给V6,在根据V7和V6的值在S盒中把这两个地方的值互相交换一下。最后就是该两个地方的值相加后作为下标在去S盒中取值后异或0x33后再加上输入字符串当前字符

这样加密后的结果就存放到了a2[i]中。

这么说可能有点绕,逻辑其实很简单画个图就明白了:

总结起来就是:(S[(unsigned char)(S[v6] + S[v7])] ^ 0x33) + a1[i] = a2[i]

那么?就是我们的密文了,a1[i]就是我们要找的明文,通过密文求明文,所以逆运算逻辑就是

a2[i] = a1[i] - (S[(unsigned char)(S[v6] + S[v7])] ^ 0x33);

sub_121290函数就分析完成了,逆运算也有了。接着往下分析

for ( i = 0; i <= 36; ++i )
    ((void (__cdecl *)(char *))loc_1213C0)(&v8[i]);

可以看到V8传如loc_1213C0函数进行了操作,分析这个函数

int *__cdecl sub_1213C0(int *a1)
{
  int *result; // eax
  int v2[4]; // [esp+Ch] [ebp-24h]
  int v3; // [esp+1Ch] [ebp-14h]
  int i; // [esp+20h] [ebp-10h]
  unsigned int v5; // [esp+24h] [ebp-Ch]
  unsigned int v6; // [esp+28h] [ebp-8h]
  unsigned int v7; // [esp+2Ch] [ebp-4h]

  v2[0] = 1855465527;
  v2[1] = 1144201745;
  v2[2] = 287454020;
  v2[3] = 925407342;
  v6 = *a1;
  v5 = a1[1];
  v7 = 1719109785;
  v3 = -1640531528;
  for ( i = 0; i <= 32; ++i )
  {
    v6 += (v2[v7 & 3] + v7) ^ (v5 + ((v5 >> 6) ^ (32 * v5)));
    v7 += v3;
    v5 += (v2[(v7 >> 11) & 3] + v7) ^ (v6 + ((v6 >> 5) ^ (16 * v6)));
  }
  *a1 = v6;
  result = a1;
  a1[1] = v5;
  return result;
}

一个魔改的XTEA加密,修改了加密轮次为33轮,v3 = -1640531528等同于v3 = 0x9E3779B8,鼠标点到那个数按H就好了。

DELTA=0x9E3779B8,SUM=1719109785;逆运算无非就是把循环体中的加密操作倒以下就好了,修改一下SUM的值因为循环了33次

SUM = 1719109785 + (0x9E3779B8 * 33)

外层的for循环调用了这个XTEA函数37次,那么我们解密也要调用37次而且还要倒着来。

结合以上分析就可以写脚本进行解密了

解密脚本
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <stdint.h>

unsigned char S[256];

void swap(unsigned char* a, unsigned char* b) {
    unsigned char temp = *a;
    *a = *b;
    *b = temp;
}

void rc4_key_setup(unsigned char* key, int key_length, unsigned char S[256]) {
    unsigned char T[256];
    int i, j;

    for (i = 0; i < 256; i++) {
        S[i] = i;
        T[i] = key[i % key_length];
    }

    j = 0;
    for (i = 0; i < 256; i++) {
        j = (j + S[i] + T[i]) % 256;
        swap(&S[i], &S[j]);
    }
}

char __cdecl sub_121290(char* a1, char* a2)
{
    char result; // al
    unsigned int i; // [esp+18h] [ebp-10h]
    char* v4; // [esp+1Ch] [ebp-Ch]
    unsigned __int8 v6; // [esp+26h] [ebp-2h]
    unsigned __int8 v7; // [esp+27h] [ebp-1h]

    v7 = 0;
    v6 = 0;
    int k = 0;
    for (i = 0; i < 44; ++i)
    {
        v6 += S[++v7];
        swap(&S[v7], &S[v6]);
        // 解密逻辑
        a2[i] = a1[i] - (S[(unsigned char)(S[v6] + S[v7])] ^ 0x33);
        // 加密逻辑
        //a2[i] = (S[(unsigned char)(S[v6] + S[v7])] ^ 0x33) + a1[i];
    }
    return 0;
}

char* __cdecl sub_1213C0(uint32_t* a1)
{
    char* result; // eax
    int v2[4]; // [esp+Ch] [ebp-24h]
    int v3; // [esp+1Ch] [ebp-14h]
    int i; // [esp+20h] [ebp-10h]
    uint32_t v5; // [esp+24h] [ebp-Ch]
    uint32_t v6; // [esp+28h] [ebp-8h]
    unsigned int v7; // [esp+2Ch] [ebp-4h]

    v2[0] = 1855465527;
    v2[1] = 1144201745;
    v2[2] = 287454020;
    v2[3] = 925407342;
    v6 = a1[0];
    v5 = a1[1];
    // 循环了33次,这里要乘以33.
    v7 = 1719109785 + (0x9E3779B8 * 33);
    v3 = 0x9E3779B8;
    for (i = 0; i <= 32; ++i)
    {
        v5 -= (v2[(v7 >> 11) & 3] + v7) ^ (v6 + ((v6 >> 5) ^ (16 * v6)));
        v7 -= v3;
        v6 -= (v2[v7 & 3] + v7) ^ (v5 + ((v5 >> 6) ^ (32 * v5)));
    }
    a1[0] = v6;
    a1[1] = v5;
    return 0;
}

int main() {
    unsigned char key[] = "th0s_i0_ke9";
    int key_length = strlen((const char*)key);

    // 初始化S盒
    rc4_key_setup(key, key_length, S);

    unsigned char encenc[] = { 0x50, 0xD4, 0xC8, 0xC4, 0x8F, 0x84, 0x40, 0xEB, 0x32, 0x81, 0x8F, 0x85, 0x6C, 0xB2, 0x2B, 0x06,
    0xBF, 0x05, 0x35, 0x5D, 0x2E, 0xE3, 0x7D, 0x46, 0x8D, 0x35, 0x01, 0x70, 0x3A, 0x80, 0x81, 0xC5,
    0xE6, 0x71, 0xD3, 0xD6, 0x50, 0x69, 0x6F, 0xE2, 0x6E, 0x78, 0x14, 0xD8, 0x25 };

    // XTEA解密
    for (int i = 36; i >= 0; i--)
    {
        sub_1213C0((uint32_t*)&encenc[i]);
    }

    // RC4解密
    char encrypted_data[44];
    sub_121290((char*)encenc, encrypted_data);

    printf("%s\n", encrypted_data);
    return 0;
}
// DASCTF{Th1l_t8e1a_rc4_l8s_s8o_int9r3es4t1ng}
总结

RC4解密中有一个坑点就是

byte_1243B0[(unsigned __int8)(byte_1243B0[v6] + byte_1243B0[v7])]

byte_1243B0[v6] + byte_1243B0[v7]这两个取值后相加有可能结果为负数,因为char类型表示的范围比较小。所以要指定一下数据类型为无符号的,我在做题过程中嫌这个IDA显示数据类型看着乱就让他隐藏了,让我吃了个大亏,调试半天才发现。

分析别人算法,能复制就复制,还要特别注意数据类型,不然出了问题太难搞了。

[/md]

免费评分

参与人数 14威望 +2 吾爱币 +114 热心值 +13 收起 理由
X1a0 + 1 谢谢@Thanks!
yixi + 1 + 1 谢谢@Thanks!
pales1gh + 1 + 1 谢谢@Thanks!
theStyx + 2 + 1 我很赞同!
soyiC + 1 + 1 用心讨论,共获提升!
zhiyuckt + 1 我很赞同!
zzyzy + 1 + 1 谢谢@Thanks!
ioyr5995 + 1 + 1 我很赞同!
yiting8 + 1 + 1 我很赞同!
25983600 + 1 + 1 我很赞同!
xxxlsy + 1 + 1 热心回复!
fengbolee + 2 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
helian147 + 1 + 1 热心回复!
爱飞的猫 + 2 + 100 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
yiting8 发表于 2024-10-21 16:19
本帖最后由 yiting8 于 2024-10-21 16:24 编辑

这个题,当时没有做出来,我脱壳后,吃亏在里面的算法还没有看明白。 这次我要好好学学,主要数据类型有点乱,搞不清
沙发
northwindowo 发表于 2024-10-20 16:23
3#
spongebob99t 发表于 2024-10-20 19:06
4#
xuiaipojie 发表于 2024-10-20 20:07

学习一下
5#
uuhome 发表于 2024-10-20 20:15
学习了,谢谢楼主
6#
maoyj 发表于 2024-10-20 21:24
感谢分享
7#
yueye1122 发表于 2024-10-21 00:08
学习了,谢谢楼主
8#
Theropej 发表于 2024-10-21 06:46
大佬,能提供附件嘛!
9#
daymissed 发表于 2024-10-21 08:11
没有把附件带上来,要不能怎么跟着操作呢?感谢分享
10#
JuncoJet 发表于 2024-10-21 09:18
街头篮球老版本这个壳
看到直接放弃就行
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-22 09:04

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表