
ARM 아키텍처의 어셈블리로 코드를 구성했다.
int key1(){
asm("mov r3, pc\n");
}
int key2(){
asm(
"push {r6}\n"
"add r6, pc, $1\n"
"bx r6\n"
".code 16\n"
"mov r3, pc\n"
"add r3, $0x4\n"
"push {r3}\n"
"pop {pc}\n"
".code 32\n"
"pop {r6}\n"
);
}
int key3(){
asm("mov r3, lr\n");
}
사용된 레지스터
LR (R14) : subroutine 이후 돌아갈 리턴 주소를 저장
PC (R15) : x86의 것과 다르게, 현재 fetch되고 있는 명령의 주소를 가리킨다. 즉 현재 실행되는 명령어의 다음 주소 (+8)을 의미한다.
SP (R13) : stack pointer
R0~R12 : 함수 호출 인자를 전달
R0 : eax같은 함수 리턴 값 저장
사용된 명령어들은 intel의 어셈과 크게 다른것은 없어보이지만,
bx 명령어는 서브 루틴에서 복귀하며 LR의 값을 PC에 저장하여 복귀하는 명령어 (asm<->thumb 모드 오갈때 쓰이는듯)
.code 명령어는 code section을 어떤 모드로 조합할지를 지정해주는 역할로, ARM에는 thumb-16 모드와 ARM-32 모드가 있다고 하여 그 둘을 오갈 때 저걸 붙여주는 것 같다.
이를 바탕으로, 리턴값들을 알아야 하기 때문에 r0에 들어가는 값을 위주로 key 함수들의 반환값을 살펴보면,
Dump of assembler code for function key1:
0x00008cd4 <+0>: push {r11} ; (str r11, [sp, #-4]!)
0x00008cd8 <+4>: add r11, sp, #0
0x00008cdc <+8>: mov r3, pc
0x00008ce0 <+12>: mov r0, r3
0x00008ce4 <+16>: sub sp, r11, #0
0x00008ce8 <+20>: pop {r11} ; (ldr r11, [sp], #4)
0x00008cec <+24>: bx lr
0x8cdc + 8 = 0x8ce4
Dump of assembler code for function key2:
0x00008cf0 <+0>: push {r11} ; (str r11, [sp, #-4]!)
0x00008cf4 <+4>: add r11, sp, #0
0x00008cf8 <+8>: push {r6} ; (str r6, [sp, #-4]!)
0x00008cfc <+12>: add r6, pc, #1
0x00008d00 <+16>: bx r6
0x00008d04 <+20>: mov r3, pc
0x00008d06 <+22>: adds r3, #4
0x00008d08 <+24>: push {r3}
0x00008d0a <+26>: pop {pc}
0x00008d0c <+28>: pop {r6} ; (ldr r6, [sp], #4)
0x00008d10 <+32>: mov r0, r3
0x00008d14 <+36>: sub sp, r11, #0
0x00008d18 <+40>: pop {r11} ; (ldr r11, [sp], #4)
0x00008d1c <+44>: bx lr
r6 = 0x8cf8 + 8 + 1
r3 = 0x8d04+4+4
최종적으로 r0에 들어가는 값은 r3이기 때문에 0x8d0c
Dump of assembler code for function key3:
0x00008d20 <+0>: push {r11} ; (str r11, [sp, #-4]!)
0x00008d24 <+4>: add r11, sp, #0
0x00008d28 <+8>: mov r3, lr
0x00008d2c <+12>: mov r0, r3
0x00008d30 <+16>: sub sp, r11, #0
0x00008d34 <+20>: pop {r11} ; (ldr r11, [sp], #4)
0x00008d38 <+24>: bx lr
LR = 함수가 돌아갈 리턴 주소이기 때문에 main 함수를 보면
0x00008d7c <+64>: bl 0x8d20 <key3>
0x00008d80 <+68>: mov r3, r0
0x8d80이다.
셋을 전부 더하면 108400이고, 이를 입력으로 넣어주면 flag를 얻을 수 있다.
key2 함수에서 r6 레지스터의 값을 조작하는 부분이 있는데, 이는 thumb 모드로 전환하면서 branch 대상 주소의 최하위 비트를 1로 세팅해서 CPU에서 thumb 모드로 전환했음을 알리기 위한 신호를 구현하는 과정이다. CPU는 주소에서 lsb가 1인 것으로 모드를 판별하기 때문에 bx 이후가 실질적인 코드이다.
daddy_has_lot_of_ARM_muscl3