CTF_RE 第一周

主要是校赛的复现

Wuyu6a的猫猫寻找器

android逆向,正常拖jadx,发现rc4加密,解出来是‘youarejokkkker’,从别的师傅那了解MT资源管理器,一些信息可能藏在数据目录下,发现了一个新的apk,导出拖jadx解rc4得到真正flag。

rc4

有反调试

进程id的范围:win32是1~32,767,win64是1~4,294,967,295。

v0是根据当前时间的时间戳生成的

肯定是需要爆破找出来id

还有一个对文件读取,重命名的操作。

还有一个chacha20加密(说是通过字符串expand 32-byte k搜索得到),又去学了一下

chacha20是对称加密,加解密算法一样。是个矩阵加密。

有很多新东西,一点一点慢慢啃的

1.时间戳  2.rc4  3.chacha20  4.enc文件读取  5.反调试  6.进程id

还是有一点没搞懂,但逻辑已经很清晰了。

main.cpp
#include <stdio.h>
#include <windows.h>
#include "chacha20.h"


void rc4_init(unsigned char* s, unsigned char* key, unsigned long Len) //初始化函数
{
        int i = 0, j = 0;
        char k[256] = { 0 };
        unsigned char tmp = 0;
        for (i = 0; i < 256; i++) {
                s[i] = i;
                k[i] = key[i % Len];
        }

        for (i = 0; i < 256; i++) {
                j = (j + s[i] + k[i]) % 256;
                tmp = s[i];
                s[i] = s[j]; // 交换s[i]和s[j]
                s[j] = tmp;
        }
}

void rc4_crypt(unsigned char* s, unsigned char* Data, unsigned long Len) //加解密
{
        int i = 0, j = 0, t = 0;
        unsigned long k = 0;
        unsigned char tmp;
        for (k = 0; k < Len; k++) {
                i = (i + 1) % 256;
                j = (j + s[i]) % 256;
                tmp = s[i];
                s[i] = s[j]; //交换s[x]和s[y]
                s[j] = tmp;
                t = (s[i] + s[j]) % 256;
                Data[k] ^= s[t];
        }
}

void get_flag(unsigned char* mykey, int v0, int pid) {
        unsigned char s[256] = { 0 };
        unsigned char key[12] = "Encrypted!!";

        char hexData[48] = {              0xA3,0xA0,0xB2,0x7F,0xD1,0xB8,0xCA,0x06,0xDB,0xC6,0xA4,0x22,0x3B,0x2A,0x85,0x6E
,0x82,0x55,0x73,0xC1,0x0C,0x40,0xE8,0x81,0xCA,0x9A,0x62,0x01,0x31,0xAF,0xAF,0x70
,0x64,0xD3,0xBC,0xD7,0x6D,0x12,0xF4,0x2C,0x60,0x1D,0x7E,0x7F,0x67,0xDB,0x00,0x00

        };  //flag.enc


        int enc_len = strlen(hexData);
        rc4_init(s, key, strlen((const char*)key));
        rc4_crypt(s, (uint8_t*)hexData, enc_len);

        ChaCha20XOR((uint8_t*)mykey, 1, key, (uint8_t*)hexData, strlen(hexData));

        if (hexData[0] == 'h' && hexData[1] == 'y' && hexData[2] == 'n') {  //判定前三个字母是fla输出即可
                printf("timestamp:%d,pid:%d ", v0, pid);
                for (int i = 0; i < 48; i++) {
                        printf("%c", hexData[i]);
                }
                printf("\n");
                exit(0);
        }
        /*else {
                printf("you are wrong!\n");
        }*/
}

int main() {
        unsigned char mykey[32];
        int timestamp;
        DWORD Seed;

        timestamp = 1712827218;  // time(0);  2024-04-11 17:11:00
        for (int pid = 20000; pid <23000; pid++) {
                for (timestamp = 1712826660; timestamp <= 1712827218; timestamp++) {   //时间戳要从出题时间点开始算起
                        Seed = timestamp ^ pid;
                        srand(Seed);
                        for (int i = 0; i < 32; ++i)
                                mykey[i] = (unsigned __int16)rand() >> 8;

                        get_flag(mykey, timestamp, pid);  
                }
        }

        printf("end\n");
        return 0;
}

