RE_NaCl

本题只看反编译很难看懂, 需要汇编 + 调试得到题目的大致逻辑

按照逻辑逐步调试

第一轮加密的data[]生成

首先是生成第一轮加密所需的data数组(元素类型为DWORD),

Untitled

跳转到生成data数组的代码块

Untitled

data生成完成后会有一个输入字符串分组逆序处理

后续是第一部分的加密区域

第一部分加密

Untitled

循环左移的次数都是固定的, 其算法逻辑为

输入: abcdefgh

分大组: abcdefgh  八个一组

分小组: abcd    efgh

小组逆序:   dcba(倒序一组)    hgfe(倒序二组)

for i in range(0x2b):注意左移是循环左移
结果1 = (倒序一组 << 1) & (倒序一组 << 8) ^ (倒序一组 << 2) ^ 倒序二组(无左移) ^ (data + 4 * i)    

结果2 = (结果1 << 1)     & (结果1 << 8)      ^ (结果1 << 2)     ^ 倒序一组(无左移) ^ (data + 4 * i)

结果3 = (结果2 << 1)     & (结果2 << 8)      ^ (结果2 << 2)     ^ 结果1 ^ (data + 4 * i)

结果4 = (结果3 << 1)     & (结果3 << 8)      ^ (结果3 << 2)     ^ 结果2 ^ (data + 4 * i)

以此类推
.........
进行0x2b次

其中的data就是前面生成的

Untitled

随后来到第二部分的加密

第二部分加密

其实就是XTEA加密, 但是当时只照着XXTEA的算法看了一下发现不是就没管了.(主要想着有TEA特征delta值, 但是这道题把delta给改了)

下面是一些分析 c

