analysis:
int __fastcall main(int argc, const char **argv, const char **envp)
{
char v3; // al
char tmp_byte; // [rsp+7h] [rbp-39h]
unsigned int seed[2]; // [rsp+8h] [rbp-38h] BYREF
__int64 idx; // [rsp+10h] [rbp-30h]
FILE *stream; // [rsp+18h] [rbp-28h]
size_t size; // [rsp+20h] [rbp-20h]
void *buffer; // [rsp+28h] [rbp-18h]
FILE *s; // [rsp+30h] [rbp-10h]
unsigned __int64 v12; // [rsp+38h] [rbp-8h]
v12 = __readfsqword(0x28u);
stream = fopen("flag", "rb");
fseek(stream, 0LL, 2);
size = ftell(stream);
fseek(stream, 0LL, 0);
buffer = malloc(size);
fread(buffer, size, 1uLL, stream);
fclose(stream);
seed[0] = time(0LL);
srand(seed[0]);
for ( idx = 0LL; idx < (__int64)size; ++idx )
{
*((_BYTE *)buffer + idx) ^= rand();
v3 = rand();
tmp_byte = *((_BYTE *)buffer + idx);
seed[1] = v3 & 7;
*((_BYTE *)buffer + idx) = __ROL1__(tmp_byte, v3 & 7);
}
s = fopen("flag.enc", "wb");
fwrite(seed, 1uLL, 4uLL, s);
fwrite(buffer, 1uLL, size, s);
fclose(s);
return 0;
}
我们有一个加密结果enc,可以逆向看到加密过程,目标逆向还原加密内容
要是觉得for看着乱七八糟的,看反汇编看过程更清晰:
.text:0000000000001350
.text:0000000000001350 loc_1350: ; CODE XREF: main+13F↓j
.text:0000000000001350 call _rand
.text:0000000000001355 movzx ecx, al ; one random num
.text:0000000000001358 mov rdx, [rbp+idx]
.text:000000000000135C mov rax, [rbp+buffer]
.text:0000000000001360 add rax, rdx
.text:0000000000001363 movzx eax, byte ptr [rax] ; get a byte
.text:0000000000001366 mov edx, eax
.text:0000000000001368 mov eax, ecx
.text:000000000000136A mov ecx, edx
.text:000000000000136C xor ecx, eax ; a byte xor random num
.text:000000000000136E mov rdx, [rbp+idx]
.text:0000000000001372 mov rax, [rbp+buffer]
.text:0000000000001376 add rax, rdx
.text:0000000000001379 mov edx, ecx
.text:000000000000137B mov [rax], dl ; save the res in this idx
.text:000000000000137D call _rand
.text:0000000000001382 and eax, 7 ; next random num, less than 7
.text:0000000000001385 mov ecx, eax
.text:0000000000001387 mov rdx, [rbp+idx]
.text:000000000000138B mov rax, [rbp+buffer]
.text:000000000000138F add rax, rdx
.text:0000000000001392 movzx eax, byte ptr [rax]
.text:0000000000001395 movzx eax, al ; get the same byte
.text:0000000000001398 mov rsi, [rbp+idx]
.text:000000000000139C mov rdx, [rbp+buffer]
.text:00000000000013A0 add rdx, rsi
.text:00000000000013A3 mov [rbp+tmp_byte], al
.text:00000000000013A6 mov [rbp+random_num2], ecx
.text:00000000000013A9 movzx eax, [rbp+tmp_byte]
.text:00000000000013AD mov esi, eax
.text:00000000000013AF mov eax, [rbp+random_num2]
.text:00000000000013B2 mov ecx, eax
.text:00000000000013B4 rol sil, cl ; 循环左移随机数位
.text:00000000000013B7 mov eax, esi
.text:00000000000013B9 mov [rdx], al ; 保存起来
.text:00000000000013BB add [rbp+idx], 1
.text:00000000000013C0
这里就是基础的算法反着写就行,正常流程:
- 取1字节,和随机数1异或
- 随机数2&7(取一个小于7的值,反正超过8就会变原状,所以有效结果就是对7取余)
- 循环左移
反过来即可拿到flag
exp:
#include <stdio.h>
unsigned char hexData[32] = {
0x5A, 0x35, 0xB1, 0x62, 0x00, 0xF5, 0x3E, 0x12, 0xC0, 0xBD, 0x8D, 0x16, 0xF0, 0xFD, 0x75, 0x99,
0xFA, 0xEF, 0x39, 0x9A, 0x4B, 0x96, 0x21, 0xA1, 0x43, 0x16, 0x23, 0x71, 0x65, 0xFB, 0x27, 0x4B
};
unsigned char ror(unsigned char val, int shift) {
return (val >> shift) | (val << (8 - shift));
}
int main() {
unsigned int seed = *(unsigned int*)hexData;
unsigned char *enc = &hexData[4];
unsigned char dec[28+1]={0};
printf("Seed: %08x\n", seed);
srand(seed);
for(int i = 0; i < 28; i++) {
unsigned char random1 = rand() & 0xff;
unsigned char random2 = rand() & 7;
dec[i] = ror(enc[i], random2);
dec[i] ^= random1;
printf("%02x: %02x %02x - %02x\n",enc[i], random1, random2,dec[i]);
}
printf("%s\n", dec);
}
需要在linux下编译,Windows和Linux的rand运行结果是不一样的,程序用的Linux我们也得用Linux
summary:
- 小细节:
rand()
在相同种子情况下,不同平台下运行结果不同