TemporalParadox
根据ida的报错 patch掉花指令之后还原代码爆破即可
可惜当时做的时候没有看出花指令,然后想通过Frida Hook函数爆破,然后就没写出来呜呜(┭┮﹏┭┮)
(事实证明Frida爆破也是一种可行的方式,需要hook time64() 函数,并通过多进程的方式爆破)
main.cpp
#include <iostream>
#include <string>
#include <Windows.h>
#include <cmath>
#include <sstream>
#include "md5.h"
using namespace std;
unsigned int time0 = 0;
unsigned int arr[] = {
0xCC, 0xB4, 0xFFFFFF94, 0xFFFFFF86, 0xFFFFFF9A, 0xFFFFFF8A, 0xFFFFFF9A, 0xFFFFFF8E, 0xFFE7AC2D,
0xA2,0xFFFFFF9A, 0xAE, 0xFFB70487, 0xD2, 0xCC, 0xDE, 0xFFFFFF96, 0xCC, 0xCC, 0xFFFFE65F, 0xFFF7E40F,
0xFFFFFF86, 0xB4, 0xFFFFE65F, 0xFFFF1957, 0xFFFFFF94, 0xFFFFFF8C, 0xFFFFFF88, 0xC6, 0xFFFFFF98,
0xFFFFFF92, 0xFFFD4C05
};
// ========== 全局查表表(根据实际值初始化) ==========
uint8_t SBox[16] = { 0xE,0x4,0xD,0x1,0x2,0xF,0xB,0x8,0x3,0xA,0x6,0xC,0x5,0x9,0x0,0x7 }; // dword_7FF6ABB6C020
uint8_t Perm[16] = { 0x1,0x5,0x9,0xD,0x2,0x6,0xA,0xE,0x3,0x7,0xB,0xF,0x4,0x8,0xC,0x10 }; // dword_7FF6ABB6C0A0
int encode_0(int index, int random) {
return (random << (4 * (index - 1))) >> 16;
}
int scramble(int val) {
for (int i = 0; i < 4; ++i) {
int high_nibble = (val >> 12) & 0xF;
val = (val << 4) | SBox[high_nibble];
}
return val;
}
int permute_bits(int val) {
int result = 0;
for (int i = 0; i < 16; ++i) {
int src_bit = (val >> (Perm[i] - 1)) & 1;
result |= (src_bit << i);
}
return result;
}
int mix_round(int input) {
int mixed = scramble(input);
return permute_bits(mixed);
}
uint32_t generate_cipher(uint32_t time, int random) {
uint32_t val = time;
// 3轮扰动
for (int i = 1; i <= 3; ++i) {
val ^= encode_0(i, random);
val = mix_round(val);
}
// 第4轮单独扰动
val ^= encode_0(4, random);
val = scramble(val);
// 最终 XOR
val ^= encode_0(5, random);
return val;
}
string init_salt() {
std::stringstream ss;
for (int i = 0; i <= 31; i++) {
int v = arr[i];
int c;
if (v >= 0)
{
c = v / 3 + '0';
}
else if (v >= -728)
{
c = ~v;
}
else
{
double f = log(static_cast<double>(-v)); // 假设 sub_7FF6ABB631D0 是 log()
c = static_cast<int>(f / 1.09861228866811 - 6.0 + 48.0);
}
ss << static_cast<char>(c);
}
return ss.str();
}
void init_time0(__time64_t current_time) {
if (!current_time) {
time0 = 1;
}
else {
time0 = current_time;
}
}
int encode() {
unsigned int tmp;
tmp = (((time0 << 13) ^ time0) >> 17) ^ (time0 << 13) ^ time0;
time0 = (tmp << 5) ^ tmp;
return time0 & 0x7FFFFFFF;
}
void bruteforce() {
const std::string target_md5 = "8a2fc1e9e2830c37f8a7f51572a640aa";
string salt_str = init_salt();
for (__time64_t current_time = 0x686D4081; current_time <= 0x686E3153; ++current_time) {
init_time0(current_time);
unsigned int key[4] = { 0 };
for (int i = 0; i < encode(); i++) {
key[0] = encode();
key[1] = encode();
key[2] = encode();
key[3] = encode();
}
unsigned int random_val = encode();
double val_a = 0x61;
double val_b = val_a * pow((key[0] | key[2]), 2.0);
double val_c = 0xb;
std::stringstream ss;
if (val_b == pow((key[1] | key[3]), 2.0) * val_c) {
uint32_t cipher = generate_cipher(current_time, random_val);
ss << "salt=" << salt_str
<< "&t=" << current_time
<< "&r=" << random_val
<< "&cipher" << cipher;
} else {
ss << "salt=" << salt_str
<< "&t=" << current_time
<< "&r=" << random_val
<< "&a=" << key[0]
<< "&b=" << key[1]
<< "&x=" << key[2]
<< "&y=" << key[3];
}
std::string full = ss.str();
std::string hash = md5(full);
if (hash == target_md5) {
cout << "[*] Match found!" << endl;
cout << "Time: " << current_time << " (0x" << std::hex << current_time << ")" << endl;
cout << "Raw string: " << full << endl;
cout << "MD5: " << hash << endl;
return;
}
}
cout << "[*] No match found in given time range." << endl;
}
int main() {
bruteforce();
return 0;
}
其中md5的实现就不贴上来了,从网上抄一份就行,或者直接ai生成.
终焉之门
这道题目是和opengl相关的,比赛的时候完全不知道着色器函数逻辑,就没做出来(┭┮﹏┭┮)
问了一下GPT,这个应该就是着色器函数启动的地方了,然后从别人的WP上看到了这里就是最后着色器内部代码解密出来的地方
__int64 __fastcall sub_7FF63580E700(__int64 a1, unsigned int a2)
{
__int64 v3; // rbx
unsigned int v4; // esi
char *v5; // rdi
__int64 v6; // rcx
int v8; // [rsp+24h] [rbp-24h] BYREF
int v9; // [rsp+28h] [rbp-20h] BYREF
int v10[7]; // [rsp+2Ch] [rbp-1Ch] BYREF
__int64 v11; // [rsp+50h] [rbp+8h] BYREF
v11 = a1;
v3 = qword_7FF6358F1B70(a2);
qword_7FF6358F09A0(v3, 1i64, &v11, 0i64);
v8 = 0;
qword_7FF6358F1CA8(v3);
qword_7FF6358F13B0(v3, 35713i64, &v8);
if ( !v8 )
{
switch ( a2 )
{
case 0x8B31u:
sub_7FF63588D460(4i64, "SHADER: [ID %i] Failed to compile vertex shader code", v3);
break;
case 0x91B9u:
sub_7FF63588D460(4i64, "SHADER: [ID %i] Failed to compile compute shader code", v3);
break;
case 0x8B30u:
sub_7FF63588D460(4i64, "SHADER: [ID %i] Failed to compile fragment shader code", v3);
break;
}
v9 = 0;
qword_7FF6358F13B0(v3, 35716i64, &v9);
v4 = v9;
if ( v9 > 0 )
{
v10[0] = 0;
v5 = calloc(v9, 1ui64);
qword_7FF6358F13D0(v3, v4, v10, v5);
sub_7FF63588D460(4i64, "SHADER: [ID %i] Compile error: %s", v3, v5);
free(v5);
}
v6 = v3;
LODWORD(v3) = 0;
qword_7FF6358F1A98(v6);
return v3;
}
switch ( a2 )
{
case 0x8B31u:
sub_7FF63588D460(3i64, "SHADER: [ID %i] Vertex shader compiled successfully", v3);
return v3;
case 0x91B9u:
sub_7FF63588D460(3i64, "SHADER: [ID %i] Compute shader compiled successfully", v3);
return v3;
case 0x8B30u:
sub_7FF63588D460(3i64, "SHADER: [ID %i] Fragment shader compiled successfully", v3);
return v3;
default:
return v3;
}
}
跟踪进去可以看到一大串数据,转化成数组后dump出来
又是沟槽的虚拟机,出题人已经沉迷于虚拟机无法自拔了
#version 430 core
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
layout(std430, binding = 0) buffer OpCodes { int opcodes[]; };
layout(std430, binding = 2) buffer CoConsts { int co_consts[]; };
layout(std430, binding = 3) buffer Cipher { int cipher[16]; };
layout(std430, binding = 4) buffer Stack { int stack_data[256]; };
layout(std430, binding = 5) buffer Out { int verdict; };
const int MaxInstructionCount = 1000;
void main()
{
if (gl_GlobalInvocationID.x > 0) return;
uint ip = 0u;
int sp = 0;
verdict = -233;
while (ip < uint(MaxInstructionCount))
{
int opcode = opcodes[int(ip)];
int arg = opcodes[int(ip)+1];
switch (opcode)
{
case 2:
stack_data[sp++] = co_consts[arg];
break;
case 7:
{
int b = stack_data[--sp];
int a = stack_data[--sp];
stack_data[sp++] = a + b;
break;
}
case 8:
{
int a = stack_data[--sp];
int b = stack_data[--sp];
stack_data[sp++] = a - b;
break;
}
case 14:
{
int b = stack_data[--sp];
int a = stack_data[--sp];
stack_data[sp++] = a ^ b;
break;
}
case 15:
{
int b = stack_data[--sp];
int a = stack_data[--sp];
stack_data[sp++] = int(a == b);
break;
}
case 16:
{
bool ok = true;
for (int i = 0; i < 16; i++)
{
if (stack_data[i] != (cipher[i] - 20))
{
ok = false;
break;
}
}
verdict = ok ? 1 : -1;
return;
}
case 18:
{
int c = stack_data[--sp];
if (c == 0) ip = uint(arg);
break;
}
default:
verdict = 500;
return;
}
ip+=2;
}
verdict = 501;
}
观察这里的数据,结合里边的binding值,在代码中标记出变量
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
layout(std430, binding = 0) buffer OpCodes { int opcodes[]; };
layout(std430, binding = 2) buffer CoConsts { int co_consts[]; };
layout(std430, binding = 3) buffer Cipher { int cipher[16]; };
layout(std430, binding = 4) buffer Stack { int stack_data[256]; };
layout(std430, binding = 5) buffer Out { int verdict; };
跟踪进去把里面的值dump出来,再根据原来的逻辑还原代码,是一个简单的虚拟机
#include <stdio.h>
#include <stdlib.h>
#define STACK_SIZE 256
#define MAX_INSTRUCTIONS 1000
unsigned char opcodes[] = {
0x2, 0x0, 0x2, 0x1, 0x2, 0x0, 0xE, 0x0, 0x2, 0x10, 0x8, 0x0, 0x2, 0x2, 0x2, 0x1, 0xE, 0x0, 0x2, 0x11,
0x8, 0x0, 0x2, 0x3, 0x2, 0x2, 0xE, 0x0, 0x2, 0x12, 0x7, 0x0, 0x2, 0x4, 0x2, 0x3, 0xE, 0x0, 0x2, 0x13,
0x7, 0x0, 0x2, 0x5, 0x2, 0x4, 0xE, 0x0, 0x2, 0x14, 0x8, 0x0, 0x2, 0x6, 0x2, 0x5, 0xE, 0x0, 0x2, 0x15,
0x7, 0x0, 0x2, 0x7, 0x2, 0x6, 0xE, 0x0, 0x2, 0x16, 0x7, 0x0, 0x2, 0x8, 0x2, 0x7, 0xE, 0x0, 0x2, 0x17,
0x7, 0x0, 0x2, 0x9, 0x2, 0x8, 0xE, 0x0, 0x2, 0x18, 0x7, 0x0, 0x2, 0xA, 0x2, 0x9, 0xE, 0x0, 0x2, 0x19,
0x7, 0x0, 0x2, 0xB, 0x2, 0xA, 0xE, 0x0, 0x2, 0x1A, 0x7, 0x0, 0x2, 0xC, 0x2, 0xB, 0xE, 0x0, 0x2, 0x1B,
0x8, 0x0, 0x2, 0xD, 0x2, 0xC, 0xE, 0x0, 0x2, 0x1C, 0x8, 0x0, 0x2, 0xE, 0x2, 0xD, 0xE, 0x0, 0x2, 0x1D,
0x7, 0x0, 0x2, 0xF, 0x2, 0xE, 0xE, 0x0, 0x2, 0x1E, 0x8, 0x0, 0x10, 0x0, 0x2, 0x10, 0x2, 0x11, 0xF, 0x0,
0x12, 0x54, 0x2, 0x1F, 0x1, 0x0, 0x3, 0x1
};
unsigned char co_consts[] = {
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0xB0, 0xC8, 0xFA, 0x86, 0x6E, 0x8F, 0xAF, 0xBF, 0xC9, 0x64, 0xD7, 0xC3, 0xE3, 0xEF, 0x87, 0x0
};
unsigned char cipher[] = {
0xF3, 0x82, 0x6, 0x1FD, 0x150, 0x38, 0xB2, 0xDE, 0x15A, 0x197, 0x9C, 0x1D7, 0x6E, 0x28, 0x146, 0x97
};
int stack[STACK_SIZE];
int verdict = -233;
int main() {
unsigned int ip = 0;
int sp = 0;
while (ip < MAX_INSTRUCTIONS) {
int opcode = opcodes[ip];
int arg = opcodes[ip + 1];
printf("ip=%03d: ", ip); // 输出指令地址
switch (opcode) {
case 2: // PUSH
stack[sp++] = co_consts[arg];
printf("PUSH co_consts[%d] = %d\n", arg, co_consts[arg]);
break;
case 7: // ADD
{
int b = stack[--sp];
int a = stack[--sp];
stack[sp++] = a + b;
printf("ADD => %d + %d = %d\n", a, b, a + b);
break;
}
case 8: // SUB
{
int a = stack[--sp];
int b = stack[--sp];
stack[sp++] = a - b;
printf("SUB => %d - %d = %d\n", a, b, a - b);
break;
}
case 14: // XOR
{
int b = stack[--sp];
int a = stack[--sp];
stack[sp++] = a ^ b;
printf("XOR => %d ^ %d = %d\n", a, b, a ^ b);
break;
}
case 15: // CMP_EQ
{
int b = stack[--sp];
int a = stack[--sp];
int eq = (a == b) ? 1 : 0;
stack[sp++] = eq;
printf("CMP_EQ => %d == %d => %d\n", a, b, eq);
break;
}
case 16: // CHECK
{
int ok = 1;
for (int i = 0; i < 16; ++i) {
if (stack[i] != (cipher[i] - 20)) {
ok = 0;
break;
}
}
verdict = ok ? 1 : -1;
printf("CHECK => %s\n", ok ? "SUCCESS" : "FAIL");
goto end;
}
case 18: // JUMP_IF_ZERO
{
int c = stack[--sp];
if (c == 0) {
printf("JUMP_IF_ZERO to %d (taken)\n", arg);
ip = arg;
continue;
} else {
printf("JUMP_IF_ZERO to %d (skipped)\n", arg);
}
break;
}
default:
verdict = 500;
printf("INVALID OPCODE %d at ip=%d\n", opcode, ip);
goto end;
}
ip += 2;
}
verdict = 501;
end:
printf("Verdict: %d\n", verdict);
if (verdict == 1) {
printf("Final stack[0..15]:\n");
for (int i = 0; i < 16; ++i) {
printf("%d ", stack[i]);
}
printf("\n");
}
return 0;
}
co_consts的前16个字符是空的,结合前面我们输入的32个字符的数据,和代码将两个字符合并的逻辑,逆向出能使这里的代码通过检测的input
L3HCTF{df6ef2e93c249eca468388c35a143283}
easyvm
主函数很简单,最重要的就是这三个函数
由于VM函数内部体积庞大,程序处理流程也很复杂难以跟踪,我们选择跟踪运算符的执行流程,查看相关的数据处理过程,从而推断出加密算法.
具体操作见L3HCTF WP | Liv's blog师傅的博客
要补充的是下条件断点的时候注意取消这个选项,这样运行时不会停下,方便我们查看调试信息
这里截取一段output出的数据
91919190 = 32323232 << 3;
// input[1] << 3
36FD3D5D = 91919190 + A56BABCD;
// (input[1] << 3) + A56BABCD
32323232 = 32323232 + 0;
// tmp1 = input[1] + sum
32323232 = 32323232 + 0;
// tmp2 = tmp1 + 0
4CF0F6F = 32323232 ^ 36FD3D5D;
// ((input[1] << 3) + A56BABCD) ^ (input[1] + sum + 0)
3232323 = 32323232 >> 4;
// input[1] >> 4
3232322 = 3232323 + FFFFFFFF;
// (input[1] >> 4) + FFFFFFFF
7EC2C4D = 4CF0F6F ^ 3232322;
// ((input[1] << 3) + A56BABCD) ^ (input[1] + sum + 0) ^ ((input[1] >> 4) + FFFFFFFF)
391D5D7E = 7EC2C4D + 31313131;
// v0 += ((input[1] << 3) + A56BABCD) ^ (input[1] + sum + 0) ^ ((input[1] >> 4) + FFFFFFFF)
11223344 = 0 + 11223344;
// sum = 0 + 11223344
E47575F8 = 391D5D7E << 2;
// v0 << 2
E47575F7 = E47575F8 + FFFFFFFF;
// (v0 << 2) + FFFFFFFF
4A3F90C2 = 391D5D7E + 11223344;
// v0 + sum
F60D7FC3 = 4A3F90C2 + ABCDEF01;
// v0 + sum + ABCDEF01
12780A34 = F60D7FC3 ^ E47575F7;
// ((v0 << 2) + FFFFFFFF) ^ (v0 + sum + ABCDEF01)
1C8EAEB = 391D5D7E >> 5;
// v0 >> 5
A73496B8 = 1C8EAEB + A56BABCD;
// (v0 >> 5) + A56BABCD
B54C9C8C = 12780A34 ^ A73496B8;
// ((v0 << 2) + FFFFFFFF) ^ (v0 + sum + ABCDEF01) ^ (v0 >> 5) + A56BABCD)
E77ECEBE = B54C9C8C + 32323232;
// v1 += ((v0 << 2) + FFFFFFFF) ^ (v0 + sum + ABCDEF01) ^ ((v0 >> 5) + A56BABCD)
3F = 40 - 1; // 轮数减1
标准的TEA加密算法
void encrypt (uint32_t* v, uint32_t* k) {
uint32_t v0=v[0], v1=v[1], sum=0, i; /* set up */
uint32_t delta=0x9e3779b9;
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
for (i=0; i < 32; i++) { /* basic cycle start */
sum += delta;
v0 += ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
v1 += ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
} /* end cycle */
v[0]=v0; v[1]=v1;
}
根据运算分析出来的加密算法
unsigned int key[] = {0, 0xA56BABCD, 0xFFFFFFFF, 0xABCDEF01}
void encrypt_easy_vm (uint32_t* v, uint32_t* k) {
uint32_t v0=v[0], v1=v[1], sum=0, i; /* set up */
uint33_t delta=0x11223344;
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3]; /* cache key */
for (i=0; i < 40; i++) { /* basic cycle start */
v0 += ((v1<<3) + k1) ^ (v1 + sum + k0) ^ ((v1>>4) + k2);
sum += delta;
v1 += ((v0<<2) + k2) ^ (v0 + sum + k3) ^ ((v0>>5) + k1);
} /* end cycle */
v[0]=v0;
v[1]=v1;
}
可以分析出这是经过修改后的tea算法,从cmp_with_cipher函数中dump出密文编写脚本即可解密
注意的是delta是全局变量,用于每一次cipher,所以我们逆序编写脚本
#include <iostream>
#include <Windows.h>
const uint32_t delta = 0x11223344;
DWORD sum = delta * 0x40 * 4;
void decrypt_easy_vm(uint32_t *v, uint32_t *k)
{
uint32_t v0 = v[0], v1 = v[1];
uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
printf("sum: %X\n", sum);
for (uint32_t i = 0; i < 0x40; i++) /* 反向循环 40 次 */
{
v1 -= ((v0 << 2) + k2) ^ (v0 + sum + k3) ^ ((v0 >> 5) + k1);
sum -= delta;
v0 -= ((v1 << 3) + k1) ^ (v1 + sum + k0) ^ ((v1 >> 4) + k2);
}
v[0] = v0;
v[1] = v1;
printf("v0 : %X\nv1 : %X\n", v0, v1);
}
int main() {
unsigned int key[] = { 0, 0xA56BABCD, 0xFFFFFFFF, 0xABCDEF01 };
unsigned int v8[8];
v8[0] = 0x877A62A6;
v8[1] = 0x6A55F1F3;
v8[2] = 0xAE194847;
v8[3] = 0xB1E643E7;
v8[4] = 0xA94FE881;
v8[5] = 0x9BC8A28A;
v8[6] = 0xC4CFAA9F;
v8[7] = 0xF1A00CA1;
for (int i = 6; i >= 0; i -= 2) {
decrypt_easy_vm(&v8[i], key);
}
printf("L3HCTF{%.32s}", v8);
return 0;
}
L3HCTF{9c50d10ba864bedfb37d7efa4e110bf2}
ez_android
java层有很严重的混淆,直接观察lib
筛选ez_android_lib可以找到这些函数,一个个观察可以发现这个函数有加密的逻辑
丢给gpt分析加密的逻辑,直接一把梭了
# -*- coding: utf-8 -*-
K = b"dGhpc2lzYWtleQ" # 14 字节密钥表(ASCII),非 base64 解码后的内容
TARGET_HEX = "0c1525a06396400a5c1665402906e11f90722c0e4c0a02fc4f322a"
TARGET = bytes.fromhex(TARGET_HEX)
def rol8(x, r):
return ((x << r) | (x >> (8 - r))) & 0xFF
def ror8(x, r):
return ((x >> r) | ((x << (8 - r)) & 0xFF)) & 0xFF
def idx_odd(i: int) -> int:
# 生成序列 1,3,5,7,9,11,13,1,3,5,...
x = 2 * i + 1
k = (147 * x) >> 11
return x - 14 * k
def encrypt27(buf: bytes) -> bytes:
assert len(buf) == 27
out = bytearray(27)
for i in range(27):
i1 = i % 14
odd = idx_odd(i)
t = (K[odd] + (buf[i] ^ K[i1])) & 0xFF
r = K[(i + 3) % 14] & 7
out[i] = K[(i + 4) % 14] ^ rol8(t, r)
return bytes(out)
def decrypt27(ct: bytes) -> bytes:
assert len(ct) == 27
out = bytearray(27)
for i in range(27):
odd = idx_odd(i)
r = K[(i + 3) % 14] & 7
t = ct[i] ^ K[(i + 4) % 14]
z = ror8(t, r)
x = ((z - K[odd]) & 0xFF) ^ K[i % 14]
out[i] = x
return bytes(out)
def main():
# 1) 由目标密文反推原始明文(正确输入)
plain = decrypt27(TARGET)
print("明文:", plain.decode(errors="ignore"))
print("明文hex:", plain.hex())
# 2) 验证:再次加密应回到目标
enc = encrypt27(plain)
print("回加密是否匹配目标:", enc == TARGET)
# 3) 也可自行测试任意输入
candidate = b"L3HCTF{ez_rust_reverse_lol}"
print("测试候选是否正确:", encrypt27(candidate) == TARGET)
if __name__ == "__main__":
main()
L3HCTF{ez_rust_reverse_lol}
Obfuscated
超绝混淆题目,用了一大堆函数把栈帧混乱了,并且直接跳过程序还会直接结束.
汇编层单步跟踪可以找到一个ptrace反调试,nop后可以继续调试,往下继续调试一大段可以找到scanf和cmp,从中可以推断出flag的长度是32个字节
然后观察函数表,排除了一大段用于控制流混淆的函数,可以找到加密函数
由于我用了htrng和d810两个去混淆插件,所以看起来还是比较清晰的,但是动态调试的时候就会出错,所以要跟着汇编观察一下变量
识别出这是魔改了的RC5加密函数
标准RC5加密
void RC5_Encrypt(WORD *pt, WORD *ct) {
WORD A = pt[0] + S[0];
WORD B = pt[1] + S[1];
for (int i = 1; i <= r; i++) {
A = ROTL(A ^ B, B) + S[2 * i];
B = ROTL(B ^ A, A) + S[2 * i + 1];
}
ct[0] = A;
ct[1] = B;
}
复原的修改后的加密函数,轮数是12轮
void RC5_Encrypt(WORD *pt, WORD *ct) {
WORD A = pt[0] + S[0];
WORD B = pt[1] + S[1];
for (int i = 1; i <= r; i++) {
A = ROTL(A ^ B, B) + S[2 * i];
B = ROTL(B ^ A, A) + S[2 * i + 1];
A ^= B;
}
ct[0] = A;
ct[1] = B;
}
RC5函数解密需要SBOX,可以从sub_55CD39CEB250这个函数中dump出来,这个是KSA密钥拓展函数
unsigned int Sbox[26] = {
0x122F2C9C, 0xE3BCCAE7, 0xD0FFC0F2, 0xD9A12544, 0x8A27992F, 0x55B1B935, 0x9110B161, 0x92811564,
0x5CE9B359, 0x77C79A51, 0x4265527A, 0x8AB57C4B, 0x11529FA4, 0x9D9F63FF, 0xA970B936, 0xC8EABA0D,
0x9A0EB4AA, 0xB0BC6E7F, 0x9784B100, 0x70DCD3AE, 0x6057A44E, 0x89187658, 0xE00098A8, 0x45773540,
0xF9374F1A, 0x913FA548
};
密文则可以从这个函数中的v3中dump出来,这个是密文比较函数
解密魔改后的RC5函数即可 L3HCTF{5fd277be39046905ef6348ba89131922}
Comments NOTHING