SFI:0000000008080180 ; START OF FUNCTION CHUNK FOR sub_8080900
SFI:0000000008080180
SFI:0000000008080180 loc_8080180:            ; 加密结果高32位
SFI:0000000008080180 mov     eax, [r15-0Ch]  ; 上次加密的结果B562C653
SFI:0000000008080184 shl     eax, 4          ; 高密 << 4, 注意这里不是逻辑左移了, 溢出32直接没了
SFI:0000000008080184                         ; 密文 << 4
SFI:0000000008080187 mov     edx, eax
SFI:0000000008080189 mov     eax, [r15-0Ch]
SFI:000000000808018D shr     eax, 5          ; 高密 >> 5
SFI:000000000808018D                         ; 密文 >> 5
SFI:0000000008080190 xor     edx, eax        ; (高密 << 4) ^ (高密 >> 5)
SFI:0000000008080190                         ; (密文 << 4) ^ (密文 >> 5)
SFI:0000000008080192 mov     eax, [r15-0Ch]
SFI:0000000008080196 lea     ecx, [rdx+rax]  ; 高密 + ((高密 << 4) ^ (高密 >> 5))
SFI:0000000008080196                         ; 密文 + ((密文 << 4) ^ (密文 >> 5))
SFI:0000000008080199 mov     eax, [r15-10h]  ; 0
SFI:0000000008080199                         ; 10325476
SFI:000000000808019D and     eax, 3          ; 0
SFI:000000000808019D                         ; eax = 2
SFI:00000000080801A0 lea     rdx, ds:0[rax*4]
SFI:00000000080801A8 mov     rax, [r15-18h]  ; 这是一个内存存有000102030405060708090A....注意小端排序
SFI:00000000080801AC add     rax, rdx
SFI:00000000080801AF mov     eax, eax
SFI:00000000080801B1 mov     edx, [r13+rax+0] ; rdx = 03020100
SFI:00000000080801B1                         ; rdx = B0A09080
SFI:00000000080801B6 mov     eax, [r15-10h]  ; 0
SFI:00000000080801B6                         ; 10325476
SFI:00000000080801BA add     eax, edx        ; 03020100
SFI:00000000080801BA                         ; 1B3C5D7E
SFI:00000000080801BC xor     eax, ecx        ; 03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5)))
SFI:00000000080801BC                         ; 1B3C5D7E ^ (密文 + ((密文 << 4) ^ (密文 >> 5)))
SFI:00000000080801BE xchg    ax, ax
SFI:00000000080801C0 add     [r15-8], eax    ; 低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))
SFI:00000000080801C0                         ; (低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) + (1B3C5D7E ^ (密文 + ((密文 << 4) ^ (密文 >> 5))))
SFI:00000000080801C4 mov     eax, [r15-1Ch]  ; 01234567,  小端排序rax = 10325476
SFI:00000000080801C4                         ; 同上rax = 10325476
SFI:00000000080801C8 add     [r15-10h], eax  ;
SFI:00000000080801C8                         ; [r15 - 10h] = 2 * 10325476 = 2064A8EC
SFI:00000000080801CC mov     eax, [r15-8]
SFI:00000000080801D0 shl     eax, 4          ; (低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) << 4
SFI:00000000080801D0                         ; ((低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) + (1B3C5D7E ^ (密文 + ((密文 << 4) ^ (密文 >> 5))))) << 4
SFI:00000000080801D3 mov     edx, eax
SFI:00000000080801D5 mov     eax, [r15-8]
SFI:00000000080801D9 shr     eax, 5          ; (低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) >> 5
SFI:00000000080801D9                         ; ((低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) + (1B3C5D7E ^ (密文 + ((密文 << 4) ^ (密文 >> 5))))) >> 5
SFI:00000000080801DC xor     edx, eax        ; ((低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) >> 5) ^ ((低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) << 4)
SFI:00000000080801DC                         ; (((低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) + (1B3C5D7E ^ (密文 + ((密文 << 4) ^ (密文 >> 5))))) >> 5) ^ (((低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) + (1B3C5D7E ^ (密文 + ((密文 << 4) ^ (密文 >> 5))))) << 4)
SFI:00000000080801DE xchg    ax, ax
SFI:00000000080801E0 mov     eax, [r15-8]    ; 低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))
SFI:00000000080801E0                         ; (低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) + (1B3C5D7E ^ (密文 + ((密文 << 4) ^ (密文 >> 5))))
SFI:00000000080801E4 lea     ecx, [rdx+rax]  ; (低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) +(((低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) >> 5) ^ ((低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) << 4))
SFI:00000000080801E4                         ; ((((低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) + (1B3C5D7E ^ (密文 + ((密文 << 4) ^ (密文 >> 5))))) >> 5) ^ (((低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) + (1B3C5D7E ^ (密文 + ((密文 << 4) ^ (密文 >> 5))))) << 4)) + ((低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) + (1B3C5D7E ^ (密文 + ((密文 << 4) ^ (密文 >> 5)))))
SFI:00000000080801E7 mov     eax, [r15-10h]  ; 10325476
SFI:00000000080801E7                         ; 2064A8EC
SFI:00000000080801EB shr     eax, 0Bh        ; 2064A
SFI:00000000080801EB                         ; 40C95
SFI:00000000080801EE mov     eax, eax
SFI:00000000080801F0 and     eax, 3          ; EAX = 2
SFI:00000000080801F0                         ; EAX = 1
SFI:00000000080801F3 lea     rdx, ds:0[rax*4] ; RDX = 8
SFI:00000000080801F3                         ; RDX = 4
SFI:00000000080801FB mov     rax, [r15-18h]
SFI:00000000080801FF nop
SFI:0000000008080200 add     rax, rdx        ; 数组地址 + 8
SFI:0000000008080200                         ; 数组地址 + 4
SFI:0000000008080203 mov     eax, eax
SFI:0000000008080205 mov     edx, [r13+rax+0] ; 0B0A0908
SFI:0000000008080205                         ; 07060504
SFI:000000000808020A mov     eax, [r15-10h]  ; 10325476
SFI:000000000808020A                         ; 2064A8EC
SFI:000000000808020E add     eax, edx        ; 10325476 + 0B0A0908
SFI:000000000808020E                         ; 2064A8EC + 07060504
SFI:0000000008080210 xor     eax, ecx        ; ((低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) +(((低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) >> 5) ^ ((低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) << 4))) ^ (10325476 + 0B0A0908)
SFI:0000000008080210                         ; (((((低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) + (1B3C5D7E ^ (密文 + ((密文 << 4) ^ (密文 >> 5))))) >> 5) ^ (((低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) + (1B3C5D7E ^ (密文 + ((密文 << 4) ^ (密文 >> 5))))) << 4)) + ((低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) + (1B3C5D7E ^ (密文 + ((密文 << 4) ^ (密文 >> 5)))))) ^ (2064A8EC + 07060504)
SFI:0000000008080212 add     [r15-0Ch], eax  ; 高密 + (((低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) +(((低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) >> 5) ^ ((低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) << 4))) ^ (10325476 + 0B0A0908))
SFI:0000000008080212                         ; (高密 + (((低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) +(((低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) >> 5) ^ ((低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) << 4))) ^ (10325476 + 0B0A0908))) + ((((((低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) + (1B3C5D7E ^ (密文 + ((密文 << 4) ^ (密文 >> 5))))) >> 5) ^ (((低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) + (1B3C5D7E ^ (密文 + ((密文 << 4) ^ (密文 >> 5))))) << 4)) + ((低密 + (03020100 ^ (高密 + ((高密 << 4) ^ (高密 >> 5))))) + (1B3C5D7E ^ (密文 + ((密文 << 4) ^ (密文 >> 5)))))) ^ (2064A8EC + 07060504))
SFI:0000000008080216 inc     dword ptr [r15-4] ; i++
SFI:000000000808021A nop     word ptr [rax+rax+00h]

