Recently, I decided to put my reversing skills to a test by solving some of the public crackmes and writing down my solutions. This is the first of many to come.
For work, I do some very basic Android, TEE, RASP and IoT reversing, but most of those tasks are too shallow and repetitive. You can’t get much depth when reversing is an “if there’s time to waste” part of an engagement with overall length of a week and bottomless scope, so this series is about a monkey learning to dive deeper.
I decided to source my first batch of challenges from Crackmes.One. First challenge is of difficulty level 1 and is targeting ARM/Linux.
Running it
First, figure out what the binary is:
$ file diff1_crack
diff1_crack: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 2.6.16, BuildID[sha1]=30a939bb626d5ec53b08ccc7ad6745b93bcce5cf, stripped
$ readelf -a diff1_crack
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: ARM
Version: 0x1
Entry point address: 0x8591
Start of program headers: 52 (bytes into file)
Start of section headers: 4512 (bytes into file)
Flags: 0x5000002, Version5 EABI, <unknown>
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 9
Size of section headers: 40 (bytes)
Number of section headers: 27
Section header string table index: 26
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 00008154 000154 000013 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 00008168 000168 000020 00 A 0 0 4
[ 3] .note.gnu.build-i NOTE 00008188 000188 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 000081ac 0001ac 000068 04 A 5 0 4
[ 5] .dynsym DYNSYM 00008214 000214 000110 10 A 6 1 4
[ 6] .dynstr STRTAB 00008324 000324 0000b3 00 A 0 0 1
[ 7] .gnu.version VERSYM 000083d8 0003d8 000022 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 000083fc 0003fc 000040 00 A 6 2 4
[ 9] .rel.dyn REL 0000843c 00043c 000018 08 A 5 0 4
[10] .rel.plt REL 00008454 000454 000070 08 A 5 12 4
[11] .init PROGBITS 000084c4 0004c4 00000a 00 AX 0 0 4
[12] .plt PROGBITS 000084d0 0004d0 0000c0 04 AX 0 0 4
[13] .text PROGBITS 00008590 000590 0004d4 00 AX 0 0 4
[14] .fini PROGBITS 00008a64 000a64 000006 00 AX 0 0 4
[15] .rodata PROGBITS 00008a6c 000a6c 0000e4 00 A 0 0 4
[16] .ARM.exidx ARM_EXIDX 00008b50 000b50 000008 00 AL 13 0 4
[17] .init_array INIT_ARRAY 00010f04 000f04 000004 00 WA 0 0 4
[18] .fini_array FINI_ARRAY 00010f08 000f08 000004 00 WA 0 0 4
[19] .jcr PROGBITS 00010f0c 000f0c 000004 00 WA 0 0 4
[20] .dynamic DYNAMIC 00010f10 000f10 0000f0 08 WA 6 0 4
[21] .got PROGBITS 00011000 001000 000048 04 WA 0 0 4
[22] .data PROGBITS 00011048 001048 00000c 00 WA 0 0 4
[23] .bss NOBITS 00011058 001054 000010 00 WA 0 0 8
[24] .comment PROGBITS 00000000 001054 00002a 01 MS 0 0 1
[25] .ARM.attributes ARM_ATTRIBUTES 00000000 00107e 000031 00 0 0 1
[26] .shstrtab STRTAB 00000000 0010af 0000f0 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
y (purecode), p (processor specific)
There are no section groups in this file.
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
EXIDX 0x000b50 0x00008b50 0x00008b50 0x00008 0x00008 R 0x4
PHDR 0x000034 0x00008034 0x00008034 0x00120 0x00120 R E 0x4
INTERP 0x000154 0x00008154 0x00008154 0x00013 0x00013 R 0x1
[Requesting program interpreter: /lib/ld-linux.so.3]
LOAD 0x000000 0x00008000 0x00008000 0x00b58 0x00b58 R E 0x8000
LOAD 0x000f04 0x00010f04 0x00010f04 0x00150 0x00164 RW 0x8000
DYNAMIC 0x000f10 0x00010f10 0x00010f10 0x000f0 0x000f0 RW 0x4
NOTE 0x000168 0x00008168 0x00008168 0x00044 0x00044 R 0x4
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4
GNU_RELRO 0x000f04 0x00010f04 0x00010f04 0x000fc 0x000fc R 0x1
Section to Segment mapping:
Segment Sections...
00 .ARM.exidx
01
02 .interp
03 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .ARM.exidx
04 .init_array .fini_array .jcr .dynamic .got .data .bss
05 .dynamic
06 .note.ABI-tag .note.gnu.build-id
07
08 .init_array .fini_array .jcr .dynamic
Dynamic section at offset 0xf10 contains 25 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libc.so.6]
0x00000001 (NEEDED) Shared library: [ld-linux.so.3]
0x0000000c (INIT) 0x84c5
0x0000000d (FINI) 0x8a65
0x00000019 (INIT_ARRAY) 0x10f04
0x0000001b (INIT_ARRAYSZ) 4 (bytes)
0x0000001a (FINI_ARRAY) 0x10f08
0x0000001c (FINI_ARRAYSZ) 4 (bytes)
0x6ffffef5 (GNU_HASH) 0x81ac
0x00000005 (STRTAB) 0x8324
0x00000006 (SYMTAB) 0x8214
0x0000000a (STRSZ) 179 (bytes)
0x0000000b (SYMENT) 16 (bytes)
0x00000015 (DEBUG) 0x0
0x00000003 (PLTGOT) 0x11000
0x00000002 (PLTRELSZ) 112 (bytes)
0x00000014 (PLTREL) REL
0x00000017 (JMPREL) 0x8454
0x00000011 (REL) 0x843c
0x00000012 (RELSZ) 24 (bytes)
0x00000013 (RELENT) 8 (bytes)
0x6ffffffe (VERNEED) 0x83fc
0x6fffffff (VERNEEDNUM) 2
0x6ffffff0 (VERSYM) 0x83d8
0x00000000 (NULL) 0x0
Relocation section '.rel.dyn' at offset 0x43c contains 3 entries:
Offset Info Type Sym.Value Sym. Name
00011044 00000115 R_ARM_GLOB_DAT 00000000 __gmon_start__
00011058 00000514 R_ARM_COPY 00011058 __stack_chk_guard@GLIBC_2.4
00011060 00000b14 R_ARM_COPY 00011060 stdin@GLIBC_2.4
Relocation section '.rel.plt' at offset 0x454 contains 14 entries:
Offset Info Type Sym.Value Sym. Name
0001100c 00000716 R_ARM_JUMP_SLOT 000084e4 abort@GLIBC_2.4
00011010 00000d16 R_ARM_JUMP_SLOT 000084f0 __libc_start_main@GLIBC_2.4
00011014 00000816 R_ARM_JUMP_SLOT 000084fc signal@GLIBC_2.4
00011018 00000116 R_ARM_JUMP_SLOT 00000000 __gmon_start__
0001101c 00000216 R_ARM_JUMP_SLOT 00008518 ptrace@GLIBC_2.4
00011020 00000316 R_ARM_JUMP_SLOT 00008524 fgets@GLIBC_2.4
00011024 00000e16 R_ARM_JUMP_SLOT 00008530 system@GLIBC_2.4
00011028 00000f16 R_ARM_JUMP_SLOT 0000853c memcpy@GLIBC_2.4
0001102c 00000416 R_ARM_JUMP_SLOT 00008548 alarm@GLIBC_2.4
00011030 00000916 R_ARM_JUMP_SLOT 00008554 printf@GLIBC_2.4
00011034 00000a16 R_ARM_JUMP_SLOT 00008560 malloc@GLIBC_2.4
00011038 00001016 R_ARM_JUMP_SLOT 0000856c __stack_chk_fail@GLIBC_2.4
0001103c 00000616 R_ARM_JUMP_SLOT 00008578 puts@GLIBC_2.4
00011040 00000c16 R_ARM_JUMP_SLOT 00008584 exit@GLIBC_2.4
Unwind section '.ARM.exidx' at offset 0xb50 contains 1 entry:
0x8590: 0x1 [cantunwind]
Symbol table '.dynsym' contains 17 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__
2: 00008518 0 FUNC GLOBAL DEFAULT UND ptrace@GLIBC_2.4 (2)
3: 00008524 0 FUNC GLOBAL DEFAULT UND fgets@GLIBC_2.4 (2)
4: 00008548 0 FUNC GLOBAL DEFAULT UND alarm@GLIBC_2.4 (2)
5: 00011058 4 OBJECT GLOBAL DEFAULT 23 __stack_chk_guard@GLIBC_2.4 (3)
6: 00008578 0 FUNC GLOBAL DEFAULT UND puts@GLIBC_2.4 (2)
7: 000084e4 0 FUNC GLOBAL DEFAULT UND abort@GLIBC_2.4 (2)
8: 000084fc 0 FUNC GLOBAL DEFAULT UND signal@GLIBC_2.4 (2)
9: 00008554 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.4 (2)
10: 00008560 0 FUNC GLOBAL DEFAULT UND malloc@GLIBC_2.4 (2)
11: 00011060 4 OBJECT GLOBAL DEFAULT 23 stdin@GLIBC_2.4 (2)
12: 00008584 0 FUNC GLOBAL DEFAULT UND exit@GLIBC_2.4 (2)
13: 000084f0 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.4 (2)
14: 00008530 0 FUNC GLOBAL DEFAULT UND system@GLIBC_2.4 (2)
15: 0000853c 0 FUNC GLOBAL DEFAULT UND memcpy@GLIBC_2.4 (2)
16: 0000856c 0 FUNC GLOBAL DEFAULT UND __stack_chk_fail@GLIBC_2.4 (2)
Histogram for `.gnu.hash' bucket list length (total of 3 buckets):
Length Number % of total Coverage
0 0 ( 0.0%)
1 0 ( 0.0%) 0.0%
2 0 ( 0.0%) 0.0%
3 0 ( 0.0%) 0.0%
4 1 ( 33.3%) 26.7%
5 1 ( 33.3%) 60.0%
6 1 ( 33.3%) 100.0%
Version symbols section '.gnu.version' contains 17 entries:
Addr: 00000000000083d8 Offset: 0x0003d8 Link: 5 (.dynsym)
000: 0 (*local*) 0 (*local*) 2 (GLIBC_2.4) 2 (GLIBC_2.4)
004: 2 (GLIBC_2.4) 3 (GLIBC_2.4) 2 (GLIBC_2.4) 2 (GLIBC_2.4)
008: 2 (GLIBC_2.4) 2 (GLIBC_2.4) 2 (GLIBC_2.4) 2 (GLIBC_2.4)
00c: 2 (GLIBC_2.4) 2 (GLIBC_2.4) 2 (GLIBC_2.4) 2 (GLIBC_2.4)
010: 2 (GLIBC_2.4)
Version needs section '.gnu.version_r' contains 2 entries:
Addr: 0x00000000000083fc Offset: 0x0003fc Link: 6 (.dynstr)
000000: Version: 1 File: ld-linux.so.3 Cnt: 1
0x0010: Name: GLIBC_2.4 Flags: none Version: 3
0x0020: Version: 1 File: libc.so.6 Cnt: 1
0x0030: Name: GLIBC_2.4 Flags: none Version: 2
Displaying notes found in: .note.ABI-tag
Owner Data size Description
GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag)
OS: Linux, ABI: 2.6.16
Displaying notes found in: .note.gnu.build-id
Owner Data size Description
GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring)
Build ID: 30a939bb626d5ec53b08ccc7ad6745b93bcce5cf
Attribute Section: aeabi
File Attributes
Tag_CPU_name: "7-A"
Tag_CPU_arch: v7
Tag_CPU_arch_profile: Application
Tag_ARM_ISA_use: Yes
Tag_THUMB_ISA_use: Thumb-2
Tag_FP_arch: VFPv3-D16
Tag_ABI_PCS_wchar_t: 4
Tag_ABI_FP_denormal: Needed
Tag_ABI_FP_exceptions: Needed
Tag_ABI_FP_number_model: IEEE 754
Tag_ABI_align_needed: 8-byte
Tag_ABI_align_preserved: 8-byte, except leaf SP
Tag_ABI_enum_size: int
Tag_ABI_HardFP_use: Deprecated
Tag_DIV_use: Not allowed
This is a 32-bit ARM v7-A binary, built for Linux Kernel 2.6.16. I didn’t have a compatible system handy, so I did some studying and found this gem: https://people.debian.org/~aurel32/qemu/armhf/.
It’s a plug-n-play ARMv7 QEMU image. But when I tried to run it, I got this:
user@debian-armhf:~$ ./diff1_crack
-bash: ./diff1_crack: No such file or directory
I’ve seen it before, and usually it happens when the system doesn’t have a compatible loader/dynamic linker. Looking at output of readelf
again, I saw ld-linux.so.3
mentioned and searched for it on the QEMU system:
user@debian-armhf:~$ ls -al /lib/ld-linux*
lrwxrwxrwx 1 root root 30 Dec 30 2012 /lib/ld-linux-armhf.so.3 -> arm-linux-gnueabihf/ld-2.13.so
user@debian-armhf:~$ ls -al /lib/arm-linux-gnueabihf/
total 5892
drwxr-xr-x 4 root root 12288 Dec 16 2013 .
drwxr-xr-x 13 root root 4096 Aug 3 09:41 ..
-rwxr-xr-x 1 root root 93472 Dec 30 2012 ld-2.13.so
lrwxrwxrwx 1 root root 10 Dec 30 2012 ld-linux-armhf.so.3 -> ld-2.13.so
lrwxrwxrwx 1 root root 19 Dec 30 2012 ld-linux.so.3 -> ld-linux-armhf.so.3
Looks like it’s just missing a symlink. Let’s try that:
root@debian-armhf:~# ln -s /lib/arm-linux-gnueabihf/ld-linux.so.3 /lib/ld-linux.so.3
user@debian-armhf:~$ ./diff1_crack
WELCOME TO ARM CRACK ME LEVEL 1 or MAYBE IT WAS 2????? GOOD LUCK
NO PATCHING OR MODIFYING WHATSOEVER
The program then blocks for input and exits silently upon receiving any.
Step 1 complete: get the binary to run.
Entry point
At the entry point, I see a typical entry point for a binary compiled from C. There is a short setup step that will execute functions from .init_array
section before entering main()
.
; Segment type: Pure code
AREA .text, CODE
; ORG 0x8590
CODE16
; Attributes: noreturn
EXPORT start
start
MOV.W R11, #0 ; Rd = Op2
MOV.W LR, #0 ; Rd = Op2
POP.W {R1} ; argc
MOV R2, SP ; ubp_av
PUSH.W {R2} ; stack_end
PUSH.W {R0} ; rtld_fini
LDR.W R12, =(nullsub_1+1) ; Load from Memory
PUSH.W {R12} ; fini
LDR R0, =(libc_main+1) ; main
LDR R3, =(libc_init+1) ; init
BLX __libc_start_main ; Branch with Link and Exchange (immediate address)
BLX abort ; Branch with Link and Exchange (immediate address)
; End of function start
At this point I invoke Hex-Rays to get the lay of the land.
int libc_main()
{
malloc(12u);
buf1_ptr = malloc(12u);
buf2_ptr = malloc(12u);
puts("WELCOME TO ARM CRACK ME LEVEL 1 or MAYBE IT WAS 2????? GOOD LUCK\n NO PATCHING OR MODIFYING WHATSOEVER");
sub_8688(12, buf1_ptr, buf2_ptr, "`TU_KU_KRXMS");
dest = malloc(12u);
s = malloc(12u);
memcpy(dest, "`TU_KU_KRXMS", 12u);
v0 = memcpy(buf1_ptr, "`TU_KU_KRXMS", 12u);
sub_862C(v0);
fgets(s, 13, stdin); // this is where we are. Read up to 12 chars.
if ( sub_8738(s) != 12 )
exit(1);
if ( sub_86A8(dest, s) == 1 )
{
printf("YOU Sol... OOOPS YOU DIDNT");
system("yes FAIL");
}
for ( i = 0; i <= 11; ++i )
dest[i] = sub_877C(dest[i], 12, 12, 12);
memcpy(buf2_ptr, s, 12u);
v1 = sub_8874(buf1_ptr, buf2_ptr, "`TU_KU_KRXMS", dest);
if ( v1 == 1 )
puts("WAY TO GO SOLVED IT!");
return v1;
}
From the looks of it, sub_8738
does a length check. Let’s verify that.
int __fastcall sub_8738(char *a1)
{
int next_byte; // ST08_4
int loop_ctr; // [sp+Ch] [bp+Ch]
for ( loop_ctr = 0; a1[loop_ctr]; next_byte = a1[loop_ctr] + 0xF )
++loop_ctr;
return loop_ctr;
}
Condition for loop continuation is a1[loop_ctr]
. Remember, that this doesn’t have to be an explicit comparison, it just has to evaluate to true
. Generally, such checks in assembly are implemented as a comparison of some value to zero. So a1[loop_ctr]
evaluating to zero at some point will terminate the loop. A zero is also a null-byte. A null-byte is often used as a string terminator. So this loop goes through the supplied string one byte at a time, until it encounters a zero - it counts characters. However, there is still this weird action that is done on every iteration: next_byte = a1[loop_ctr] + 0xF
. It doesn’t make sense, so could this be a decompiler issue?
When in doubt, I examine the disassembly.
In the first basic block, a loop counter is initialized to 0
:
PUSH {R7} ; Push registers
SUB SP, SP, #0x14 ; Rd = Op1 - Op2
ADD R7, SP, #0 ; Rd = Op1 + Op2
STR R0, [R7,#0x14+a1] ; Store to Memory
MOV.W R3, #0 ; Rd = Op2
STR R3, [R7,#0x14+loop_ctr] ; Store to Memory
B loc_8762 ; Branch
Next, a character is loaded into r3
and checked against a null:
loc_8762
LDR R3, [R7,#0x14+loop_ctr] ; Load from Memory
LDR R2, [R7,#0x14+a1] ; Load from Memory
ADDS R3, R2, R3 ; Rd = Op1 + Op2
LDRB R3, [R3] ; loads a character into r3
CMP R3, #0 ; checks if it's a null, proceeds to epilogue if yes
BNE not_null_loop ; Branch
From here, there are two branches. The non-null one increments the counter and performs some more nonsense that never gets used again:
not_null_loop
LDR R3, [R7,#0x14+loop_ctr] ; Load from Memory
ADD.W R3, R3, #1 ; increments loop counter
STR R3, [R7,#0x14+loop_ctr] ; puts it back
LDR R3, [R7,#0x14+loop_ctr] ; loads it again (redundant, as well as the rest of this BB)
LDR R2, [R7,#0x14+a1] ; loads a1 (which is a char *)
ADDS R3, R2, R3 ; r3 = a1 + loop_ctr
LDRB R3, [R3] ; load next byte from char *
STR R3, [R7,#0x14+next_byte] ; redundant store-load op
LDR R3, [R7,#0x14+next_byte] ; Load from Memory
ADD.W R3, R3, #0xF ; adds 0xF to this byte
STR R3, [R7,#0x14+next_byte] ; stores the resulting value overwriting what was there before
This is why the decompilation output in IDA was confusing. It doesn’t get used indeed, so for my use case, I can ignore what happens here after incrementing the loop counter.
The epilogue of the function pulls a value from a memory location into r3
, moves it into r0
, restores the registers and returns:
epilogue_867E
LDR R3, [R7,#0x14+loop_ctr] ; Load from Memory
MOV R0, R3 ; puts current counter value in r0, restores the registers and returns
ADD.W R7, R7, #0x14 ; Rd = Op1 + Op2
MOV SP, R7 ; Rd = Op2
POP {R7} ; Pop registers
BX LR ; Branch to/from Thumb mode
; End of function sub_8738
For some reason, the value was not read directly into r0
, which has to be due to a convention or some limitation of ARM architecture. I decided not to follow up. Either way, the return value is in r0
, which is standard for ARM/Linux binaries.
Back to main()
. So this crackme expects precisely 12
characters to be given to it and exits if that is not the case:
fgets(s, 13, stdin); // this is where we are. Read up to 12 chars.
if ( strlen(s) != 12 )
exit(1);
Next, it sends my input and a buffer filled out earlier in the function to sub_86A8
. If that returns 1
, the crackme will prank me by executing yes FAIL
as a shell command:
if ( sub_86A8(dest, s) == 1 ) // remember, dest has || `TU_KU_KRXMS ||
{
printf("YOU Sol... OOOPS YOU DIDNT");
system("yes FAIL");
}
Upon closer examination of sub_86A8
, I see that it returns 1
if both strings passed to it are equal and 12
bytes long:
signed int __fastcall sub_86A8(char *other_str, char *input_str)
{
signed int i; // [sp+Ch] [bp+Ch]
for ( i = 0; ; ++i )
{
if ( !other_str[i] && !input_str[i] ) // both strings have next byte as 0 -> 1
return 1;
if ( !other_str[i] ) // first string has next byte as 0 -> -1
return -1;
if ( !input_str[i] ) // input string has next byte as 0 -> -2
return -2;
if ( other_str[i] != input_str[i] )
return 9;
if ( i > 10 )
break;
}
return 1;
}
Therefore, entering `TU_KU_KRXMS into the prompt will trigger the prank.
Next, dest
is overwritten with output of sub_877C
, buf2
is overwritten with my input and sub_8874
performs the crackme solution check:
for ( i = 0; i <= 11; ++i )
dest[i] = sub_877C(dest[i], 12, 12);
memcpy(buf2_ptr, s, 12u);
v0 = sub_8874(buf1_ptr, buf2_ptr, "`TU_KU_KRXMS", dest);
if ( v0 == 1 )
puts("WAY TO GO SOLVED IT!");
return v0;
}
First, I decided to look at the solution check.
int __fastcall sub_8874(void *buf1, char *my_input, char *static_key, char *dest)
{
signed int cmp_static_dest; // [sp+10h] [bp+10h]
char *_buf1; // [sp+18h] [bp+18h]
signed int cmp_dest_myinput; // [sp+1Ch] [bp+1Ch]
_buf1 = memcpy(buf1, dest, 12u);
cmp_dest_myinput = custom_compare_strings(_buf1, my_input);
cmp_static_dest = custom_compare_strings(static_key, dest);
while ( cmp_dest_myinput <= 7 )
cmp_dest_myinput += custom_compare_strings(_buf1, my_input);
return cmp_static_dest - cmp_dest_myinput;
} // buf1_ptr, buf2_ptr, "`TU_KU_KRXMS", dest
The equation to solve is: cmp_static_dest - cmp_dest_myinput = 1
.
cmp_static_dest
is computed based on hardcoded values in sub_877C
. It is safe to assume that a modified copy of the hardcoded value will not be equal to that value, so this comparison is likely to result in the case of non-equal strings without me even looking at sub_877C
. A quick look at the comparison function earlier shows that this would result in a return value of 9
.
The equation becomes: 9 - cmp_dest_myinput = 1
.
cmp_dest_myinput
is a value based on my input and needs to be 8
for me to solve this challenge. There is another loop here that means it will always be 8
after the loop, as long as I provide the input this code expects.
However, my string here will be compared to a value based on computations in sub_877C
. Looks like the author wants me to calculate that value, provide it to the binary and that will be the solution.
This is what that function does:
int __fastcall sub_877C(char a1, int a2, int a3)
{
if ( a1 > 0xA )
a3 = a1;
return a3 + 20;
}
For character codes above 0x0A
(newline), it will return that character value incremented by 20
. 0x0A
is a character code smaller than that of any printable character, so for my purposes, I can ignore this condition and just increment all characters in the static string by 20
. Solution code:
#include <stdio.h>
int main(int argc, char const *argv[])
{
const char base[12] = "`TU_KU_KRXMS";
char solution[12];
for (int i = 0; i < 12; i++) {
solution[i] = base[i] + 20;
}
printf("Solution: %s\n", solution);
return 0;
}
And the answer is:
$ ./solution1
Solution: this_is_flag
user@debian-armhf:~$ ./diff1_crack
WELCOME TO ARM CRACK ME LEVEL 1 or MAYBE IT WAS 2????? GOOD LUCK
NO PATCHING OR MODIFYING WHATSOEVER
this_is_flag
WAY TO GO SOLVED IT!