NSS中[柏鹭杯 2021]baby_go

和RC4这题比较像,写写试试,不过是go语言写的,只有一个ChaCha20-Poly1305,是一个组合加密算法,由ChaCha20和Poly1305两个密码学原语组成。

ChaCha20是一个流加密算法,它可以对任意长度的数据进行加密,并产生一个相同长度的密文。Poly1305是一个消息验证码,它可以对任意长度的消息进行验证,并输出一个固定长度的摘要。将这两个密码学原语结合起来,就可以实现一个安全高效的加密算法,即ChaCha20-Poly1305

对称密码,直接再次运行

得到flag.txt拿到flag

codeischange

加了反调试处一般为关键信息,进去看看

看汇编也不是花指令,那可能是smc自解密,动调一下,重定义函数。

输入15位,9*9排列,提取数据,发现是数独游戏。解出答案得到flag

该题属于是smc自解密。

[网鼎杯 2020 青龙组]jocker

VitualProtect函数,这里对内存权限进行了修改,大概率是SMC,或者函数加壳。

点encrypt函数报错,

先看汇编代码

动调进入encrypt函数

看别人的wp发现下面问题:

代码逆向的思路,动态调试使用的一些细节,堆栈不平衡相关知识

        堆栈不平衡那里没弄明白为什么sp是改为0,改成其他值会怎么样?

f5不能反编译可能是堆栈不平衡,须进行栈指针修改。options-->general-->勾选stack pointer

        动态调试时是怎么定位到循环结束后的语句的?

可以使用条件断点。

        代码逆向,最后那个找出key的思路要记起来

动态调试结束后encrypt函数又会识别错误,这是因为每次运行程序会执行脱壳代码,而回到静态调试时会回到脱壳前的状态,所以导致识别错误

一看好像还是没有结束,继续往下翻一下发现一个和encrypt()差不多函数,再次操作一下

代码差不多都出来了,开始分析写脚本

根据encrypt()得到

buffer='hahahaha_do_you_find_me?'
v1=[ 0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x09, 0x00, 
  0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 
  0x05, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x56, 0x00, 
  0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 
  0x0C, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x1F, 0x00, 
  0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 
  0x6B, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x59, 0x00, 
  0x00, 0x00, 0x0D]
enc=[]
flag=''
for i in range(0,len(v1),4):
    enc.append(v1[i])
for i in range(19):
    flag+=chr(enc[i]^ord(buffer[i]))
print(flag)
#flag{d07abccf8a410c

另一半应该在finally(),基于时间,好像不是伪随机,但可以通过(v3[(unsigned __int8)v3[5]] != a1[(unsigned __int8)v3[5]]),找到异或的值:猜测是 ':'^v4='}',得到v4

import random
import time
v3='%tp&:'
v1=time.time()
random.seed(v1)
# v4=random.randint()%100  需要两个参数
v4=random.randint(0,99)
print(v4)
v4=ord(':')^ord('}')
for i in range(len(v3)):
    print(chr(ord(v3[i])^v4),end='')
# b37a}