需要抽取其中最关键的特征

  • (高密 << 4)
  • (高密 >> 5)
  • and eax, 3
  • mov eax, [r15-10h] 和 add eax, edx, 传给eax的是10325476, 就是delta

综上是可以判断其为XTEA算法, 魔改可以通过调试验证. 这道题是没有魔改过的

最后是关键判断

关键判断

Untitled

查看一下

Untitled

通过调试得到了大体的逻辑, 接下来复现算法

#if 1
#define _CRT_SECURE_NO_WARNINGS
#include <stdint.h>
#include <stdio.h>
#include <math.h>

uint32_t rol(uint32_t num, uint32_t n);
void encode(uint32_t n1, uint32_t n2, uint32_t *mid);
void encipher(unsigned int num_rounds, uint32_t* high, uint32_t* low);

int main(void)
{
	uint32_t input[8] = { 0x61626364, 0x65666768, 0x61626364, 0x65666768,
							0x61626364, 0x65666768, 0x61626364, 0x65666768 };

	uint32_t midput[8];

	uint32_t i;

	for (i = 0; i < 8; i += 2) {
		encode(input[i], input[i + 1], midput + i);
		//printf("第一部分加密:组%d:0x%x%x\n", i, tmp[i / 2] & 0xffff0000, tmp[i /2] & 0xffff);
	}

	for (i = 0; i < 4; i++) {
		encipher(pow(2, i + 1), midput + (i * 2), midput + ((i * 2) + 1));
	
		printf("0x%08x%08x\n", midput[i * 2], midput[(i * 2) + 1]);
	}
}

uint32_t rol(uint32_t num, uint32_t n)
{
	//uint32_t tmp = (num >> (32 - n));
	//return ((num << n) & tmp);
	return ((num << n) | ((num >> (32 - n))));
}

//可逆加密
void encode(uint32_t n1, uint32_t n2, uint32_t *mid)
{
	uint32_t data[] = { 0x04050607, 0x00010203, 0x0C0D0E0F, 0x08090A0B, 
			0xCD3FE81B, 0xD7C45477, 0x9F3E9236, 0x0107F187, 0xF993CB81, 
			0xBF74166C, 0xDA198427, 0x1A05ABFF, 0x9307E5E4, 0xCB8B0E45, 
			0x306DF7F5, 0xAD300197, 0xAA86B056, 0x449263BA, 0x3FA4401B, 
			0x1E41F917, 0xC6CB1E7D, 0x18EB0D7A, 0xD4EC4800, 0xB486F92B, 
			0x8737F9F3, 0x765E3D25, 0xDB3D3537, 0xEE44552B, 0x11D0C94C, 
			0x9B605BCB, 0x903B98B3, 0x24C2EEA3, 0x896E10A2, 0x2247F0C0, 
			0xB84E5CAA, 0x8D2C04F0, 0x3BC7842C, 0x1A50D606, 0x49A1917C, 
			0x7E1CB50C, 0xFC27B826, 0x5FDDDFBC, 0xDE0FC404, 0xB2B30907 };

	uint64_t result, i;

	uint32_t xordata[2] = { n2, n1 };//初始化运算中轮换的异或数据

	for (i = 0; i <= 0x2b; i++) {
		result = ((rol(xordata[1], 1) & rol(xordata[1], 8)) ^ rol(xordata[1], 2)) ^ xordata[0] ^ data[i];
		xordata[0] = xordata[1];
		xordata[1] = result;
	}
	printf("0x%x%x\n", xordata[1], xordata[0]);

	*mid = xordata[0];//这里是顺序变了
	*(mid + 1) = xordata[1];
}

