首页 > 其他分享 >SMC逆向

SMC逆向

时间:2024-05-27 13:23:02浏览次数:17  
标签:逆向 int hex unsigned a1 v4 v6 SMC

SMC逆向介绍

参考自CTF Wiki SMC

自修改代码(Self-Modified Code)是一类特殊的代码技术,即在运行时修改自身代码,从而使得程序实际行为与反汇编结果不符,同时修改前的代码段数据也可能非合法指令,从而无法被反汇编器识别。

自修改代码通常有两种破解方式,第一种是根据静态分析结果直接修改程序二进制文件,第二种则是在动态调试时将解密后的程序从内存中 dump 下来。

以下例题采用第二种方法。

[磐石行动2024]ezRE

下载文件查看是否有壳

32位文件,无壳,放入ida

flag长度30

简单异或以及减法

比较字符串
直接复制ida中unk_4040C0相关的所有内容到txt,代码提取十六进制数值

with open('data_tianqi.txt', 'r') as file:
    lines = file.readlines()

hex_values = []
for line in lines:
    match = line.split('db')[1].strip().split()[0].rstrip('h')
    if match:
        hex_values.append(int(match, 16))

hex_values = [value for value in hex_values if value != 0]  # 去除0,不需要这个功能的时候注释掉
print(hex_values)
hexx_values = [hex(value) for value in hex_values]
hex_values_trimmed = [value[2:] for value in hexx_values]
result = ' '.join(hex_values_trimmed)
print(result)


将十六进制数据进行反变换,减变成加,异或不变,代码如下:

hex_string = "66 6b 63 64 7f 63 69 70 57 60 79 54 78 5b 6b 50 67 54 73 61 7c 50 64 48 6c 56 7e 46 65 60"
hex_values = [int(char, 16) for char in hex_string.split()]
print(hex_values)

def crazy(s):
    for i in range(len(s)):
        if i % 2 == 1:  # If the index is odd
            s[i] = chr(s[i] + i)
        else:
            s[i] = chr(s[i] ^ i)

    return ''.join(s)

output_string = crazy(hex_values)
print(output_string)

运行得到flag,提交发现不对,尝试运行exe输入flag验证
flag{how_is_the_weather_today}

刚才得到的flag是假的,后续还有代码未执行,下断点接着动态调试,发现Ok,please go on.后面执行的代码找不到位置了,而且my_function函数非常奇怪,无法正常反汇编

代码中有VirtualProtect函数,猜测my_function函数进行了smc加密,所以没法直接看到,需要在动态调试过程中查看代码

找到my_function函数的位置,然后在主函数中重新下断点调试

这里的断点应该下在((void (*)(void))lpAddress)()位置上,断点在上图中的位置单步调试费时费力,没有必要

程序运行到断点之后单步执行,直到运行call命令(调用函数),下一步就是进入my_function函数,找到my_function函数所在地址,如上图

选中该函数名称,使用按键U将数据设置为无定义(或右键找到对应功能)

然后选中该函数涉及到的所有数据内容,使用按键C,选择Force选项转换成代码

最后点击my_function使用按键P定义为函数,再用F5反编译,得到my_function函数


my_function涉及到的代码如下,共三个函数my_function、xxx_init、xxx_crypt

