More advanced than very very advanced
0A, FB, F4, 88, DD, 9D, 7D, 5F, 9E, A3, C6, BA, F5, 95, 5D, 88, 3B, E1, 31, 50, C7, FA, F5, 81, 99, C9, 7C, 23, A1, 91, 87, B5, B1, 95, E4,
Category: Reversing
This is a tiny ARM object, very similar to Advanced Reversing Mechanics 1.
kali@kali:~/Downloads$ ls -l hard.o
-rw-r--r-- 1 kali kali 1560 Jul 11 18:59 hard.o
kali@kali:~/Downloads$ file hard.o
hard.o: ELF 32-bit LSB relocatable, ARM, EABI5 version 1 (SYSV), not stripped
Let's just look at the encryptFlag()
function:
void encryptFlag(byte *param_1)
{
byte *pbVar1;
byte *pbVar2;
uint uVar3;
byte bVar4;
uint uVar5;
uint uVar6;
bVar4 = *param_1;
pbVar1 = param_1;
if (bVar4 == 0) {
return;
}
while( true ) {
uVar5 = (uint)bVar4;
uVar3 = uVar5 - 10 & 0xff;
uVar6 = uVar5;
if ((bVar4 < 0x50) && (uVar6 = uVar3, 0x50 < uVar3)) {
uVar6 = uVar5 + 0x46 & 0xff;
}
uVar6 = (uVar6 - 7 ^ 0x43) & 0xff;
pbVar2 = pbVar1 + 1;
*pbVar1 = (byte)(uVar6 << 6) | (byte)(uVar6 >> 2);
bVar4 = *pbVar2;
if (bVar4 == 0) break;
uVar6 = (int)(pbVar2 + -(int)param_1) % 5;
bVar4 = bVar4 << (-uVar6 & 7) | bVar4 >> (uVar6 & 0xff);
if (uVar6 == 2) {
bVar4 = bVar4 - 1;
}
*pbVar2 = bVar4;
bVar4 = *pbVar2;
pbVar1 = pbVar2;
}
return;
}
This obviously does a more complex transformation on the input string than just a simple decrement on each character like the last one.
Here's the disassembly of the main while loop in encryptFlag()
:
LAB_0001001c XREF[1]: 00010098(j)
0001001c 92 4c c5 e0 smull r4,r5,r2,r12
00010020 c5 30 63 e0 rsb r3,r3,r5, asr #0x1
00010024 03 31 83 e0 add r3,r3,r3, lsl #0x2
00010028 03 30 42 e0 sub r3,r2,r3
0001002c 00 20 63 e2 rsb r2,r3,#0x0
00010030 07 20 02 e2 and r2,r2,#0x7
00010034 1e 22 a0 e1 mov r2,lr, lsl r2
00010038 02 00 53 e3 cmp r3,#0x2
0001003c 3e 33 82 e1 orr r3,r2,lr, lsr r3
00010040 ff 30 03 e2 and r3,r3,#0xff
00010044 01 30 43 02 subeq r3,r3,#0x1
00010048 00 30 c1 e5 strb r3,[r1,#0x0]
0001004c 00 30 d1 e5 ldrb r3,[r1,#0x0]
LAB_00010050 XREF[1]: 00010018(j)
00010050 0a 20 43 e2 sub r2,r3,#0xa
00010054 4f 00 53 e3 cmp r3,#0x4f
00010058 ff 20 02 e2 and r2,r2,#0xff
0001005c 03 00 00 8a bhi LAB_00010070
00010060 50 00 52 e3 cmp r2,#0x50
00010064 46 30 83 e2 add r3,r3,#0x46
00010068 02 30 a0 91 cpyls r3,r2
0001006c ff 30 03 82 andhi r3,r3,#0xff
LAB_00010070 XREF[1]: 0001005c(j)
00010070 07 30 43 e2 sub r3,r3,#0x7
00010074 43 30 23 e2 eor r3,r3,#0x43
00010078 ff 30 03 e2 and r3,r3,#0xff
0001007c 03 23 a0 e1 mov r2,r3, lsl #0x6
00010080 23 31 82 e1 orr r3,r2,r3, lsr #0x2
00010084 01 30 c1 e4 strb r3,[r1],#0x1
00010088 00 e0 d1 e5 ldrb lr,[r1,#0x0]
0001008c 00 20 41 e0 sub r2,r1,r0
00010090 00 00 5e e3 cmp lr,#0x0
00010094 c2 3f a0 e1 mov r3,r2, asr #0x1f
00010098 df ff ff 1a bne LAB_0001001c
0001009c 30 40 bd e8 ldmia sp!,{ r4 r5 lr }=>local_c
000100a0 1e ff 2f e1 bx lr
My first thought when I look at this is that it's probably going to be quicker and easier to solve this one character at a time with the extracted encryptFlag()
function than to try to understand all the details and derive a working decryptFlag()
function. I did something similar in Put_your_thang_down, so I took the same approach here.
Write a solver that takes the decompiled encryptFlag()
function and then calls that in a loop for each printable character to derive the correct input character that transforms to the expected output character.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
typedef unsigned char byte;
void encryptFlag(byte *param_1)
{
byte *pbVar1;
byte *pbVar2;
uint uVar3;
byte bVar4;
uint uVar5;
uint uVar6;
bVar4 = *param_1;
pbVar1 = param_1;
if (bVar4 == 0) {
return;
}
while( 1 ) {
uVar5 = (uint)bVar4;
uVar3 = uVar5 - 10 & 0xff;
uVar6 = uVar5;
if ((bVar4 < 0x50) && (uVar6 = uVar3, 0x50 < uVar3)) {
uVar6 = uVar5 + 0x46 & 0xff;
}
uVar6 = (uVar6 - 7 ^ 0x43) & 0xff;
pbVar2 = pbVar1 + 1;
*pbVar1 = (byte)(uVar6 << 6) | (byte)(uVar6 >> 2);
bVar4 = *pbVar2;
if (bVar4 == 0) break;
uVar6 = (int)(pbVar2 + -(int)param_1) % 5;
bVar4 = bVar4 << (-uVar6 & 7) | bVar4 >> (uVar6 & 0xff);
if (uVar6 == 2) {
bVar4 = bVar4 - 1;
}
*pbVar2 = bVar4;
bVar4 = *pbVar2;
pbVar1 = pbVar2;
}
return;
}
int main()
{
char *printable_chars = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
int npchars = strlen(printable_chars);
byte nums[35] = {0x0A, 0xFB, 0xF4, 0x88, 0xDD, 0x9D, 0x7D, 0x5F, 0x9E, 0xA3, 0xC6, 0xBA, 0xF5, 0x95, 0x5D, 0x88, 0x3B, 0xE1, 0x31, 0x50, 0xC7, 0xFA, 0xF5, 0x81, 0x99, 0xC9, 0x7C, 0x23, 0xA1, 0x91, 0x87, 0xB5, 0xB1, 0x95, 0xE4};
byte flag[36] = {'r', 'g', 'b', 'C', 'T', 'F', '{', 0};
for (int cur = 0; cur < sizeof(flag); cur++) {
for (int x = 0; x < npchars; x++) {
char ch = printable_chars[x];
byte arr[36] = {0};
strcpy(arr,flag);
arr[cur] = ch;
encryptFlag(arr);
if (nums[cur] == arr[cur]) {
flag[cur] = ch;
printf("%s\n",flag);
break;
}
}
}
printf("\n");
}
Run it and we quickly get our flag:
kali@kali:~/Downloads$ gcc -Wno-pointer-to-int-cast -o adv_rev_mech2 adv_rev_mech2.c && time ./adv_rev_mech2
rgbCTF{
rgbCTF{
rgbCTF{
rgbCTF{
rgbCTF{
rgbCTF{
rgbCTF{
rgbCTF{A
rgbCTF{AR
rgbCTF{ARM
rgbCTF{ARM_
rgbCTF{ARM_a
rgbCTF{ARM_ar
rgbCTF{ARM_ar1
rgbCTF{ARM_ar1t
rgbCTF{ARM_ar1th
rgbCTF{ARM_ar1thm
rgbCTF{ARM_ar1thm3
rgbCTF{ARM_ar1thm3t
rgbCTF{ARM_ar1thm3t1
rgbCTF{ARM_ar1thm3t1c
rgbCTF{ARM_ar1thm3t1c_
rgbCTF{ARM_ar1thm3t1c_r
rgbCTF{ARM_ar1thm3t1c_r0
rgbCTF{ARM_ar1thm3t1c_r0c
rgbCTF{ARM_ar1thm3t1c_r0ck
rgbCTF{ARM_ar1thm3t1c_r0cks
rgbCTF{ARM_ar1thm3t1c_r0cks_
rgbCTF{ARM_ar1thm3t1c_r0cks_f
rgbCTF{ARM_ar1thm3t1c_r0cks_fa
rgbCTF{ARM_ar1thm3t1c_r0cks_fad
rgbCTF{ARM_ar1thm3t1c_r0cks_fad9
rgbCTF{ARM_ar1thm3t1c_r0cks_fad96
rgbCTF{ARM_ar1thm3t1c_r0cks_fad961
rgbCTF{ARM_ar1thm3t1c_r0cks_fad961}
real 0m0.003s
user 0m0.003s
sys 0m0.000s
Now this worked just fine and solved it in milliseconds, but immediately after this I realized angr.io would have been a nice way to solve this without having to recompile any code. That solution would probably be quicker to write but take longer to run.
In any case, the flag is:
rgbCTF{ARM_ar1thm3t1c_r0cks_fad961}