void encipher(unsigned int num_rounds, uint32_t *high, uint32_t *low) {
	unsigned int i;

	uint32_t key[4] = {0x03020100, 0x07060504, 0x0B0A0908, 0x0F0E0D0C};

	uint32_t v0 = *high, v1 = *low;
	uint32_t sum = 0, delta = 0x10325476;

	for (i = 0; i < num_rounds; i++) {
		v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
		sum += delta;
		v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
	}

	*high = v0; *low = v1;
}

接下来是解密算法

解密算法

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdint.h>
#include <math.h>

uint32_t rol(uint32_t num, uint32_t n);
void decipther(unsigned int num_runds, uint32_t* high, uint32_t* low);
void decode(uint32_t* mid);
void re_chr(uint32_t num);

int main(void)
{
    uint32_t en_data[8] = {0xFDF5C266, 0x7A328286, 0xCE944004, 0x5DE08ADC, 0xA6E4BD0A, 0x16CAADDC, 0x13CD6F0C, 0x1A75D936};
    unsigned int i;

    for (i = 0; i < 4; i++) {
        decipther(pow(2, i + 1), en_data + (2 * i), en_data + ((2 * i) + 1));
    }
    
    for (i = 0; i < 8; i+=2) {
        decode(en_data + i);
        printf("0x%x%x\n", en_data[i], en_data[i + 1]);
    }

    for (i = 0; i < 8; i++) {
        re_chr(en_data[i]);
    }

    return 0;
}

uint32_t rol(uint32_t num, uint32_t n)
{
    return ((num << n) | ((num >> (32 - n))));
}

void decipther(unsigned int num_rounds, uint32_t* high, uint32_t* low) 
{
    unsigned int i;

    uint32_t key[4] = { 0x03020100, 0x07060504, 0x0B0A0908, 0x0F0E0D0C };

    uint32_t v0 = *high, v1 = *low, delta = 0x10325476, sum = delta * num_rounds;
    for (i = 0; i < num_rounds; i++) {
        v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
        sum -= delta;
        v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
    }
    *high = v0; *low = v1;
}

void decode(uint32_t* mid)
{
    uint32_t result, i;

    uint32_t data[] = { 0x04050607, 0x00010203, 0x0C0D0E0F, 0x08090A0B,
            0xCD3FE81B, 0xD7C45477, 0x9F3E9236, 0x0107F187, 0xF993CB81,
            0xBF74166C, 0xDA198427, 0x1A05ABFF, 0x9307E5E4, 0xCB8B0E45,
            0x306DF7F5, 0xAD300197, 0xAA86B056, 0x449263BA, 0x3FA4401B,
            0x1E41F917, 0xC6CB1E7D, 0x18EB0D7A, 0xD4EC4800, 0xB486F92B,
            0x8737F9F3, 0x765E3D25, 0xDB3D3537, 0xEE44552B, 0x11D0C94C,
            0x9B605BCB, 0x903B98B3, 0x24C2EEA3, 0x896E10A2, 0x2247F0C0,
            0xB84E5CAA, 0x8D2C04F0, 0x3BC7842C, 0x1A50D606, 0x49A1917C,
            0x7E1CB50C, 0xFC27B826, 0x5FDDDFBC, 0xDE0FC404, 0xB2B30907 };

    uint32_t xordata[2] = {*mid, *(mid + 1)};

    for (i = 0; i <= 0x2b; i++) {
        result = ((rol(xordata[0], 1) & rol(xordata[0], 8)) ^ rol(xordata[0], 2)) ^ xordata[1] ^ data[0x2b - i];
        xordata[1] = xordata[0];
        xordata[0] = result;
    }

    mid[0] = xordata[1];
    mid[1] = xordata[0];
}

void re_chr(uint32_t num)
{
    for (int i = 24; i >= 0; i -= 8)
        printf("%c", (num & (0xff << i)) >> i);
}

input

得到了inputmM7pJIobsCTQPO6R0g-L8kFExhYuivBN

flag

CTF{mM7pJIobsCTQPO6R0g-L8kFExhYuivBN}

RE_SimpleFileSystem(可逆加密算法, 文件系统)

txt文档初始化后可以看到有很多的inode(中文为索引节点, 用于存放档案及目录的基本信息, 包含时间, 档名, 使用者及其群组, 只不过这道题简化成了编号)