[NSSRound#3 Team]jump_by_jump_revenge

主要运用倒序,取模,爆破。

// v3 = sub_411037("%s", (char)Str1);
//for (i = 0; i < 29; ++i)
//{
//    Str1[i] = (Str1[i] + Str1[(i * v3 + 123) % 21]) % 96 + 32;
//    v3 = i + 1;
//}
//if (!j_strcmp(Str1, "~4G~M:=WV7iX,zlViGmu4?hJ0H-Q*"))
//puts("right!");
//else
//puts("nope!");
//return 0;
//}
//取模可以直接考虑爆破
#include <iostream>
#include <string>
using namespace std;

int main()
{
    string Str1 = "~4G~M:=WV7iX,zlViGmu4?hJ0H-Q*";
    int v3 = 0;
    for (int i = 28; i >= 0; i--) {
        int pos = (i * i + 123)%21;
        for (int j = 0; j <= 3; j++) {
            int tmp = Str1[i] - 32 + j * 96 - Str1[pos];
            if (tmp >= 33 && tmp <= 126) {
                Str1[i] = (char)tmp;
                break;
            }
        }
    }
    cout << Str1;
    return 0;
}

[HDCTF 2023]double_code

打开,没有main函数,string也没有什么特别明显信息,又从start一直进去,发现了一个code

v0 = &v10;
  for ( i = 254i64; i; --i )
  {
    *(_DWORD *)v0 = 3435973836;
    v0 += 4;
  }                                             // GetCurrentThreadId()
                                                // 返回值是调用线程的线程标识符。
  v20 = 0;
  sub_7FF6BC2A137A((__int64)&unk_7FF6BC2B401B);
  printf("alloc:%p\n", sub_7FF6BC2AF000);       // ***
  memset(v11, 0, 0x70ui64);
  v11[0] = 0x4000000070i64;
  v11[2] = L"open";
  v11[3] = L"notepad.exe";
  sub_7FF6BC2A10FA((__int64)v11);               // ***
  hSnapshot = j_CreateToolhelp32Snapshot(2u, 0);// 创建进程快照
  if ( hSnapshot == (HANDLE)-1i64 )
  {
    LastError = GetLastError();
    printf("CreateToolhelp32Snapshot:%d\n", LastError);
  }
  pe.dwSize = 568;                              // pe结构体的大小为568字节
  v14 = j_Process32FirstW(hSnapshot, &pe);
  if ( !v14 )
  {
    v3 = GetLastError();                        // 获取最近一次的错误代码
    printf("Process32First:%d\n", v3);
  }
  while ( v14 )
  {
    if ( !wcscmp(pe.szExeFile, L"notepad.exe") )// 比较两个宽字符,正数,0,负数
    {
      v20 = 1;
      th32ProcessID = pe.th32ProcessID;
      break;
    }
    v14 = j_Process32NextW(hSnapshot, &pe);
  }
  if ( !v20 )
    sub_7FF6BC2A109B("pid");
  v16 = qword_7FF6BC2AF368(0x1FFFFFi64, 0i64, th32ProcessID);
  if ( !v16 )
  {
    v4 = GetLastError();
    printf("\nopenprocess error%d\n", v4);
  }
  if ( !v20 )
    sub_7FF6BC2A109B("pid");
  printf("pid:%d", th32ProcessID);
  v17 = qword_7FF6BC2AF370(v16, 0i64, 405i64, 4096i64, 64);
  if ( !v17 )
  {
    v5 = GetLastError();
    printf("VirtualAllocEx error%d\n", v5);
  }
  if ( !(unsigned int)qword_7FF6BC2AF360(v16, v17, sub_7FF6BC2AF000, 405i64, 0i64) )
  {
    v6 = GetLastError();
    printf("WriteProcessMemory:%d\n", v6);
  }
  if ( !qword_7FF6BC2AF378(v16, 0i64, 0i64, v17, 0i64, 0, 0i64) )
  {
    v7 = GetLastError();
    printf("CreateRemoteThread:%d\n", v7);
  }
  sub_7FF6BC2A1311((__int64)v9, (__int64)&unk_7FF6BC2ABE80);
  return 0i64;

继续跟进去,发现了一个应该是关键函数,查了资料,搞清楚了

alloca是向申请内存,因此无需释放。

malloc 分配的内存是位于 堆中 的,并且没有 初始化内存的内容 ,因此基本上malloc之后,调用函数memset来初始化这部分的内存空间,需要用 Free 方式释放空间.

calloc则将初始化malloc这部分的内存,设置为0. 

realloc则对malloc申请的内存进行大小的调整.

free将malloc申请的内存最终需要通过该函数进行释放. 

sbrk则是增加数据段的大小;

alloca()和malloc()函数都会返回分配空间的首地址,一般是void型指针。

好像VirtualAllocEx    WriteProcessMemory     CreateRemoteThread  也是关键,分别是

        分配内存,写入内存,创建进程。

BOOL WriteProcessMemory(
  [in]  HANDLE  hProcess,
  [in]  LPVOID  lpBaseAddress,
  [in]  LPCVOID lpBuffer,  //指向写入内存的指针
  [in]  SIZE_T  nSize,
  [out] SIZE_T  *lpNumberOfBytesWritten
);
__int64 sub_14001F000()
{
  __int64 v0; // rdx
  __int64 v1; // rcx
  __int64 v2; // r8
  __int64 v3; // r9
  int v4; // esp
  unsigned __int64 v5; // rax
  int v6; // esp
  unsigned __int64 v7; // rax
  int v8; // esp
  unsigned __int64 v9; // rax
  int v10; // esp
  unsigned __int64 v11; // rax
  int v12; // esp
  unsigned __int64 v13; // rax
  char v15[41]; // [rsp+1Fh] [rbp-41h] BYREF
  int v16; // [rsp+48h] [rbp-18h]
  int v17; // [rsp+4Ch] [rbp-14h]
  int v18; // [rsp+50h] [rbp-10h]
  int v19; // [rsp+54h] [rbp-Ch]
  int v20; // [rsp+58h] [rbp-8h]
  int i; // [rsp+5Ch] [rbp-4h]

  MEMORY[0x140029C60]();
  *(_DWORD *)&v15[37] = 1;
  v16 = 5;
  v17 = 2;
  v18 = 4;
  v19 = 3;
  strcpy(v15, "************************************");
  for ( i = 0;
        (unsigned int)MEMORY[0x14003A250](v1, v0, v2, v3, *(_QWORD *)&v15[1], *(_QWORD *)&v15[9], *(_QWORD *)&v15[17]) > i;
        ++i )
  {
    v0 = (unsigned int)(i / 5);
    v1 = (unsigned int)(i % 5);
    v20 = i % 5;
    if ( i % 5 == 1 )
    {
      v5 = (unsigned int)(v4 + 31 + i);  //v5指向一个新地址
      v1 = *(unsigned __int8 *)v5 ^ 0x23u;  //(unsigned __int8 *) 强制将 v5 视为一个指向无符号字符(字节)的指针
      v0 = (unsigned int)(v4 + 31);    为下一次寻址做准备
      *(_BYTE *)(unsigned int)(v0 + i) = *(_BYTE *)v5 ^ 0x23;
//将地址 (v0 + i) 处的字节数据取出,并与 0x23 进行异或运算后,存储回原地址。这样就实现了对指定位置的字节数据进行修改
    }
    else
    {
      switch ( v20 )
      {
        case 2:
          v7 = (unsigned int)(v6 + 31 + i);
          v0 = (unsigned int)*(unsigned __int8 *)v7 + 2;
          v1 = (unsigned int)(v6 + 31);
          *(_BYTE *)(unsigned int)(v1 + i) = *(_BYTE *)v7 + 2;
          break;
        case 3:
          v9 = (unsigned int)(v8 + 31 + i);
          v0 = (unsigned int)*(unsigned __int8 *)v9 - 3;
          v1 = (unsigned int)(v8 + 31);
          *(_BYTE *)(unsigned int)(v1 + i) = *(_BYTE *)v9 - 3;
          break;
        case 4:
          v11 = (unsigned int)(v10 + 31 + i);
          v0 = (unsigned int)*(unsigned __int8 *)v11 - 4;
          v1 = (unsigned int)(v10 + 31);
          *(_BYTE *)(unsigned int)(v1 + i) = *(_BYTE *)v11 - 4;
          break;
        case 5:
          v13 = (unsigned int)(v12 + 31 + i);
          v0 = (unsigned int)*(unsigned __int8 *)v13 - 25;
          v1 = (unsigned int)(v12 + 31);
          *(_BYTE *)(unsigned int)(v1 + i) = *(_BYTE *)v13 - 25;
          break;
      }
    }
  }
  return 0i64;
}

故这就是加密算法了。

MEMORY[0x140029C60](); // 调用地址为 0x140029C60 的函数指针所指向的函数
for (i = 0; (unsigned int)MEMORY[0x14003A250](v1, v0, v2, v3, *(_QWORD *)&v15[1], *(_QWORD *)&v15[9], *(_QWORD *)&v15[17]) > i; ++i)
    // 使用地址为 0x14003A250 的函数指针所指向的函数,进行循环条件(函数返回值和i)判断

v15应该就是flag,进行了循环switch-case操作

X = [0x48,0x67,0x45,0x51,0x42,0x7b,0x70,0x6a,0x30,0x68,0x6c,0x60,0x32,0x61,0x61,0x5f,0x42,0x70,0x61,0x5b,0x30,0x53,0x65,0x6c,0x60,0x65,0x7c,0x63,0x69,0x2d,0x5f,0x46,0x35,0x70,0x75,0x7d]
for i in range(len(X)):
    if(i%5 == 1):
        X[i] ^= 0x23
    if(i%5 == 2):
        X[i] -= 2
    if(i%5 == 3):
        X[i] += 3
    if(i%5 == 4):
        X[i] += 4
    if(i%5 == 5):
        X[i] += 25
for i in X:
    print(chr(i),end='')
# HDCTF{Sh3llC0de_and_0pcode_al1_e3sy}

攻防世界Windows_Reverse2

先脱北斗壳,pushad下硬件断点F9到popad,调到call,jmp一个极远地址后,dump程序,拖进ida

挨个分析

再看return的函数,本来没看出来是什么,gpt上问了一下,三字节变四字节发现应该是base64

看一下byte又不是base表

但这有一个补‘=’的函数

所以应该就是base64

发现好像要异或byte_83020[(unsigned __int8)*(&v17 + v8)] ^ 0x76

之后得到标准base表

总的流程,输入的大写的十六进制----->转为十进制实数------>base64加密------->与reverse+相比较
所以逆向逻辑就是reverse+来base64解密------>转大写十六进制。

import base64
s='reverse+'
print base64.b64decode(s).encode('hex').upper()

得到flag

[HNCTF 2022 WEEK2]Try2Bebug_Plus

一道动调的题目,发现一些关键函数不能直接f8跳过了,需要慢慢调,查看关键变量的内存地址

猜测v3就是flag,循环后查看v3

或者把数据提取出来,然后跑程序。

reverse-for-the-holy-grail-350

是c++写的,比较难看,要输入三次,但前两次没有影响,最后一次输入即为flag

倒着看

__int64 __fastcall stringMod(__int64 *a1)
{
  __int64 v1; // r9
  __int64 v2; // r10
  __int64 v3; // rcx
  int v4; // r8d
  int *v5; // rdi
  int *v6; // rsi
  int v7; // ecx
  int v8; // r9d
  int v9; // r10d
  unsigned int v10; // eax
  int v11; // esi
  int v12; // esi
  int v14[18]; // [rsp+0h] [rbp-60h] BYREF
  __int64 v15; // [rsp+48h] [rbp-18h] BYREF

  memset(v14, 0, sizeof(v14));                  // v4>=0
  v1 = a1[1];
  if ( v1 )
  {
    v2 = *a1;
    v3 = 0LL;
    v4 = 0;
    do
    {
      v12 = *(char *)(v2 + v3);                 // 读取基址v12+偏移量v3的空间,即读取a1
      v14[v3] = v12;                            // v14.append(v12)
      if ( 3 * ((unsigned int)v3 / 3) == (_DWORD)v3 && v12 != firstchar[(unsigned int)v3 / 3] )
        v4 = -1;                                // first是v3是3倍数,
                                                // 后一个v12==fir[v3/3]
                                                // 第0,3,6,9,12,15字符
      ++v3;
    }
    while ( v3 != v1 );                         // wp说全部循环
  }
  else
  {
    v4 = 0;
  }                                             // 
                                                // 
  v5 = v14;                                     // 指向同一空间的两指针
  v6 = v14;
  v7 = 666;
  do
  {
    *v6 = v7 ^ *(unsigned __int8 *)v6;
    v7 += v7 % 5;
    ++v6;
  }
  while ( &v15 != (__int64 *)v6 );              // 应该也是全部循环
                                                // v7也在变
                                                // 
  v8 = 1;
  v9 = 0;
  v10 = 1;
  v11 = 0;
  do
  {
    if ( v11 == 2 )                             // 对第2,5,8,11,14,17进行操作
    {
      if ( *v5 != thirdchar[v9] )
        v4 = -1;
      if ( v10 % *v5 != masterArray[v9] )       // 相当于v5[0]*v5[1]%v5[2]!=masterArray[v9]
        v4 = -1;                                // 得到第1,4,7,10,13,16个数字
      ++v9;
      v10 = 1;
      v11 = 0;
    }
    else
    {
      v10 *= *v5;
      if ( ++v11 == 3 )                         // 控制上面每3位执行一次
        v11 = 0;
    }
    ++v8;
    ++v5;
  }
  while ( v8 != 19 );                           // flag应该18位
                                                // 
  return (unsigned int)(v7 * v4);
}

嗯,分析起来还是有点难度的。

这个函数没有改变输入。

就开始写脚本了(对字符操作的话c简单些):

#include <stdio.h>
int main(int argc, char* argv[]) {
	int firstchar[] = { 0x41, 0x69, 0x6E, 0x45, 0x6F, 0x61 };
	int thirdchar[] = { 0x2EF, 0x2C4, 0x2DC, 0x2C7, 0x2DE, 0x2FC };
	int masterArray[] = { 0x1D7, 0x0C, 0x244, 0x25E, 0x93, 0x6C };
	int v7 = 666;
	int v77[18] = { 0 };
	char flag[19] = { 0 };
	// 0 3 6 ... 位 先执行所以初次不用异或
	for (int i = 0; i < 18; i = i + 3) {
		flag[i] = firstchar[i / 3];
	}
	// 先把每位要异或数据求出
	for (int i = 0; i < 18; i++) {
		v77[i] = v7;
		v7 += v7 % 5;
	}
	// 2 5 8 ... 位 后执行需要异或
	for (int i = 2; i < 18; i = i + 3) {
		flag[i] = thirdchar[i / 3] ^ v77[i];
	}
	// 1 4 7 ... 关键判断,暴力破解
	for (int i = 1; i < 18; i = i + 3) {
		// 缩小判断,可打印字符
		for (int j = 32; j < 127; j++) {
			if ((flag[i - 1] ^ v77[i - 1]) * (j ^ v77[i]) % (flag[i + 1] ^
				v77[i + 1]) == masterArray[i / 3]) {
					flag[i] = j;
				break;
			}
		}
	}
	printf("tuctf{%s}\n", flag);
	return 0;
}

babymips

这个题还是比较简单的

按位运算,v1=(a1[i]>>2 | (a1[i])<<6)即a1低2位和高6位互换

如101100_11--->11_101100

flag="Q|j{g"
flag=list(map(ord,flag))
flag+=[0x52,0xFD,0x16,0xA4,0x89,0xBD,0x92,0x80,0x13,0x41,0x54,0xA0,0x8D,0x45,0x18,
0x81,0xDE,0xFC,0x95,0xF0,0x16,0x79,0x1A,0x15,0x5B,0x75,0x1F]
for i in range(5,32):
   if i&1!=0:
      flag[i]=((flag[i]<<2)|(flag[i]>>6))&0xFF
   else:
    flag[i]=((flag[i]>>2)|(flag[i]<<6))&0xFF
for i in range(32):
   flag[i]=chr(flag[i]^(32-i))
print(''.join(flag))

[HDCTF 2023]enc

ida中一个橙色变量value may be undefined,可能是未定义,无法确定初始值

典型的TEA加密

v7,v8连续是密文,v9是密钥,得到明文

from ctypes import *
#tea
def decrypt(v, k):
    v0 = c_uint32(v[0])
    v1 = c_uint32(v[1])
    delta = 0x9e3779b9
    sum1 = c_uint32(delta * 32)
    for i in range(32):
        v1.value -= ((v0.value << 4) + k[2]) ^ (v0.value + sum1.value) ^ ((v0.value >> 5) + k[3])
        v0.value -= ((v1.value << 4) + k[0]) ^ (v1.value + sum1.value) ^ ((v1.value >> 5) + k[1])
        sum1.value -= delta
    return v0.value, v1.value
if __name__ == '__main__':
    a = [1627184887, 37149676]
    k = [18, 52, 86, 120]
    print("解密后数据:", decrypt(a,k))
# 得到(3,4) 3就是我们输入的

又发现一个函数

没有逻辑问题,不是花指令,再结合什么对内存操作,判断是smc加密

下断点调试,可知smc解密的位置,可在反汇编窗口,通过搜索”hdctf“得到具体位置。

重新定义函数选中按p

 

发现是rc4加密

def rc4_decrypt(ciphertext, key):
    # 初始化 S-box
    S = list(range(256))
    j = 0
    for i in range(256):
        j = (j + S[i] + key[i % len(key)]) % 256
        S[i], S[j] = S[j], S[i]

    # 初始化变量
    i = j = 0
    plaintext = []

    # 解密过程
    for byte in ciphertext:
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        k = S[(S[i] + S[j]) % 256]
        plaintext.append(byte ^ k)

    return bytes(plaintext)


# 示例用法
encrypted_data = [0xF,0x94,0xAE,0xF2,0xC0,0x57,0xC2,0xE0,0x9A,0x45,0x37,0x50,0xF5,0xA0,0x5E,0xCB,0x2C,0x16,0x28,0x29,0xFE,0xFF,0x33,0x46,0xE,0x57,0x82,0x22,0x52,0x26,0x2B,0x6E,0xE4,0x82,0x24]
  # 替换成你的密文
encryption_key = b'you_are_master'  # 替换成你的密钥

decrypted_data = rc4_decrypt(encrypted_data, encryption_key)
print("Decrypted Data:", decrypted_data.decode('utf-8'))

# Decrypted Data: HDCTF{y0u_ar3_rc4_t3a_smc_m4ster!!}

也可以使用ida内嵌脚本

for i in range(0x0041D000,0x0041E600):
    patch_byte(i,get_wide_byte(i)^3)
print('done')

感谢https://blog.csdn.net/liaochonxiang/article/details/130997949

  • 29
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Tea(Tiny Encryption Algorithm)是一种加密算法,也是一种对称密钥密码体制。它由科学家卡尔·诺尔斯(David Wheeler)和罗杰·斯托夫门汉(Roger Needham)在1994年共同提出。该算法基于32位的块密码结构,使用相同的密钥对数据进行加密和解密。 Tea算法的加密过程包含多个轮次,每个轮次都包含了一系列的加密运算。这些运算包括轮密钥加、混乱和置换。在加密过程中,数据将被分割成两个32位的部分,并经过一系列交替的操作进行处理。经过多轮次的加密,最后得到加密后的数据。 解密过程与加密过程类似,只是在每个轮次的加密运算中使用了轮密钥的逆操作。这样,使用相同的密钥和轮次数,就可以将加密后的数据解密回原始数据。 使用Python编程语言可以实现Tea算法的加密和解密过程。我们可以自定义加密函数和解密函数,并根据Tea算法的规则实现相应的加密运算。通过调用这些函数,就能够对数据进行加密和解密操作。 在Python中,可以通过使用字节流和位运算等操作,实现32位数据的分割和操作过程。通过使用循环和条件语句,可以模拟多轮次的加密和解密过程。最终,通过打印输出或保存到文件中,就可以得到加密或解密后的结果。 总结来说,使用Python可以编写Tea算法的加密和解密函数,实现对数据的加密和解密过程。这是一种基于对称密钥的加密算法,通过定义相应的运算和使用相同的密钥,可以实现加密和解密过程。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值