Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

230224
1178
109352


ARM64- 스택 푸쉬(Stack Push) Userspace -> Kernel Space [Linux][ARM] Core Analysis

유저 스페이스에서 커널 스페이스로 전환하려면 시스템 콜을 호출해야 한다 것은 마르고 닳도록 들었죠?
이번에는 ARM64 Architecture에서 EL0 ->EL1로 변환될 시, Stack을 어떻게 Push하는 지 살펴볼께요.

"rild"란 프로세스의 콜스택을 예를 들어 볼 께요. 잘 보면, 유저 공간에서 sendto란 시스템 콜을 호출했다는 걸 알 수 있네요.
-000|do_mem_abort()
-001|el1_da(asm)
 -->|exception
-002|ch_pop_remote_rx_intent()
-003|glink_tx_common()
-004|glink_txv()
-005|ipc_router_glink_xprt_write()
-006|msm_ipc_router_write_pkt(inline)
-006|msm_ipc_router_send_to()
-007|msm_ipc_router_sendmsg()
-008|sock_sendmsg_nosec(inline)
-008|sock_sendmsg()
-009|SYSC_sendto(inline)
-009|sys_sendto()
-010|el0_svc_naked(asm)
.
아래는 "rild"란 프로세스의 task descriptor(주소: 0xFFFFFFE577E1A400) 인데요. stack란 멤버가 0xFFFFFFE4DE6A4000을 가르키고 있는데요, 이 주소는 스택의 Top Address를 가르켜요.
  (struct task_struct*)0xFFFFFFE577E1A400 = 0xFFFFFFE577E1A400 -> (
    state = 0x0,
    stack = 0xFFFFFFE4DE6A4000,
    usage = (counter = 0x2),
// .. 생략..
    comm = "rild",
    nameidata = 0x0,
.
리눅스 커널에서 스택은 Bottom Address(높은 주소) 에서 Top Address(낮은 주소로) 자란다는 사실은 다 아실 꺼에요. ARM64(Aarch64) 아키텍처에서는 커널 공간의 프로세스 스택 주소는 0x4000바이트로 이거든요. 그럼 rild"란 프로세스는 (0xFFFFFFE4DE6A4000+0x4000) 에서 0xFFFFFFE4DE6A4000 주소로 스택이 자라난다는 사실을 알 수 있어요. 스택이 자라난다는 소리가 뭐냐구요? 스택 공간에는 로컬 변수를 할당 받아 쓰고 가장 중요한 함수가 호출될 때 돌아갈 주소(Linked Register)와 돌아갈 주소가 있는 주소(Frame Pointer 주소)를 스택에 저장하거든요. 스택에 어떤 값을 푸쉬하면 자연히 스택 주소가 점점 작아져요. 이걸 스택이 자란다고 할 수 있죠. 높은 주소에서 낮은 주소로요. 

만약 A -> B -> C 순서로 함수가 호출되었으면 아래와 같은 심볼 정보 확인을 할 수 있어요. 아래는 정말 쉽게 설명한 도식인데. 나중에 ARM64 아키텍쳐의 Calling Convention은 자세히 다룰 예정이에요.
0xFFFFFFE4DE6A4000   //<<-- 낮은 주소(stack top 주소)
// 생략
0xFFFFFFE4DE6A7FA0       D
0xFFFFFFE4DE6A7FA8 
0xFFFFFFE4DE6A7FB0
0xFFFFFFE4DE6A7FB8       C 
0xFFFFFFE4DE6A7FC0
0xFFFFFFE4DE6A7FC8
0xFFFFFFE4DE6A7FD0       B
0xFFFFFFE4DE6A7FD8 
0xFFFFFFE4DE6A7FE0
0xFFFFFFE4DE6A7FE8
0xFFFFFFE4DE6A7FF0       A
0xFFFFFFE4DE6A7FF8
0xFFFFFFE4DE6A8000     //<<-- 높은 주소(stack bottom 주소)
.
그럼 스택 bottom 주소 0xFFFFFFE4DE6A8000로 가볼까요? 잘 보면 유저 공간에서 구동 중인 레지스터가 스택에 Push된 걸 알 수 있어요.
_____________address|_data____________________|value_____________|symbol
NUD:FFFFFFE4DE6A7E60| 00 00 00 00 00 00 00 00  0x0
NUD:FFFFFFE4DE6A7E68| 30 31 68 99 9F FF FF FF  0xFFFFFF9F99683130 \\vmlinux\Global\el0_svc_naked+0x24
NUD:FFFFFFE4DE6A7E70| 00 00 00 00 00 00 00 00  0x0
NUD:FFFFFFE4DE6A7E78| 28 DF FD AF 7B 00 00 00  0x7BAFFDDF28
NUD:FFFFFFE4DE6A7E80| FF FF FF FF FF FF FF FF  0xFFFFFFFFFFFFFFFF
NUD:FFFFFFE4DE6A7E88| 30 31 68 99 9F FF FF FF  0xFFFFFF9F99683130 \\vmlinux\Global\el0_svc_naked+0x24
NUD:FFFFFFE4DE6A7E90| 00 00 00 00 00 00 00 00  0x0
NUD:FFFFFFE4DE6A7E98| 08 00 00 00 00 00 00 00  0x8
NUD:FFFFFFE4DE6A7EA0| FF FF FF FF FF FF FF FF  0xFFFFFFFFFFFFFFFF
NUD:FFFFFFE4DE6A7EA8| 00 00 00 00 00 00 00 00  0x0
NUD:FFFFFFE4DE6A7EB0| 00 00 00 A0 00 00 00 00  0xA0000000
NUD:FFFFFFE4DE6A7EB8| 3C 6B 77 2B 46 76 A8 C2  0xC2A876462B776B3C
NUD:FFFFFFE4DE6A7EC0| 45 00 00 00 00 00 00 00  0x45        // x0 
NUD:FFFFFFE4DE6A7EC8| 80 37 81 AF 7B 00 00 00  0x7BAF813780    // x1
NUD:FFFFFFE4DE6A7ED0| 16 00 00 00 00 00 00 00  0x16       // x2
NUD:FFFFFFE4DE6A7ED8| 40 00 00 00 00 00 00 00  0x40       // x3
NUD:FFFFFFE4DE6A7EE0| 20 DD FD AF 7B 00 00 00  0x7BAFFDDD20   // x4
NUD:FFFFFFE4DE6A7EE8| 14 00 00 00 00 00 00 00  0x14       // x5
NUD:FFFFFFE4DE6A7EF0| C8 DC FD AF 7B 00 00 00  0x7BAFFDDCC8  // x6
NUD:FFFFFFE4DE6A7EF8| 01 00 00 00 00 00 00 00  0x1     // x7
NUD:FFFFFFE4DE6A7F00| CE 00 00 00 00 00 00 00  0xCE     // x8
NUD:FFFFFFE4DE6A7F08| 02 00 00 00 00 00 00 00  0x2   // x9
NUD:FFFFFFE4DE6A7F10| 00 00 00 00 00 00 00 00  0x0   // x10
NUD:FFFFFFE4DE6A7F18| 00 00 00 00 00 00 00 00  0x0   // x11
NUD:FFFFFFE4DE6A7F20| B9 E6 3B AF 7B 00 00 00  0x7BAF3BE6B9  // x12
NUD:FFFFFFE4DE6A7F28| 40 01 00 00 00 00 00 00  0x140    // x13
NUD:FFFFFFE4DE6A7F30| 0D 00 00 00 00 00 00 00  0x0D   // x14
NUD:FFFFFFE4DE6A7F38| AB AA AA AA AA AA AA AA  0xAAAAAAAAAAAAAAAB // x15
NUD:FFFFFFE4DE6A7F40| 98 CF E2 B1 7B 00 00 00  0x7BB1E2CF98 // x16
NUD:FFFFFFE4DE6A7F48| 14 B3 AB B2 7B 00 00 00  0x7BB2ABB314 // x17
NUD:FFFFFFE4DE6A7F50| D6 00 00 00 00 00 00 00  0xD6       // x18
NUD:FFFFFFE4DE6A7F58| 78 DD FD AF 7B 00 00 00  0x7BAFFDDD78  // x19
NUD:FFFFFFE4DE6A7F60| C0 72 81 AF 7B 00 00 00  0x7BAF8172C0   // x20
NUD:FFFFFFE4DE6A7F68| 16 00 00 00 00 00 00 00  0x16       // x21
NUD:FFFFFFE4DE6A7F70| 28 8D E2 B1 7B 00 00 00  0x7BB1E28D28   // x22
NUD:FFFFFFE4DE6A7F78| C0 83 84 AF 7B 00 00 00  0x7BAF8483C0  // x23
NUD:FFFFFFE4DE6A7F80| 70 83 84 AF 7B 00 00 00  0x7BAF848370  // x24
NUD:FFFFFFE4DE6A7F88| B8 83 84 AF 7B 00 00 00  0x7BAF8483B8 // x25
NUD:FFFFFFE4DE6A7F90| 80 D7 80 AF 7B 00 00 00  0x7BAF80D780 // x26
NUD:FFFFFFE4DE6A7F98| 10 B0 90 B1 7B 00 00 00  0x7BB190B010 // x27
NUD:FFFFFFE4DE6A7FA0| 17 4A A6 B1 7B 00 00 00  0x7BB1A64A17 // x28
NUD:FFFFFFE4DE6A7FA8| 10 DD FD AF 7B 00 00 00  0x7BAFFDDD10 // x29
NUD:FFFFFFE4DE6A7FB0| 30 B3 AB B2 7B 00 00 00  0x7BB2ABB330  // x30
NUD:FFFFFFE4DE6A7FB8| 10 DD FD AF 7B 00 00 00  0x7BAFFDDD10  // sp
NUD:FFFFFFE4DE6A7FC0| C4 B7 AF B2 7B 00 00 00  0x7BB2AFB7C4  // pc
NUD:FFFFFFE4DE6A7FC8| 00 00 00 60 00 00 00 00  0x60000000 // cprs
NUD:FFFFFFE4DE6A7FD0| 45 00 00 00 00 00 00 00  0x45
NUD:FFFFFFE4DE6A7FD8| CE 00 00 00 00 00 00 00  0xCE
NUD:FFFFFFE4DE6A7FE0| 00 00 00 00 00 00 00 00  0x0
NUD:FFFFFFE4DE6A7FE8| 00 00 00 00 00 00 00 00  0x0
NUD:FFFFFFE4DE6A7FF0| C8 C3 39 00 EA 71 00 00  0x71EA0039C3C8
NUD:FFFFFFE4DE6A7FF8| 13 00 00 00 00 00 00 00  0x13
NUD:FFFFFFE4DE6A8000| 00 00 00 00 00 00 00 00  0x0
NUD:FFFFFFE4DE6A8008| 00 00 00 00 00 00 00 00  0x0

여기서 아주 주의 깊게 쳐야 볼 레지스터는 X8인데요. 0xCE란 값을 가르키고 있네요. 이게 뭐냐면 시스템 콜 번호이거든요. 시스템 콜 번호는 CrashTool로 확인이 가능해요. 자 확인해 볼까요? sys_sendto란 시스템 콜의 번호는 0xce임을 알 수 있어요.
crash64> sys -c
NUM  SYSTEM CALL                FILE AND LINE NUMBER
  0  sys_io_setup               ../kernel/fs/aio.c: 1257
// .. 생략
 cd  sys_getpeername            ../kernel/net/socket.c: 1794
 ce  sys_sendto                 ../kernel/net/socket.c: 1826
 cf  sys_recvfrom               ../kernel/net/socket.c: 1889


Reference(프로세스 관리)
4.9 프로세스 컨택스트 정보는 어떻게 저장할까?
 4.9.1 컨택스트 소개
 4.9.2 인터럽트 컨택스트 정보 확인하기
 4.9.3 Soft IRQ 컨택스트 정보 확인하기
 4.9.4 선점 스케줄링 여부 정보 저장
4.10 프로세스 디스크립터 접근 매크로 함수
 4.10.1 current_thread_info()
 4.10.2 current 매크로란
4.11 프로세스 디버깅
 4.11.1 glibc fork 함수 gdb 디버깅
 4.11.2 유저 프로그램 실행 추적 

핑백

덧글

댓글 입력 영역