使用IDA分析

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  int v4; // eax
  int *v5; // rax
  char *v6; // rax
  unsigned int v7; // eax
  unsigned int v8; // eax
  int i; // [rsp+18h] [rbp-1038h]
  int j; // [rsp+1Ch] [rbp-1034h]
  int v11; // [rsp+20h] [rbp-1030h]
  int v12; // [rsp+24h] [rbp-102Ch]
  unsigned int v13; // [rsp+28h] [rbp-1028h]
  unsigned int v14; // [rsp+28h] [rbp-1028h]
  int v15; // [rsp+28h] [rbp-1028h]
  unsigned int v16; // [rsp+28h] [rbp-1028h]
  unsigned int v17; // [rsp+28h] [rbp-1028h]
  unsigned int v18; // [rsp+28h] [rbp-1028h]
  unsigned int v19; // [rsp+28h] [rbp-1028h]
  unsigned int v20; // [rsp+28h] [rbp-1028h]
  int r_num1; // [rsp+2Ch] [rbp-1024h]
  int r_num2; // [rsp+30h] [rbp-1020h]
  time_t timer; // [rsp+38h] [rbp-1018h] BYREF
  char s[16]; // [rsp+40h] [rbp-1010h] BYREF
  char s1[1024]; // [rsp+440h] [rbp-C10h] BYREF
  char nptr[1024]; // [rsp+840h] [rbp-810h] BYREF
  char v27[1032]; // [rsp+C40h] [rbp-410h] BYREF
  unsigned __int64 v28; // [rsp+1048h] [rbp-8h]

  v28 = __readfsqword(0x28u);
  if ( a1 != 3 )                                // 如果参数不是三个则提示输入的格式
  {
    printf("use: %s <diskfile> <nblocks>\n", *a2);
    return 1LL;
  }
  v4 = atoi(a2[2]);

  if ( !(unsigned int)sub_55A3FD5D9BEC(a2[1], v4) )// 打开文件image.flag
  {
    v5 = __errno_location();
    v6 = strerror(*v5);
    printf("couldn't initialize %s: %s\n", a2[1], v6);
    return 1LL;
  }

  v7 = sub_55A3FD5D9C9E();
  printf("opened emulated disk image %s with %d blocks\n", a2[1], v7);
  while ( 1 )                                   // shell的while循环
  {
    printf(" simplefs> ");                      // 打印输入提示符
    fflush(stdout);
    if ( !fgets(s, 1024, stdin) )               // 输入指令
      break;
    if ( s[0] != '\n' )                         // 如果有输入内容
    {
      s[strlen(s) - 1] = 0;                     // 把结尾的'\n'改为'\0'
      v11 = __isoc99_sscanf(s, "%s %s %s", s1, nptr, v27);// 输入三个参数(也可以不输入这么多, 这个只是最大值), 从0号开始检验
      if ( v11 )                                // 其中v11是成功录入的变量数量, 根据这个判断输入是否有效
      {
        if ( !strcmp(s1, "format") )
        {
          if ( v11 == 1 )
          {
            if ( (unsigned int)sub_55A3FD5D8357() )// 这里应该是为该系统分配磁盘, 在这里初始化了
              puts("disk formatted.");          // 磁盘格式化
            else
              puts("format failed!");
          }
          else
          {
            puts("use: format");
          }
        }
        else if ( !strcmp(s1, "mount") )        // 挂载: 是指由操作系统使一个存储设备(诸如硬盘、CD-ROM或共享资源)上的计算机文件和目录可供用户通过计算机的文件系统访问的一个过程
        {
          if ( v11 == 1 )
          {
            if ( (unsigned int)sub_55A3FD5D87C2() )// 这里设置了inode_index为1
              puts("disk mounted.");
            else
              puts("mount failed!");
          }
          else
          {
            puts("use: mount");
          }
        }
        else if ( !strcmp(s1, "debug") )
        {
          if ( v11 == 1 )
            sub_55A3FD5D84BB();
          else
            puts("use: debug");
        }
        else if ( !strcmp(s1, "getsize") )
        {
          if ( v11 == 2 )
          {
            v13 = atoi(nptr);
            v12 = sub_55A3FD5D9014(v13);
            if ( v12 < 0 )
              puts("getsize failed!");
            else
              printf("inode %d has size %d\n", v13, (unsigned int)v12);
          }
          else
          {
            puts("use: getsize <inumber>");
          }
        }
        else if ( !strcmp(s1, "create") )
        {
          if ( v11 == 1 )
            sub_55A3FD5D816A();
          else
            puts("use: create");
        }
        else if ( !strcmp(s1, "delete") )
        {
          if ( v11 == 2 )
          {
            v14 = atoi(nptr);
            if ( (unsigned int)sub_55A3FD5D8DD1(v14) )
              printf("inode %d deleted.\n", v14);
            else
              puts("delete failed!");
          }
          else
          {
            puts("use: delete <inumber>");
          }
        }
        else if ( !strcmp(s1, "cat") )
        {
          if ( v11 == 2 )
          {
            v15 = atoi(nptr);
            if ( !(unsigned int)sub_55A3FD5D8017(v15, "/dev/stdout") )
              puts("cat failed!");
          }
          else
          {
            puts("use: cat <inumber>");
          }
        }
        else if ( !strcmp(s1, "captureflag") )  // 这个应该是个无用指令
        {
          puts("Are you kidding?");
        }
        else if ( !strcmp(s1, "plantflag") )    // 可疑代码
        {
          v8 = time(&timer);                    // 记录现在的时间戳到v8和timer中
          srand(v8);                            // 随机数播种
          r_num1 = rand() % 100;                // 100以内的随机数
          r_num2 = rand() % 100;                // 同上
          for ( i = 0; i < r_num1; ++i )        // 循环随机的次数
          {
            v16 = sub_55A3FD5D816A();
            if ( (unsigned int)sub_55A3FD5D7E16("flag", v16, 2) )
              printf("copied file %s to inode %d\n", nptr, v16);
            else
              puts("copy failed!");
          }

          v17 = sub_55A3FD5D816A();
          if ( (unsigned int)sub_55A3FD5D7E16("flag", v17, 1) )
            printf("plant flag to inode %d!\n", v17);
          else
            puts("copy failed!");

          for ( j = 0; j < r_num2; ++j )
          {
            v18 = sub_55A3FD5D816A();
            if ( (unsigned int)sub_55A3FD5D7E16("flag", v18, 2) )
              printf("copied file %s to inode %d\n", nptr, v18);
            else
              puts("copy failed!");
          }
        }
        else if ( !strcmp(s1, "copyin") )
        {
          if ( v11 == 3 )
          {
            v19 = atoi(v27);
            if ( !(unsigned int)sub_55A3FD5D7E16(nptr, v19, 0) )
              goto LABEL_69;
            printf("copied file %s to inode %d\n", nptr, v19);
          }
          else
          {
            puts("use: copyin <filename> <inumber>");
          }
        }
        else if ( !strcmp(s1, "copyout") )
        {
          if ( v11 == 3 )
          {
            v20 = atoi(nptr);
            if ( (unsigned int)sub_55A3FD5D8017(v20, v27) )
              printf("copied inode %d to file %s\n", v20, v27);
            else
LABEL_69:
              puts("copy failed!");
          }
          else
          {
            puts("use: copyout <inumber> <filename>");
          }
        }
        else if ( !strcmp(s1, "help") )
        {
          puts("Commands are:");
          puts("    format");
          puts("    mount");
          puts("    debug");
          puts("    create");
          puts("    delete  <inode>");
          puts("    cat     <inode>");
          puts("    captureflag");
          puts("    plantflag");
          puts("    copyin  <file> <inode>");
          puts("    copyout <inode> <file>");
          puts("    help");
          puts("    quit");
          puts("    exit");
        }
        else
        {
          if ( !strcmp(s1, "quit") || !strcmp(s1, "exit") )
            break;
          printf("unknown command: %s\n", s1);
          puts("type 'help' for a list of commands.");
        }
      }
    }
  }
  puts("closing emulated disk.");
  sub_55A3FD5D9E6B();
  return 0LL;
}

