본문 바로가기

[Debugging] Tips

[Best][Kernel][Stability] 스택 오염(Stack Corruption) 디버깅

아래 글에서 stack canary에 대한 내용을 다뤘습니다. 스택을 깨는 지 점검하는 루틴인데요.
 
이번에는 다른 디버깅 패치를 작성해서 어떤 루틴이 스택 오염을 시켰는지 점검해보겠습니다.
 
우선 스택이 깨지는 순서를 살펴보겠습니다.
 
1. 아래 함수가 처음 실행될 때 순서로 스택을 푸쉬합니다.
current_sp-0x1c---  R14 // 0xC06FAE8C 실행 시 스택 주소(스택 푸쉬 후)
current_sp-0x18---  R3
current_sp-0x14---  R4 
current_sp-0x10---  R11
current_sp-0xc----  R12
current_sp-0x8----  LR
current_sp-0x4----  PC
current_sp---------      0xC06FAE80 주소에서의 스택 주소
 
해당 코드는 아래와 같습니다.
NSR:C06FAE80|E1A0C00D  touch_irq_handler:  cpy     r12,r13
NSR:C06FAE84|E92DD818                      push    {r3-r4,r11-r12,r14,pc}
NSR:C06FAE88|E24CB004                      sub     r11,r12,#0x4     ; r11,r12,#4
NSR:C06FAE8C|E52DE004                      push    {r14}
 
2. touch_irq_handler() 함수에서 bald_bsp() 란 함수를 호출했다고 가정할께요.
NSR:C06FDE84|E1A0C00D  bald_bsp:  cpy     r12,r13
NSR:C06FDE88|E92DD818                      push    {r3-r4, r14}
 
bald_bsp()의  0xC06FDE88 코드에서 스택 푸쉬를 하고 나면 아래와 같이 스택 푸쉬를 합니다.
current_sp-0x28----  R3  //<<-- bald_bsp()의  0xC06FDE88 코드에서 스택 푸쉬 후 스택 주소
current_sp-0x24----  R4
current_sp-0x20----  R14 // touch_irq_handler에서 bald_bsp()를 호출했으니 touch_irq_handler 함수 내 주소일 것임
current_sp-0x1c---  R14 // 0xC06FAE8C(touch_irq_handler 내) 실행 시 스택 주소(스택 푸쉬 후)
current_sp-0x18---  R3
current_sp-0x14---  R4 
current_sp-0x10---  R11
current_sp-0xc----  R12
current_sp-0x8----  LR
current_sp-0x4----  PC
 
3. bald_bsp() 함수에서 코드를 누군가 잘못 짜서 string copy를 out-of-bound로 처리하면 아래와 같이 스택이 오염됩니다.
current_sp-0x28----  "A"
current_sp-0x24----  "B"
current_sp-0x20---- "C" // R14 // touch_irq_handler에서 bald_bsp()를 호출했으니 touch_irq_handler 함수 내 주소일 것임
current_sp-0x1c---  "D" // 0xC06FAE8C(touch_irq_handler 내) 실행 시 스택 주소(스택 푸쉬 후)
current_sp-0x18---  "E // "R3가 저장된 주소
current_sp-0x14---  "F" // R4 가 저장된 주소 
current_sp-0x10---  R11
current_sp-0xc----  R12
current_sp-0x8----  LR
current_sp-0x4----  PC
 
이제 패치 코드를 작성해 볼 시간입니다. 아래 패치 코드는 touch_irq_handler 함수가 실행되기 직전 스택 주소를 얻어서 스택 덤프를 출력합니다.
diff --git a/drivers/input/touchscreen/touch_core.c b/drivers/input/touchscreen/touch_core.c
index 9cbf6ad..989c6d8 100644
--- a/drivers/input/touchscreen/touch_core.c
+++ b/drivers/input/touchscreen/touch_core.c
@@ -168,10 +168,24 @@ static void touch_core_initialize(struct touch_core_data *ts)
        touch_report_all_event(ts);
 }
 
