환경 구축 후, 간단하게 ARM 어셈블리를 살펴보려 한다.
자료를 보니... 백날 봐봤자 지루하기만해서 간단한 프로그램을 어셈으로 보면서 스택, 함수 호출, 리턴 등 관련해서 살펴본다.
사용 TOOL : GDB, IDA 32bit
richong@ubuntu:~$ file HAND
HAND: ELF 32-bit LSB executable, ARM, version 1, statically linked, for GNU/Linux 2.6.8, not stripped
[실행 화면]
기본적인 어셈 명령어 및 레지스터
R0 - R10 : 범용 레지스터, 임시 저장 용
R11 : Current SFP(Stack Frame Pointer)
R12 : ARM Mode 와 Thumb Mode 전환 필요 할 때, 접근 가능 range 벗어날 때[쉽게 fall jmp] 때 사용
추후 좀 더 자세히 Thumb Mode : 16bit, ARM Mode는 32bit Mode
R13 : Stack Pointer(SP)
R14 : Link Register(LR) Func call 했을 때 ret 저장 레지스터
R15 : Program Counter(PC), 다음 실행될 명령어 주소
ADD, SUB, AND, MOV, CMP : Intel과 유사
ADD R0, R1, R2 --> R0 = R1+R2
SUB R0, R1, R2 --> R0 = R1 - R2
B : Branch, ARM 환경에서 JMP/CALL과 같다.
BL Address : LR에 돌아 올 장소 저장 후, Address로 이동
BX R0 : R0 레지스터의 값으로 이동
BLX R0 : Thumb mode로 전호나하고 R0 이동
LDR, STR : 메모리에 값을 저장, read
LDR R1, [R2, R3] = R2+R3에서 Word만큼 읽어서 R1에 저장
R1이 100번지, R2가 200번지, 300번지 값이 7
300번지의 값인 7을 읽어서 R1에 저장
STR R1, [R2,R3]
위의 예시를 사용해서 300번지에 R1의 값을 저장
STMFD, LDMFD
STMFD sp!, {r4,r6, lr} --> r4,r6,lr을 스택에 저장하고 sp 감소
LDMFD sp!, {r4,r6,pc} --> 스택에서 r4,r6 pc 복원하고 sp 증가
--------------------------------------------------------- ARM 명령어 설명 추가 ---------------------------------------------------------
ARM 명령어 정리
명령어에 붙어있는 ! [느낌표]
값의 변경을 유지
이렇게 이론적으로 정리하고 분석
먼저 IDA를 통해서 첫부분을 보았다.
.text:000104B0 STMFD SP!, {R11,LR}
.text:000104B4 ADD R11, SP, #4
.text:000104B8 SUB SP, SP, #0x10
.text:000104BC MOV R3, #4
.text:000104C0 STR R3, [R11,#var_8]
.text:000104C4 MOV R3, #3
.text:000104C8 STR R3, [R11,#var_C]
.text:000104CC MOV R3, #0
.text:000104D0 STR R3, [R11,#var_10]
.text:000104D4 LDR R0, [R11,#var_8]
.text:000104D8 LDR R1, [R11,#var_C]
.text:000104DC BL add
.text:000104E0 STR R0, [R11,#var_14]
.text:000104E4 MOV R0, #0x24 ; size
.text:000104E8 BL malloc
.text:000104EC MOV R3, R0
.text:000104F0 STR R3, [R11,#var_10]
.text:000104F4 LDR R3, [R11,#var_10]
.text:000104F8 LDR R1, =aRichong ; "richong"
.text:000104FC MOV R2, R3
.text:00010500 MOV R3, R1
.text:00010504 LDMIA R3!, {R0,R1} ; "richong"
.text:00010508 STR R0, [R2]
.text:0001050C STR R1, [R2,#4]
.text:00010510 LDR R3, [R11,#var_10]
.text:00010514 MOV R2, #0x17
.text:00010518 STR R2, [R3,#0x14]
.text:0001051C LDR R3, [R11,#var_10]
.text:00010520 ADD R3, R3, #0x18
.text:00010524 LDR R2, =a01023456789 ; "01023456789"
.text:00010528 MOV R12, R3
.text:0001052C MOV R3, R2
.text:00010530 LDMIA R3!, {R0-R2} ; "01023456789"
.text:00010534 STR R0, [R12]
.text:00010538 STR R1, [R12,#4]
.text:0001053C STR R2, [R12,#8]
.text:00010540 LDR R0, =aHelloArm ; "Hello ARM!!"
.text:00010544 BL puts
.text:00010548 LDR R2, [R11,#var_8]
.text:0001054C LDR R3, [R11,#var_C]
.text:00010550 ADD R3, R2, R3
.text:00010554 LDR R0, =aDDD ; "%d + %d = %d\n"
.text:00010558 LDR R1, [R11,#var_8]
.text:0001055C LDR R2, [R11,#var_C]
.text:00010560 BL printf
.text:00010564 LDR R1, [R11,#var_10]
.text:00010568 LDR R3, [R11,#var_10]
.text:0001056C LDR R2, [R3,#0x14]
.text:00010570 LDR R3, [R11,#var_10]
.text:00010574 ADD R3, R3, #0x18
.text:00010578 LDR R0, =aNameSAgeDCellp ; "name : %s \n age : %d \n cellphone : %s"...
.text:0001057C BL printf
.text:00010580 MOV R3, #0
.text:00010584 MOV R0, R3
.text:00010588 SUB SP, R11, #4
.text:0001058C LDMFD SP!, {R11,PC}
.text:0001058C ; End of function main
push {r11, lr}
위의 명령이 실행이 되고 스택의 모습
sp-0x4에 존재하는 r11은 main 함수를 호출한 부분의 sp이다.
lr은 ret 주소이다.
add r11, sp, #4
해당 명령어에 의해서 r11에는 sp +0x4의 값이 들어가게 된다.
r11 == sp+0x4
sub sp, sp, #16
sp를 16바이트 만큼 옮겨서 해당 main 함수에서 사용 할 변수의 크기를 할당한다.
아마 0x10[16] 만큼의 공간을 예상 할 수있다.
위와 같이 Frame을 만든다음 r11 레지스터를 이용해서 변수들에 접근을 한다.
MOV R3, #4 //R3 = 4
STR R3, [R11,#var_8] // [R11-8] = R3 = 4
MOV R3, #3 // R3 = 3
STR R3, [R11,#var_C] // [R11-0xc] = R3 = 3
int a = 4;
int b = 3;
R3에 값을 저장하고, R11을 이용해서 변수의 값을 셋팅한다.
LDR R0, [R11,#var_8] // R0 = [R11-8]
LDR R1, [R11,#var_C] // R1 = [R11-1212]
BL add // call add
레지스터를 이용해서 인자 값을 전달한다.
R0에 첫번째 인자를 넣고, 그다음 R1 인자를 전달한다.
add(b,a);
add 함수를 살펴본다.
r11 레지스터를 스택에 저장하고 새로 사용 할 r11을 설정한다. --> add 함수의 frame을 만드는 것
그리고 sp를 12만큼 할당한다.
r11-8 위치에 첫번째 인자인 r0 값을 넣는다.
r11-12 위치에 두번째 인자인 r1의 값을 넣는다.
그리고 r2, r3에 해당 값들을 복사 한후
r3 = r2 + r3를해서 최종 결과 값을 r3에 넣는다.
그리고 return 값을 저장하는 r0에 r3 값을 넣은 후, 해당 함수의 frame을 정리하고
bx lr로 main 함수로 복귀한다.
STR R0, [R11,#var_14]
MOV R0, #0x24 ; size
BL malloc
MOV R3, R0
STR R3, [R11,#var_10]
이후, add 함수의 결과 값을 R11-14의 변수에 저장한다. [R11-14] = R0
그리고 malloc을 이용해서 사이즈 0x24만큼 Heap영역을 할당하고 해당 heap 영역의 pointer 값을
R11-var10에 저장한다.
LDR R3, [R11,#var_10] //Heap Pointer를 R3에 넣음
LDR R1, =aRichong ; "richong" // R1에 Richong 문자열의 pointer 넣음
MOV R2, R3 // R2 = Heap Pointer
MOV R3, R1 // R3 = Richong 문자열 pointer
LDMIA R3!, {R0,R1} ; "richong" // word 단위로 옮기기 때문에 rich은 R0에 ong은 R1에 저장
STR R0, [R2] //R2는 heap pointer 구조체의 첫 주소에 rich 넣는다.
STR R1, [R2,#4] //R1는 heap pointer-4 구조체 영역부터 ong을 넣는다.
위의 명령을 실행 하면서 r0와 r1 값을 보면 실제로 Rich와 ong 문자열이 들어있는 것을 알 수있다.
LDMIA 명령어는 R3에 시작하는 주소로 여러개의 레지스터에 값을 저장 할 때 사용된다.
LDR R3, [R11,#var_10] // Heap Poiner
MOV R2, #0x17 // r2 = 0x17
STR R2, [R3,#0x14] // heap pointer +0x14에 0x17 저장
LDR R3, [R11,#var_10] //Heap Pointer
ADD R3, R3, #0x18 // 구조체의 +18 번째 heap pointer 설정
LDR R2, =a01023456789 ; "01023456789" // r2에 010~ pointer
MOV R12, R3 // 구조체 포인터 r12에
MOV R3, R2 // R3에 010 POINTER
LDMIA R3!, {R0-R2} ; "01023456789" //R0, R1, R2에 01023465789를 넣는다.
STR R0, [R12] // 구조체 포인터에 차레대로 넣는다.
STR R1, [R12,#4] // ==
STR R2, [R12,#8] // ==
위와 같이 구조체를 할당하고 각 내용을 할당 하는 것을 보았다.
그리고 밑에 부분은 print하는 부분이다.
함수 호출 하는 방법과 ret, sfp, struct 이용 관련 어셈을 공부
'reversing > reversing' 카테고리의 다른 글
MIPS Disassemble (0) | 2017.03.30 |
---|---|
MIPS Instruction[Assembly] (0) | 2017.03.29 |
C++ 리버싱 연습 (0) | 2016.11.29 |
get physical address (0) | 2016.11.29 |
call[opcode 0xe8] 사용 (0) | 2016.11.20 |