与shell类似, 确定大致的分析方向, 知道哪个指令是比较关键

可以看到前两个指令都是初始化, 后面的debug, create, delete, cat, copyin, copyout, help, quit, exit都是关于文件操作的指令, 不重要. 所以排除法得到了两个关键的指令captureflag 和 plantflag. 而通过分析法相captureflag是一个无用指令, 所以可以锁定关键的指令就是plantflag.

同时通过调试得到所有索引上的数据都是通过flag文本文件经过加密后随机生成的

else if ( !strcmp(s1, "plantflag") )    // 可疑代码
        {
          v8 = time(&timer);                    // 记录现在的时间戳到v8和timer中
          srand(v8);                            // 随机数播种
          r_num1 = rand() % 100;                // 100以内的随机数
          r_num2 = rand() % 100;                // 同上
          for ( i = 0; i < r_num1; ++i )        // 循环随机的次数
          {
            v16 = sub_55A3FD5D816A();
            if ( (unsigned int)sub_55A3FD5D7E16("flag", v16, 2) )
              printf("copied file %s to inode %d\n", nptr, v16);
            else
              puts("copy failed!");
          }

          v17 = sub_55A3FD5D816A();
          if ( (unsigned int)sub_55A3FD5D7E16("flag", v17, 1) )
            printf("plant flag to inode %d!\n", v17);
          else
            puts("copy failed!");

          for ( j = 0; j < r_num2; ++j )
          {
            v18 = sub_55A3FD5D816A();
            if ( (unsigned int)sub_55A3FD5D7E16("flag", v18, 2) )
              printf("copied file %s to inode %d\n", nptr, v18);
            else
              puts("copy failed!");
          }
        }