+#define GET_LR *((unsigned long*)(current_sp+0x20))
+#define ADDRESS_OFFSET 4 // this is 4 bit architecture
+static unsigned int push_stack_size = 10;  // 0x18 in hexa format
+
+static unsigned long stack_array = 0;
+static unsigned long stack_address_offset = 0;
+
 irqreturn_t touch_irq_handler(int irq, void *dev_id)
 {
+       register unsigned long current_sp asm ("sp");
        struct touch_core_data *ts = (struct touch_core_data *) dev_id;
 
+       printk("==== start dump stack dump ==== \n");
+       for( stack_array = 0; stack_array < push_stack_size; stack_array++) {
+               stack_address_offset = stack_array * ADDRESS_OFFSET;
+               printk("[0x%lx] = 0x%lx \n", current_sp + stack_address_offset, *((unsigned long*)(current_sp + stack_address_offset)) );
+       }
+
        bald_bsp();
 
+       printk("==== start dump stack dump ==== \n");
+       for( stack_array = 0; stack_array < push_stack_size; stack_array++) {
+               stack_address_offset = stack_array * ADDRESS_OFFSET;
+               printk("[0x%lx] = 0x%lx \n", current_sp + stack_address_offset, *((unsigned long*)(current_sp + stack_address_offset)) );
+       }
+
 
위 패치의 핵심 코드는 #define GET_LR *((unsigned long*)(current_sp+0x20))인데, 패치 코드를 반영하고 나서 스택 푸쉬되는 바이트를 0x20로 계산했습니다.
   NSR:C06FAE80|E1A0C00D  touch_irq_handler:  cpy     r12,r13
   NSR:C06FAE84|E92DD818                      push    {r3-r4,r11-r12,r14,pc}
   NSR:C06FAE88|E24CB004                      sub     r11,r12,#0x4     ; r11,r12,#4
   NSR:C06FAE8C|E52DE004                      push    {r14}
 
 위 패치를 빌드해서 커널 로그를 받아보면 아래와 같이 bald_bsp() 함수가 호출된 후 스택 덤프가 깨져 있습니다.
<6>[  154.878931 / 02-06 16:59:35.892][0] ==== start dump stack dump ==== 
<6>[  154.878960 / 02-06 16:59:35.892][0] [0xc5233b58] = 0xc06fae80 
<6>[  154.878976 / 02-06 16:59:35.892][0] [0xc5233b5c] = 0xc4226e80 
<6>[  154.878992 / 02-06 16:59:35.892][0] [0xc5233b60] = 0xe1 
<6>[  154.879007 / 02-06 16:59:35.892][0] [0xc5233b64] = 0x0 
<6>[  154.879022 / 02-06 16:59:35.892][0] [0xc5233b68] = 0xc5233bb4 
<6>[  154.879037 / 02-06 16:59:35.892][0] [0xc5233b6c] = 0xc5233b78 
<6>[  154.879052 / 02-06 16:59:35.892][0] [0xc5233b70] = 0xc0085440 
<6>[  154.879067 / 02-06 16:59:35.892][0] [0xc5233b74] = 0xc06fae8c 
<6>[  154.879083 / 02-06 16:59:35.892][0] [0xc5233b78] = 0x20000193 
<6>[  154.879098 / 02-06 16:59:35.892][0] [0xc5233b7c] = 0xc0f72564 
<6>[  154.895630 / 02-06 16:59:35.912][0] ==== start dump stack dump ==== 
<6>[  154.895660 / 02-06 16:59:35.912][0] [0xc5233e58] = 0xA 
<6>[  154.895676 / 02-06 16:59:35.912][0] [0xc5233e5c] = 0xB 
<6>[  154.895691 / 02-06 16:59:35.912][0] [0xc5233e60] = 0xC 
<6>[  154.895706 / 02-06 16:59:35.912][0] [0xc5233e64] = 0xD 
<6>[  154.895723 / 02-06 16:59:35.912][0] [0xc5233e68] = 0xE 
<6>[  154.895740 / 02-06 16:59:35.912][0] [0xc5233e6c] = 0xF 
<6>[  154.895755 / 02-06 16:59:35.912][0] [0xc5233e70] = 0x10 
<6>[  154.895771 / 02-06 16:59:35.912][0] [0xc5233e74] = 0x11 
<6>[  154.895786 / 02-06 16:59:35.912][0] [0xc5233e78] = 0x12 
<6>[  154.895801 / 02-06 16:59:35.912][0] [0xc5233e7c] = 0x13 
 
이런 방식으로 분석하면 어떤 함수가 스택을 깨는 지 점검할 수 있습니다.