Try to analyze the Verrou binary to bypass its encryption and recover the secure message from given image!
Decompile in IDA:
__int64 __fastcall sub_2A12(__int64 a1, __int64 a2, int width, int height, int const_1, __int64 in_img)
{
unsigned __int64 v6; // rax
void *v7; // rsp
__int64 v8; // rax
_BYTE v10[8]; // [rsp+8h] [rbp-130h] BYREF
__int64 v11; // [rsp+10h] [rbp-128h]
int v12; // [rsp+1Ch] [rbp-11Ch]
int height_1; // [rsp+20h] [rbp-118h]
int width_1; // [rsp+24h] [rbp-114h]
__int64 v15; // [rsp+28h] [rbp-110h]
__int64 v16; // [rsp+30h] [rbp-108h]
int v17; // [rsp+38h] [rbp-100h]
int i; // [rsp+3Ch] [rbp-FCh]
int j; // [rsp+40h] [rbp-F8h]
int v20; // [rsp+44h] [rbp-F4h]
int k; // [rsp+48h] [rbp-F0h]
int m; // [rsp+4Ch] [rbp-ECh]
int n; // [rsp+50h] [rbp-E8h]
int ii; // [rsp+54h] [rbp-E4h]
int v25; // [rsp+58h] [rbp-E0h]
int v26; // [rsp+5Ch] [rbp-DCh]
int v27; // [rsp+60h] [rbp-D8h]
int v28; // [rsp+64h] [rbp-D4h]
__int64 v29; // [rsp+68h] [rbp-D0h]
_BYTE *v30; // [rsp+70h] [rbp-C8h]
double v31[4]; // [rsp+78h] [rbp-C0h] BYREF
_BYTE v32[104]; // [rsp+98h] [rbp-A0h] BYREF
unsigned __int64 v33; // [rsp+100h] [rbp-38h]
v16 = a1;
v15 = a2;
width_1 = width;
height_1 = height;
v12 = const_1;
v11 = in_img;
v33 = __readfsqword(0x28u);
sub_361E(v31, 0.0, 0.0, 0.0, 0.0);
cv::Mat::Mat(v32, (unsigned int)height_1, (unsigned int)width_1, 16, v31);
v25 = width_1 / v12 * (height_1 / v12);
v29 = 8 * v25 - 1LL;
v6 = 16 * ((8 * v25 + 15LL) / 0x10uLL);
while ( v10 != &v10[-(v6 & 0xFFFFFFFFFFFFF000LL)] )
;
v7 = alloca(v6 & 0xFFF);
if ( (v6 & 0xFFF) != 0 )
*(_QWORD *)&v10[(v6 & 0xFFF) - 8] = *(_QWORD *)&v10[(v6 & 0xFFF) - 8];
v30 = v10;
v17 = 0;
for ( i = 0; i < v25; ++i )
{
for ( j = 0; j <= 7; ++j )
v30[v17++] = (*(char *)(i + v11) >> j) & 1;
}
v20 = 0;
for ( k = 0; k < height_1 / v12; ++k )
{
for ( m = 0; m < width_1 / v12; ++m )
{
for ( n = 0; n < v12; ++n )
{
for ( ii = 0; ii < v12; ++ii )
{
v26 = v12 * k + n;
v27 = v12 * m + ii;
v28 = 255 * (char)v30[v20];
pixel((__int64)v31, v28, v28, v28);
v8 = addr((__int64)v32, v26, v27);
*(_WORD *)v8 = LOWORD(v31[0]);
*(_BYTE *)(v8 + 2) = BYTE2(v31[0]);
}
}
++v20;
}
}
cv::Mat::Mat(v16, v32);
cv::Mat::~Mat((cv::Mat *)v32);
return v16;
}
It breaks the input data into bits, and each bit is mapped to a pixel. So we just read the pixels out and reconstruct the input.
Solve:
import numpy as np
from PIL import Image
img = Image.open("flag.jpg")
data = np.array(img)
bits = ""
for i in range(40):
for j in range(313):
if data[i][j][0] < 128:
bits += "0"
else:
bits += "1"
assert len(bits) == 40 * 313
res = bytearray()
for i in range(5):
for j in range(313):
index = (i * 313 + j) * 8
# from LSB to MSB
part = bits[index:index+8][::-1]
value = int(part, 2)
res.append(value)
print(res[:res.index(b"\x00")].decode())
Output is an ASCII art:
_ ____ ___ ____ ___ _ ___ _____ _ _____ ____ _____ _ _ _____ _____ ___ _ _ _ _____ _____ ___ _ ___ ___ ___
/ \ / ___|_ _/ ___| / / || | _ __ / _ \_ _| |__ |___ /| _ \ | ___| | |___ / |___ / _ \ / |_ __ ___ | || | __ _| ____| | ____|_ __ ___ / _ \ __| |_ _|_ __ / _ \| \ \
/ _ \ \___ \| |\___ \ | || || |_| '_ \| | | || | | '_ \ |_ \| |_) | | |_ | | | |_ \ / / | | | | | '_ ` _ \| || |_ / _` | _| | _| | '_ \ / __| | | |/ _` || || '_ \ (_) | || |
/ ___ \ ___) | | ___) < < |__ _| | | | |_| || | | | | |___) | _ < | _| |_| |___) | / /| |_| | | | | | | | |__ _| (_| | |___ | |___| | | | (__| |_| | (_| || || | | \__, |_| > >
/_/ \_\____/___|____/ | | |_| |_| |_|\___/ |_| |_| |_|____/|_| \_\___|_| (_)_|____/___/_/ \___/___|_|_| |_| |_| |_| \__, |_____|___|_____|_| |_|\___|\___/ \__,_|___|_| |_| /_/(_)| |
\_\ |_____| |_____| |_____| |___/ |_____| /_/
Flag: ASIS{4n0Th3R_F!l3_7O_1m4gE_Enc0dIn9!}
. Actually, the 0
and O
are indisguishable, so I had to open a ticket for clarification.