查看代码内部可以发现两个可疑函数sub_55A3FD5D816A()sub_55A3FD5D7E16().

经过调试后在sub_55A3FD5D7E16()​中发现一个加密函数sub_55A3FD5D81B2((__int64)str, len);

Untitled

跟进查看

__int64 __fastcall sub_55A3FD5D81B2(__int64 a1, int a2)
{
  int i; // [rsp+10h] [rbp-10h]
  int xordata; // [rsp+14h] [rbp-Ch]
  _BYTE *str2; // [rsp+18h] [rbp-8h]

  xordata = sub_55A3FD5D90A3();
  for ( i = 0; i < a2; ++i )
  {
    str2 = (_BYTE *)(i + a1);
    *str2 = (*str2 >> 1) | (*str2 << 7);
    *str2 ^= xordata;
    *str2 = ((unsigned __int8)*str2 >> 2) | (*str2 << 6);
    *str2 ^= BYTE1(xordata);
    *str2 = ((unsigned __int8)*str2 >> 3) | (32 * *str2);
    *str2 ^= BYTE2(xordata);
    *str2 = ((unsigned __int8)*str2 >> 4) | (16 * *str2);
    *str2 ^= HIBYTE(xordata);
    *str2 = (*str2 >> 5) | (8 * *str2);         // str2 << 3
  }
  return 0LL;
}

在这个函数下下断点

加密的过程

Untitled

经过加密后的数据

Untitled

已知flag的前四个字符就是*CTF, 可以通过这四个字符加密过后的数据在image文件中查找并得到加密后的flag

在HexEditor中查找0x00, 0xd2, 0xfc, 0xd8

Untitled

dump数据并解密

#if 1
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdint.h>
#include <string.h>

void decode(uint8_t* output, unsigned int n);

int main(void)
{
	uint8_t output[] = { 0x00, 0xd2, 0xfc, 0xd8, 0xa2, 0xda, 0xba, 0x9e, 0x9c, 0x26,
						 0xf8, 0xf6, 0xb4, 0xce, 0x3c, 0xcc,0x96, 0x88, 0x98, 0x34,
						 0x82, 0xde, 0x80, 0x36, 0x8a, 0xd8, 0xc0, 0xf0, 0x38, 0xae,
						 0x40 };
	decode(output, sizeof(output));

	return 0;
}

void decode(uint8_t* output, unsigned int n)
{
	uint32_t xordata[4] = { 0xDEEDBEEF, 0xDEEDBE,  0xDEED, 0xDE};
	uint8_t tmp;
	
	for (int i = 0; i < n; i++) {
		tmp = *(output + i);
		tmp = (tmp << 5) | (tmp >> 3);
		tmp ^= xordata[3];
		tmp = (tmp << 4) | (tmp >> 4);
		tmp ^= xordata[2];
		tmp = (tmp << 3) | (tmp >> 5);
		tmp ^= xordata[1];
		tmp = (tmp << 2) | (tmp >> 6);
		tmp ^= xordata[0];
		tmp = (tmp << 1) | (tmp >> 7);
		printf("%c", tmp);
	}
	printf("\n");
}
#endif

即可得到flag

flag

CTF{Gwed9VQpM4Lanf0kEj1oFJR6}