void __cdecl __noreturn my_function(char *a1)
{
  unsigned int v1; // eax
  char Str[50]; // [esp+16h] [ebp-2D2h] BYREF
  int v3[30]; // [esp+48h] [ebp-2A0h] BYREF
  unsigned __int8 v4[256]; // [esp+C0h] [ebp-228h] BYREF
  char v5[256]; // [esp+1C0h] [ebp-128h] BYREF
  unsigned int v6; // [esp+2C0h] [ebp-28h]
  unsigned int j; // [esp+2C4h] [ebp-24h]
  int v8; // [esp+2C8h] [ebp-20h]
  int i; // [esp+2CCh] [ebp-1Ch]

  puts("please input your True flag:");
  scanf("%40s", Str);
  v6 = strlen(Str);
  if ( v6 != 30 )
  {
    puts("Wrong!");
    exit(0);
  }
  qmemcpy(v3, &unk_404040, sizeof(v3));
  memset(v4, 0, sizeof(v4));
  memset(v5, 0, sizeof(v5));
  v1 = strlen(a1);
  xxx_init(v4, (unsigned __int8 *)a1, v1);
  for ( i = 0; i <= 255; ++i )
    v5[i] = v4[i];
  xxx_crypt(v4, (unsigned __int8 *)Str, v6);
  v8 = 1;
  for ( j = 0; ; ++j )
  {
    if ( j >= v6 )
      goto LABEL_11;
    if ( (unsigned __int8)Str[j] != v3[j] )
      break;
  }
  v8 = 0;
LABEL_11:
  if ( v8 )
    puts("Good! have a beautiful day for you!");
  else
    puts("May be try again?");
  exit(0);
}
char *__cdecl xxx_init(unsigned __int8 *a1, unsigned __int8 *a2, unsigned int a3)
{
  char *result; // eax
  _DWORD v4[64]; // [esp+7h] [ebp-111h] BYREF
  unsigned __int8 v5; // [esp+107h] [ebp-11h]
  int v6; // [esp+108h] [ebp-10h]
  int i; // [esp+10Ch] [ebp-Ch]

  v6 = 0;
  v4[0] = 0;
  v4[63] = 0;
  result = 0;
  memset((char *)v4 + 1, 0, 4 * ((((char *)v4 - ((char *)v4 + 1) + 256) & 0xFFFFFFFC) >> 2));
  v5 = 0;
  for ( i = 0; i <= 255; ++i )
  {
    a1[i] = i;
    result = (char *)v4 + i;
    *((_BYTE *)v4 + i) = a2[i % a3];
  }
  for ( i = 0; i <= 255; ++i )
  {
    v6 = (a1[i] + v6 + *((char *)v4 + i)) % 256;
    v5 = a1[i];
    a1[i] = a1[v6];
    result = (char *)v5;
    a1[v6] = v5;
  }
  return result;
}
unsigned int __cdecl xxx_crypt(unsigned __int8 *a1, unsigned __int8 *a2, unsigned int a3)
{
  unsigned int result; // eax
  unsigned __int8 v4; // [esp+Fh] [ebp-15h]
  unsigned int i; // [esp+14h] [ebp-10h]
  int v6; // [esp+18h] [ebp-Ch]
  int v7; // [esp+1Ch] [ebp-8h]

  v7 = 0;
  v6 = 0;
  for ( i = 0; ; ++i )
  {
    result = i;
    if ( i >= a3 )
      break;
    v7 = (v7 + 1) % 256;
    v6 = (v6 + a1[v7]) % 256;
    v4 = a1[v7];
    a1[v7] = a1[v6];
    a1[v6] = v4;
    a2[i] ^= a1[(unsigned __int8)(a1[v7] + a1[v6])];
  }
  return result;
}

flag长度同样为30,加密后用来对比的字符串为unk_404040

同样复制下来运行代码提取十六进制

接着下断点,动态调试

动态调试查看参数的值,发现init函数的参数v4是256个0,a1是开始输入的假flag字符串,v1是十进制的30,应该是长度。函数用于初始化v4。
crypt函数的参数v4是刚从init初始化的数据,str是my_function中输入的字符串,v6是30。
假flag:flag{how_is_the_weather_today}

分析init函数和crypt函数,发现对输入的字符串的加密只有一个异或,根据异或的性质,再异或同样的数据一次就可以抵消,直接正向运行代码,不用作任何修改,用unk_404040的数据进行异或,得到flag。

hex_string = "4d d8 76 2d c 26 c 53 da c0 17 37 8c d7 f3 d9 d0 46 2b 15 98 67 f1 ad a6 e 7c 66 90 7f"
hex_values = [int(char, 16) for char in hex_string.split()]
print(hex_values)

def xxx_init(a1, a2, a3):
    v4 = [0] * 256
    v5 = 0
    v6 = 0
    v4[0] = 0
    v4[63] = 0
    for i in range(1, 65):
        v4[i-1] = 0

    for i in range(256):
        a1[i] = i
        v4[i] = a2[i % a3]

    for i in range(256):
        v6 = (a1[i] + v6 + v4[i]) % 256
        v5 = a1[i]
        a1[i] = a1[v6]
        a1[v6] = v5

    return a1

a1 = [0] * 256
a2 = "flag{how_is_the_weather_today}"
a2_list = [ord(char) for char in a2]
a3 = len(a2)   # 30
xxx_init(a1, a2_list, a3)
# print("Initialized a1 array:", [hex(num) for num in a1])

def xxx_decrypt(a1, a2, a3):
    v4 = 0
    v6 = 0
    v7 = 0
    for i in range(a3):
        v7 = (v7 + 1) % 256
        v6 = (v6 + a1[v7]) % 256
        v4 = a1[v7]
        a1[v7] = a1[v6]
        a1[v6] = v4
        a2[i] ^= a1[(a1[v7] + a1[v6]) & 0xFF]

    return a2

