Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

230224
1178
109352


ARM64(Aarch64) - 함수 호출시 Stack Push(스택 푸쉬) 규약 [Linux][ARM] Core Analysis

아래와 같은 콜 스택에서 유저 공간에서 돌던 레지스터 Stack Push와 Exception 발생 시 Stack Push에 대해서 살펴봤어요. 이제는 평상시 함수 호출 시 어떻게 Stack Push를 하는 지 점검하려고 해요.

자 계속 그 동안 다뤄왔던 아래 "rild"란 프로세스의 콜스택에서 msm_ipc_router_sendmsg()-> msm_ipc_router_send_to() 으로 함수가 호출된 후 스택 푸쉬가 어떻게 수행되는 지 점검해볼께요.
-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)

msm_ipc_router_sendmsg()함수 내에서 msm_ipc_router_send_to() 함수를 호출하기 직전 ARM Instruction을 살펴 볼께요. 0xFFFFFF9F9A81C2A4주소가 msm_ipc_router_send_to() 함수를 호출하거든요. 
이 때 스택 주소(SP)는  0xFFFFFFE4DE6A7CA0이에요. 

아래 코드를 보면 각각 파라미터로 입력하는 값들을 확인할 수 있는데요.
x0 = x23, x1 = x20, x3 = x29+0x88, x3 = x28
NSX:FFFFFF9F9A81C294|AA1703E0            mov     x0,x23
NSX:FFFFFF9F9A81C298|AA1403E1            mov     x1,x20
NSX:FFFFFF9F9A81C29C|910223A2            add     x2,x29,#0x88     ; x2,x29,#136
NSX:FFFFFF9F9A81C2A0|AA1C03E3            mov     x3,x28
NSX:FFFFFF9F9A81C2A4|97FFF7A3            bl      0xFFFFFF9F9A81A130   ; msm_ipc_router_send_to

