Rating:

## Ancient

The program doesn't use CODE optimizations, so it's just mostly the same as the source, but with some unexpected junk codes. Remove those instructions, then we can disassemble it normally.

| Address | Length | Original bytes | Patched bytes |
| ---- | ---- | ---- | ---- |
| 0000000000401844 | 0x9 | 0x9 74 07 0F 80 87 48 83 C4 08 | 90 90 90 90 90 90 90 90 90 |
| 000000000040184F | 0x8 | 0x8 E8 02 00 00 00 0F 85 59 | 90 90 90 90 90 90 90 90 |
| 000000000040185B | 0x7 | 74 05 0F 48 83 C4 08 | 90 90 90 90 90 90 90 |
| 000000000040188F | 0x7 | 74 05 80 48 83 C4 08 | 90 90 90 90 90 90 90 |

At first, locating `0x401ed4` by the string `Wrong. You fail to get it.`

```
ex@ubuntu-hp:~/d3ctf/ancient$ ./ancient
This is an ancient string, it represents the origin of all binary characters, isn't it. Let me see, it says 0,1,2,3,4,5,6,7...
Please input the flag:
a
Wrong. You fail to get it.
```

It's marked at `A` in disassembling the source below. Then according to the control flow of this application, we can locate `0x401c93`, or a say on, `B`.

```c++
__int64 __fastcall main(int a1, char **a2, char **a3)
{
...

v14 = malloc(0x170uLL);
sub_402110((__int64)&unk_407498, 16);
sub_402B60((__int64)&unk_407498);
v15 = 0;
sub_403500((__int64)v14, (__int64)&xmmword_407370, (_BYTE *)184, (__int64)&unk_407498, 126, 0);
int_value = 0;
v16 = *(_BYTE *)qword_407328;
if ( *(_BYTE *)qword_407328 )
{
v17 = 0LL;
v18 = 0;
C: do // count
{
if ( !v18 )
v18 = 1;
if ( v16 != v14[v17] )
break;
v15 += v18;
if ( v15 > 178 )
break;
v17 = v15;
v16 = *(_BYTE *)(qword_407328 + v15);
}
while ( v16 );
int_value = v15;
}
sub_401820(&int_value);
B: if ( int_value < 178 )
{ // wrong
if ( !(_BYTE)byte_407738 && __cxa_guard_acquire(&byte_407738) )
{
qword_407730 = 0x2E24004940415C79LL;
__cxa_atexit(sub_402070, &qword_407730, &unk_4070A8);
__cxa_guard_release(&byte_407738);
}
v19 = (const char *)&qword_407730;
if ( HIBYTE(qword_407730) )
{
LOBYTE(qword_407730) = qword_407730 ^ 0x2E;
BYTE1(qword_407730) ^= 0x2Eu;
BYTE2(qword_407730) ^= 0x2Eu;
BYTE3(qword_407730) ^= 0x2Eu;
BYTE4(qword_407730) ^= 0x2Eu;
BYTE5(qword_407730) ^= 0x2Eu;
BYTE6(qword_407730) ^= 0x2Eu;
HIBYTE(qword_407730) ^= 0x2Eu;
}
}
else
{
if ( !(_BYTE)byte_407728 && __cxa_guard_acquire(&byte_407728) )// success
{
*(_QWORD *)qword_407718 = 0xF5D5D4B4D4D5B7DLL;
word_407720 = 11812;
__cxa_atexit(sub_402060, qword_407718, &unk_4070A8);
__cxa_guard_release(&byte_407728);
}
v19 = qword_407718;
if ( HIBYTE(word_407720) )
{
qword_407718[0] ^= 0x2Eu;
qword_407718[1] ^= 0x2Eu;
qword_407718[2] ^= 0x2Eu;
qword_407718[3] ^= 0x2Eu;
qword_407718[4] ^= 0x2Eu;
qword_407718[5] ^= 0x2Eu;
qword_407718[6] ^= 0x2Eu;
qword_407718[7] ^= 0x2Eu;
LOBYTE(word_407720) = word_407720 ^ 0x2E;
HIBYTE(word_407720) ^= 0x2Eu;
}
}
v11 = strlen(v19);
output = (char *)v19; // success
}
else
{
A: if ( !(_BYTE)byte_407710 && __cxa_guard_acquire(&byte_407710) )
{
*(_OWORD *)str_ptr = xmmword_405030;
qword_407700 = 'p9mv~9vm';
dword_407708 = '\x19\x137m';
__cxa_atexit(sub_402050, str_ptr, &unk_4070A8);
__cxa_guard_release(&byte_407710);
}
sub_4020C0((__m128 *)str_ptr);
v11 = strlen(str_ptr);
output = str_ptr;
}
std::__ostream_insert<char,std::char_traits<char>>(std::cout, output, v11);
return 0LL;
}
```