enc = hex_values
result = xxx_decrypt(a1, enc, 30)
ascii_symbols = [chr(num) for num in result]
print(''.join(ascii_symbols))
# flag{This_is_a_beautiful_day!}

标签:逆向,int,hex,unsigned,a1,v4,v6,SMC
From: https://www.cnblogs.com/sK07XdAy/p/18213011

相关文章

  • 逆向CSDN发送文章接口
    业务逻辑是首先发送文章,服务器返回文章插入的id,拿着这个id再发送一次请求(这个只携带了4个参数,可能是发送给审核端审核的?或者其他的业务逻辑?但是这玩意儿不应该服务器端做吗)要逆向的参数有x-ca-nonce、x-ca-signature这两个参数其他,两个x开头的是固定的。第一个参数是随机数的意......
  • 抖音新算法之a_bogus逆向分析
    前言问:抖音的a_bogus值有什么用?1.抖音所有数据的校验都离不开a_bogus。2.抖音作为最大的短视频平台他的数据是十分多且有用的。3.获取批量抖音的数据,例如评论、无水印视频、弹幕监听、直播间抢货等。4.学习使用各种浏览器断点。1.抓包分析我们进入到章若楠的主页面,进......
  • 逆向CSDN发送文章接口
    业务逻辑是首先发送文章,服务器返回文章插入的id,拿着这个id再发送一次请求(这个只携带了4个参数,可能是发送给审核端审核的?或者其他的业务逻辑?但是这玩意儿不应该服务器端做吗)要逆向的参数有x-ca-nonce、x-ca-signature这两个参数其他,两个x开头的是固定的。第一个参数是随机数的意......
  • python爬虫js逆向(标准算法加密)
    在爬取某些网站时,可能会遇到一些参数或头部信息进行字符串加密、数据加密或字符串编码等问题,导致不能正确获取到我们想要的数据。step1观察是否有加密通过正常方法请求网页,并且也加了请求头和请求参数伪装浏览器,发现返回的状态码还是4xx,那么就要考虑到是否是请求头或者请求......
  • 逆向WeChat(三)
     本篇在博客园地址https://www.cnblogs.com/bbqzsl/p/18198572上篇回顾,对象是WEUIEngine。WeUIEngine使用了chrome::base框架,但只用来实现了单一的功能,只为了DUI的动画计时器。chrome::base框架没有用作主线程的dispatcher,所有多线程并不向chrome::base框架投递执行代码。win3......
  • js逆向系列1.1、JavaScript基础语法
    JS逆向爬虫语法初阶dy:a_b,xhs:x-s等有需要联系。v:a2247429407【1】JavaScript初识JavaScript是一种用于在客户端(即用户的浏览器)上运行的编程语言。它是一种脚本语言,可以直接嵌入到HTML页面中,并由浏览器解释和执行。在客户端环境中,JavaScript可以与用户交互,操作和修......
  • 逆向 | 驱动挂靠进程直接读内存
    逆向|驱动挂靠进程直接读内存参考:https://cloud.tencent.com/developer/article/2358904https://github.com/Whitebird0/driver_read_and_write/blob/main/04-读写内存/ReadMemory.c代码如下:代码不长但是有坑,比如说ExAllocatePool2的参数就跟之前不一样了,这个点我调试了好......
  • myfreemp3音乐token逆向
    目前还能用。window=global;constCryptoJS=require('crypto-js')functionFs(e,t){returne['charCodeAt'](Math['floor']({ELxvT:function(e,t){returne%t}}["ELxvT"](t,64)))}......
  • 逆向WeChat (二)
     本篇在博客园的地址https://www.cnblogs.com/bbqzsl/p/18187099回顾一下现时网上的资源。原生https://github.com/duilib,Ultimatehttps://github.com/qdtroy/DuiLib_Ultimate腾讯https://github.com/tencentyun/TIMSDK/tree/master/cross-platform/Windows/IMApp/Basic/d......
  • python 逆向
    python逆向也是CTFreverse的一个重要组成部分(废话)。题目一般会给一个exe文件或者pyc文件。工具工欲善其事,必先利其器,好的工具是必不可少的。exe转pyc工具:GitHub-WithSecureLabs/python-exe-unpacker:AhelperscriptforunpackinganddecompilingEXEsco......