RE_Jump(setjump和longjump打乱逻辑顺序, Burrows-Wheeler压缩算法)

关于setjump和longjump更加详细的介绍可以看CSAPP第八章的最靠后的部分, 里面有讲到setjump和longjump

进入IDA分析可以发现是去符号的, 但是还是可以分析.

通过字符串查找找到了核心函数

__int64 __fastcall sub_40293E(__int64 a1, __int64 a2)
{
  int v2; // edx
  int v3; // ecx
  int v4; // r8d
  int v5; // r9d
  int v7; // edx
  int v8; // ecx
  int v9; // r8d
  int v10; // r9d
  __int64 v11; // rdx
  int v12; // edx
  int v13; // ecx
  int v14; // r8d
  int v15; // r9d
  int v16; // edx
  int v17; // ecx
  int v18; // r8d
  int v19; // r9d
  __int64 v20; // rdx
  int v21; // ecx
  int v22; // r8d
  int v23; // r9d
  __int64 v24; // rdx
  int v25; // edx
  int v26; // ecx
  int v27; // r8d
  int v28; // r9d
  int v29; // edx
  int v30; // ecx
  int v31; // r8d
  int v32; // r9d
  char v33; // [rsp+0h] [rbp-10h]
  char v34; // [rsp+0h] [rbp-10h]
  char v35; // [rsp+0h] [rbp-10h]
  char v36; // [rsp+0h] [rbp-10h]
  char v37; // [rsp+0h] [rbp-10h]
  int v38; // [rsp+Ch] [rbp-4h]
  int v39; // [rsp+Ch] [rbp-4h]
  int v40; // [rsp+Ch] [rbp-4h]
  int v41; // [rsp+Ch] [rbp-4h]

  sub_419570(&input);                           // scanf
  v38 = setjmp(&status, a2, v2, v3, v4, v5, v33);
  if ( !v38 )
    sub_402689();                               // (0)
  if ( v38 == 1 )
    return 1LL;
  input23_addr = mb_malloc(len + 1, 1uLL);
  if ( !setjmp(&status, 1LL, v7, v8, v9, v10, v34) )// (1) 运行到了这里随后向下继续运行到了下一个setjmp
    longjmp(&off_4C9460);
  qword_4C9408 = sub_427140(8LL * len, 1LL, v11);

  v39 = setjmp(&status, 1LL, v12, v13, v14, v15, v35);// (2) 设下坐标setjmp, 初始化了1
                                                // (4) 在为input_change申请完内存空间后又回到了这里
  if ( !v39 )
    longjmp(&off_4C9460);
  if ( v39 <= len )
    longjmp(&off_4C9460);
  sub_401F62(qword_4C9408, 1uLL);
  v40 = setjmp(&status, 1LL, v16, v17, v18, v19, v36);
  if ( !v40 )
    sub_402826(&status, 1LL, v20, v21, v22, v23);// ******
  if ( v40 <= len )
    longjmp(&off_4C9540);
  sub_427730(qword_4C9408, 1LL, v20);
  sub_427730(input23_addr, 1LL, v24);           // 真正的变换在这里
  v41 = setjmp(&status, 1LL, v25, v26, v27, v28, v37);
  if ( !v41 )
    longjmp(&off_4C9540);
  if ( v41 == 1 )
    sub_411920("*CTF{%s}\n", &input, v29, v30, v31, v32);// 其实看到这里的时候就不应该把关注的重点放在input上面了, 如果变换该结果就不是输入明文.
  return 0LL;
}

setjmp会将当前的上下文保存在status中, 而longjmp会跳回最近一次的setjmp并将其参数作为setjmp的返回值.

所以看起来这个函数中只有if语句, 其实是有很多的for语句的.

还需要注意的是longjmp不能F8步过, 相当于把直接跳过了for循环的所有语句了, 只能F7步入然后跳转到相应的地方才能进行分析

最后得到了其加密算法是Burrows-Wheeler, 是单纯的换序算法.

s = '\03jmGn_=uaSZLvN4wFxE6R+p\02D2qV1CBTck'
lst = [[0]] * 34
#定义最后一列
for i in range(34):
    lst[i] = s[i]

tmp = [[0]] * 34

for i in range(34):
    tmp[i] = s[i]

for k in range(34):
    lst.sort()
    for i in range(34):
        lst[i] = tmp[i] + lst[i]
#最后按行打印
print(lst)