At least, 178 or greater is required by the `int_value`, that we can go to the `success`, according to the disassembling source above.

`int_value` has been counted at `0x401bb3`(position `C`), it can be matched at most 178 times, so we must get the whole binary content which length is 178.

It's fixed result by `sub_402110` and `sub_402110`, I don't know the role in the application clearly about `sub_403500`, and guest it's just multiplication with two big numbers. As the operation is stable that high bits don't affect the low bits, we can crack the flag byte by byte.

Finally, we can get the flag.

```c++
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>

unsigned char key[] = {
0x54, 0x67, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x61,
0x6e, 0x63, 0x69, 0x65, 0x6e, 0x74, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e,
0x67, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x72, 0x65, 0x70, 0x72, 0x65, 0x73,
0x65, 0x6e, 0x74, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x6f, 0x72, 0x69,
0x67, 0x69, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x61, 0x6c, 0x6c, 0x20, 0x62,
0x69, 0x6e, 0x61, 0x72, 0x79, 0x20, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63,
0x74, 0x65, 0x72, 0x73, 0x2c, 0x20, 0x69, 0x73, 0x6e, 0x27, 0x74, 0x20,
0x69, 0x74, 0x2e, 0x20, 0x4c, 0x65, 0x74, 0x20, 0x6d, 0x65, 0x20, 0x73,
0x65, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x73, 0x61, 0x79, 0x73, 0x20,
0x30, 0x2c, 0x31, 0x2c, 0x32, 0x2c, 0x33, 0x2c, 0x34, 0x2c, 0x35, 0x2c,
0x36, 0x2c, 0x37, 0x2e, 0x2e, 0x2e, 0x67, 0xf3, 0xa3, 0xca, 0x23, 0x58,
0xa3, 0xd1, 0xf8, 0xc1, 0x96, 0xe3, 0xd7, 0x85, 0x85, 0xfe, 0xbe, 0x7b,
0xd2, 0x82, 0x59, 0xf4, 0xd8, 0xf0, 0x5f, 0xf5, 0xe2, 0x55, 0xe5, 0x2c,
0x14, 0xdc, 0xd6, 0xf4, 0x60, 0xf9, 0x89, 0x84, 0x0c, 0x70, 0x50, 0xb8,
0xf5, 0xde, 0x7f, 0xff, 0x5a, 0xc8, 0x8d, 0x61, 0xf0, 0x02};
unsigned int key_len = 178;

unsigned char fixed[0x1000];

unsigned char printable[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789";
#define LEN (sizeof(printable) - 1)

inline static int same(unsigned char *a, unsigned char *b)
{
int i;
for (i = 0; i < key_len; i++)
{
if (*a == *b)
{
a++;
b++;
}
else
{
break;
}
}
return i - 131;
}

int main()
{
int fd, i, ii, iii, offset, temp;
unsigned char output[0x800], input[0x800] = "This is an ancient string, it represents the origin of all binary characters, isn't it."
" Let me see, it says 0,1,2,3,4,5,6,7..."
"d3ctf{w0W_sEems_u_bRe4k_uP_tHe_H1DdeN_s7R_By_Ae1tH_c0De}";

fd = open("bin", O_RDONLY);
mmap((void *)0x400000, 32768, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FIXED | MAP_DENYWRITE, fd, 0);
close(fd);

*(size_t *)0x407080 = (size_t)malloc;

mprotect((void *)0x400000, 32768, PROT_EXEC | PROT_READ);

((void (*)(unsigned char *, int))0x402110)(fixed, 16);
((void (*)(unsigned char *))0x402B60)(fixed);

memset(output, 0, sizeof(output));
offset = 178;
printf("%c%c\n\n", input[offset - 2], input[offset - 1]);

for (i = 0; i < LEN; i++)
{
for (ii = 0; ii < LEN; ii++)
{
for (iii = 0; iii < LEN; iii++)
{
input[offset] = printable[i];
input[offset + 1] = printable[ii];
input[offset + 2] = printable[iii];

((void (*)(unsigned char *, int))0x402110)(fixed, 16);
((void (*)(unsigned char *))0x402B60)(fixed);
((void (*)(unsigned char *, unsigned char *, int, unsigned char *, int, int))0x403500)(output, input, 184, fixed, 126, 0);
temp = same(key, output);
if (44 <= temp)
{
printf("%d: %c%c%c\n", temp, printable[i], printable[ii], printable[iii]);
// exit(EXIT_SUCCESS);
}
}
}
}

return 0;
}
```

d3ctf{w0W_sEems_u_bRe4k_uP_tHe_H1DdeN_s7R_By_Ae1tH_c0De}

© XMCVE

Original writeup (http://www.xmcve.com/).