자 이제, msm_ipc_router_send_to() 함수 호출이 되는 순간 어떤 짓을 할까요? 좀 살펴 볼께요.
NSX:FFFFFF9F9A81A130|A9B67BFD  msm_ipc_router_send_to: stp     x29,x30,[SP,#-0xA0]!   ; x29,x30,[SP,#-160]!  //<<--[1]
NSX:FFFFFF9F9A81A134|910003FD                          mov     x29,SP
NSX:FFFFFF9F9A81A138|A90153F3                          stp     x19,x20,[SP,#0x10]   ; x19,x20,[SP,#16] //<<--[2]
NSX:FFFFFF9F9A81A13C|A9025BF5                          stp     x21,x22,[SP,#0x20]   ; x21,x22,[SP,#32]//<<--[3]
NSX:FFFFFF9F9A81A140|A90363F7                          stp     x23,x24,[SP,#0x30]   ; x23,x24,[SP,#48]
NSX:FFFFFF9F9A81A144|A9046BF9                          stp     x25,x26,[SP,#0x40]   ; x25,x26,[SP,#64]
NSX:FFFFFF9F9A81A148|A90573FB                          stp     x27,x28,[SP,#0x50]   ; x27,x28,[SP,#80]//<<--[4]

[1] "stp     x29,x30,[SP,#-0xA0]!"
이게 무슨 명령어이냐면, 스택 공간을 더 잡아주는 명령어에요. 이 명령어가 수행되면 스택 주소가 FFFFFFE4DE6A7C00로 바뀌게 되죠.
어떻게 변경되냐구요?  FFFFFFE4DE6A7C00=FFFFFFE4DE6A7CA0(기존스택주소)- 0xA0

그리고, 동시에 x29, x30을 스택 주소와 스택 주소+0x8 위치에 Push를 해줘요.
NSD:FFFFFFE4DE6A7C00| A0 7C 6A DE E4 FF FF FF  0xFFFFFFE4DE6A7CA0  //<<--x29, 새로운 스택 주소
NSD:FFFFFFE4DE6A7C08| A8 C2 21 09 80 FF FF FF  0xFFFFFF800921C2A8  \\vmlinux\ipc_router_socket\msm_ipc_router_sendmsg+0x2C0  // <<--x30
NSD:FFFFFFE4DE6A7C10| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C18| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C20| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C28| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C30| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C38| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C40| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C48| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C50| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C58| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C60| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C68| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C70| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C78| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C80| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C88| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C90| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C98| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7CA0| 40 7D 6A DE E4 FF FF FF  0xFFFFFFE4DE6A7D40 //<<-- 기존 스택 주소
NSD:FFFFFFE4DE6A7CA8| 88 A0 5E 9A 9F FF FF FF  0xFFFFFF9F9A5EA088 \\vmlinux\socket\sock_sendmsg+0x48

[2] "stp     x19,x20,[SP,#0x10]   ; x19,x20,[SP,#16]"
스택 주소 FFFFFFE4DE6A7C00에서 0x10만큼 떨어진 주소에 x19와 x20을 푸쉬하네요.
NSD:FFFFFFE4DE6A7BF8| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C00| A0 7C 6A DE E4 FF FF FF  0xFFFFFFE4DE6A7CA0 // 스택 주소
NSD:FFFFFFE4DE6A7C08| A8 C2 21 09 80 FF FF FF  0xFFFFFF800921C2A8  \\vmlinux\ipc_router_socket\msm_ipc_router_sendmsg+0x2C0
NSD:FFFFFFE4DE6A7C10| 19 00 00 00 00 00 00 00  0x0          // <<--x19
NSD:FFFFFFE4DE6A7C18| 80 8E 96 DF E4 FF FF FF  0xFFFFFFE4DF968E80 //<<--x20
NSD:FFFFFFE4DE6A7C20| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C28| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C30| 00 00 00 00 00 00 00 00  0x0

[3] "stp     x21,x22,[SP,#0x20]   ; x21,x22,[SP,#32]"
스택 주소 FFFFFFE4DE6A7C00에서 0x20만큼 떨어진 주소에 x21와 x22을 푸쉬하네요.
NSD:FFFFFFE4DE6A7C00| A0 7C 6A DE E4 FF FF FF  0xFFFFFFE4DE6A7CA0
NSD:FFFFFFE4DE6A7C08| A8 C2 21 09 80 FF FF FF  0xFFFFFF800921C2A8  \\vmlinux\ipc_router_socket\msm_ipc_router_sendmsg+0x2C0
NSD:FFFFFFE4DE6A7C10| 19 00 00 00 00 00 00 00  0x19                \\vmlinux\Global\sha1_ce_offsetof_count+0x1
NSD:FFFFFFE4DE6A7C18| 80 8E 96 DF E4 FF FF FF  0xFFFFFFE4DF968E80
NSD:FFFFFFE4DE6A7C20| 00 15 C6 4F E5 FF FF FF  0xFFFFFFE54FC61500 //<<--x21
NSD:FFFFFFE4DE6A7C28| 80 D0 F4 56 E5 FF FF FF  0xFFFFFFE556F4D080 //<<--x22

[4] 이제 스택 푸쉬가 끝났어요. 어떤 값들인지 비교해볼까요?
[After]
NSD:FFFFFFE4DE6A7C00| A0 7C 6A DE E4 FF FF FF  0xFFFFFFE4DE6A7CA0   //<<--x29, 새로운 스택 주소
NSD:FFFFFFE4DE6A7C08| A8 C2 81 9A 9F FF FF FF  0xFFFFFF9F9A81C2A8 .vmlinux\ipc_router_socket\msm_ipc_router_sendmsg+0x2C0 //<<--x30 
NSD:FFFFFFE4DE6A7C10| 00 00 00 00 00 00 00 00  0x0   //<<-- x19
NSD:FFFFFFE4DE6A7C18| 80 8E 96 DF E4 FF FF FF  0xFFFFFFE4DF968E80 //<-- x20
NSD:FFFFFFE4DE6A7C20| 00 15 C6 4F E5 FF FF FF  0xFFFFFFE54FC61500 //<-- x21
NSD:FFFFFFE4DE6A7C28| 80 D0 F4 56 E5 FF FF FF  0xFFFFFFE556F4D080 //<-- x22
NSD:FFFFFFE4DE6A7C30| 00 70 C6 4F E5 FF FF FF  0xFFFFFFE54FC67000 //<-- x23
NSD:FFFFFFE4DE6A7C38| 16 00 00 00 00 00 00 00  0x16                    //<-- x24
NSD:FFFFFFE4DE6A7C40| 38 00 00 00 00 00 00 00  0x38                      //<-- x25
NSD:FFFFFFE4DE6A7C48| 16 00 00 00 00 00 00 00  0x16         //<-- x26
NSD:FFFFFFE4DE6A7C50| 02 00 00 00 00 00 00 00  0x2         //<-- x27
NSD:FFFFFFE4DE6A7C58| 00 00 00 00 00 00 00 00  0x0         //<-- x28
NSD:FFFFFFE4DE6A7C60| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C68| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C70| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C78| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C80| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C88| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C90| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C98| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7CA0| 40 7D 6A DE E4 FF FF FF  0xFFFFFFE4DE6A7D40 //<<-- 기존 스택 주소
NSD:FFFFFFE4DE6A7CA8| 88 A0 5E 9A 9F FF FF FF  0xFFFFFF9F9A5EA088 \\vmlinux\socket\sock_sendmsg+0x48

[Before]
NSD:FFFFFFE4DE6A7C00| A0 7C 6A DE E4 FF FF FF  0xFFFFFFE4DE6A7CA0  //<<--x29, 새로운 스택 주소
NSD:FFFFFFE4DE6A7C08| A8 C2 21 09 80 FF FF FF  0xFFFFFF800921C2A8  \\vmlinux\ipc_router_socket\msm_ipc_router_sendmsg+0x2C0  // <<--x30
NSD:FFFFFFE4DE6A7C10| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C18| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C20| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C28| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C30| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C38| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C40| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C48| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C50| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C58| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C60| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C68| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C70| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C78| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C80| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C88| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C90| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7C98| 00 00 00 00 00 00 00 00  0x0
NSD:FFFFFFE4DE6A7CA0| 40 7D 6A DE E4 FF FF FF  0xFFFFFFE4DE6A7D40 //<<-- 기존 스택 주소
NSD:FFFFFFE4DE6A7CA8| 88 A0 5E 9A 9F FF FF FF  0xFFFFFF9F9A5EA088 \\vmlinux\socket\sock_sendmsg+0x48

자 다시, msm_ipc_router_send_to() 함수를 호출하기 직전 FFFFFF9F9A81C2A4 주소로 되돌아 가 볼께요.
아래와 같이 파라미터를 저장했지오.
x0 = x23, x1 = x20, x3 = x29+0x88, x3 = x28
NSX:FFFFFF9F9A81C294|AA1703E0            mov     x0,x23
NSX:FFFFFF9F9A81C298|AA1403E1            mov     x1,x20
NSX:FFFFFF9F9A81C29C|910223A2            add     x2,x29,#0x88     ; x2,x29,#136
NSX:FFFFFF9F9A81C2A0|AA1C03E3            mov     x3,x28
NSX:FFFFFF9F9A81C2A4|97FFF7A3            bl      0xFFFFFF9F9A81A130   ; msm_ipc_router_send_to

그리고 msm_ipc_router_send_to() 함수 호출 후 스택에 푸쉬된 값을 보면, x23은 0xFFFFFFE54FC67000 x20은 0xFFFFFFE4DF968E80 이거든요.
그런데 이 x23은 x0즉 첫 번 째 파라미터로 전달을 하고,  x20은 두 번 째 파라미터로 전달을 했죠.
NSD:FFFFFFE4DE6A7C00| A0 7C 6A DE E4 FF FF FF  0xFFFFFFE4DE6A7CA0   //<<--x29, 새로운 스택 주소
NSD:FFFFFFE4DE6A7C08| A8 C2 81 9A 9F FF FF FF  0xFFFFFF9F9A81C2A8 .vmlinux\ipc_router_socket\msm_ipc_router_sendmsg+0x2C0 //<<--x30 
NSD:FFFFFFE4DE6A7C10| 00 00 00 00 00 00 00 00  0x0   //<<-- x19
NSD:FFFFFFE4DE6A7C18| 80 8E 96 DF E4 FF FF FF  0xFFFFFFE4DF968E80 //<-- x20
NSD:FFFFFFE4DE6A7C20| 00 15 C6 4F E5 FF FF FF  0xFFFFFFE54FC61500 //<-- x21
NSD:FFFFFFE4DE6A7C28| 80 D0 F4 56 E5 FF FF FF  0xFFFFFFE556F4D080 //<-- x22
NSD:FFFFFFE4DE6A7C30| 00 70 C6 4F E5 FF FF FF  0xFFFFFFE54FC67000 //<-- x23
NSD:FFFFFFE4DE6A7C38| 16 00 00 00 00 00 00 00  0x16                    //<-- x24
NSD:FFFFFFE4DE6A7C40| 38 00 00 00 00 00 00 00  0x38                      //<-- x25
NSD:FFFFFFE4DE6A7C48| 16 00 00 00 00 00 00 00  0x16         //<-- x26
NSD:FFFFFFE4DE6A7C50| 02 00 00 00 00 00 00 00  0x2         //<-- x27
NSD:FFFFFFE4DE6A7C58| 00 00 00 00 00 00 00 00  0x0         //<-- x28

이제 함수 호출 시 스택 푸쉬가 되는 규칙을 활용해서 디버깅을 좀 해볼까 해요.

이제 소스 코드를 열어 보아야 할 것 같은데요.
msm_ipc_router_send_to() 함수 호출 시 전달되는 파라미터의 타입을 좀 더 자세히 알 수 있네요. 정리하면..
x0(x23) = 0xFFFFFFE54FC67000 = struct msm_ipc_port *src, x1(x20) =  0xFFFFFFE4DF968E80 = struct sk_buff_head *out_skb_head
int msm_ipc_router_send_msg(struct msm_ipc_port *src,
       struct msm_ipc_addr *dest,
       void *data, unsigned int data_len)
{
 struct sk_buff_head *out_skb_head;
 int ret;

 out_skb_head = msm_ipc_router_buf_to_skb(data, data_len);
//.. 생략..
 ret = msm_ipc_router_send_to(src, out_skb_head, dest, 0);

그럼 전달되는 파라미터는 확인해보면 아래와 같아요.
첫번 째 파라미터 (struct msm_ipc_port*)
v.v %s %y %t (struct msm_ipc_port*)0xFFFFFFE54FC67000
  (struct msm_ipc_port *) (struct msm_ipc_port*)0xFFFFFFE54FC67000 = 0xFFFFFFE54FC67000 =  -> (
    (struct list_head) list = (
      (struct list_head *) next = 0xFFFFFF9F9C19D1E0 = local_ports[5],
      (struct list_head *) prev = 0xFFFFFFE5775EC200 = ),
    (struct kref) ref = ((atomic_t) refcount = ((int) counter = 1)),
    (struct msm_ipc_port_addr) this_port = ((uint32_t) node_id = 1, (uint32_t) port_id = 197),
    (struct msm_ipc_port_name) port_name = ((uint32_t) service = 0, (uint32_t) instance = 0),
    (uint32_t) type = 0,
    (unsigned int) flags = 0,
    (struct mutex) port_lock_lhc3 = ((atomic_t) count = ((int) counter = 1), (spinlock_t) wait_lock
    (struct comm_mode_info) mode_info = ((int) mode = 0, (void *) xprt_info = 0x0 = ),
    (struct msm_ipc_port_addr) dest_addr = ((uint32_t) node_id = 0, (uint32_t) port_id = 0),
    (int) conn_status = 0,
    (struct list_head) port_rx_q = ((struct list_head *) next = 0xFFFFFFE54FC67078 = , (struct list_
    (struct mutex) port_rx_q_lock_lhc3 = ((atomic_t) count = ((int) counter = 1), (spinlock_t) wait_
    (char [32]) rx_ws_name = "ipc000000c5_1490_rild",
    (struct wakeup_source *) port_rx_ws = 0xFFFFFFE54FC60100 = ,

두번 째 파라미터 (struct sk_buff_head*)
v.v %y %s %h (struct sk_buff_head*)0xFFFFFFE4DF968E80
  (struct sk_buff_head*)0xFFFFFFE4DF968E80 = 0xFFFFFFE4DF968E80 =  -> (
    next = 0xFFFFFFE54FC61500 = ,
    prev = 0xFFFFFFE54FC61500 = ,
    qlen = 0x1,
    lock = (rlock = (raw_lock = (owner = 0x1, next = 0x1))))

이런 방식으로 디버깅을 할 수 있어요. 이게 모두 ARM64 아키텍쳐에서 함수호출 시 스택 Push가 어떻게 수행되는지 알았기 때문에 가능한 것이지오. 참 쉽죠?



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 유저 프로그램 실행 추적 


덧글

댓글 입력 영역