본문 바로가기

Programming

Hook Disclosure with Timing Attack

1. 개요


악성코드가 자신이 사용하는 OS 의 시스템 콜이 후킹이 된 상태인지 아닌지를 시스템콜 수행에 걸리는 시간을 보고 간파할 수 있는지, 가능하다면 후킹된 함수가 어느정도의 추가 시간지연을 해야 하는지를 각종 상황과 환경별로 실험하여 확인 하고자 함.




2. 실험환경


- 실험을 수행한 물리적 기계의 CPU 스펙은 다음과 같다.






- 실험용 OS 를 작동하는 가상머신의 스펙은 다음과 같다.



- 실험을 수행한 OS 의 스펙은 다음과 같다.






- 시스템콜 시간 측정용 프로그램 소스코드는 다음과 같다 [app.c]


#include <stdio.h>

#include <unistd.h> #include <sys/syscall.h> #include <sys/types.h>

#define CPUHZ (2.8*1024*1024*1024) // 2.8GHz

static inline void rdtsc( unsigned int * upper, unsigned int * lower )

{

   asm volatile( "rdtsc"

                 : "=a"(*lower), "=d"(*upper) );

}


int main(){

unsigned int eax, edx, eax2, edx2;

double delta=0;


// time check

rdtsc( &edx, &eax );


// system call

syscall( 24 );


// time check2

rdtsc( &edx2, &eax2 );


// calculate time in seconds

delta = (double)(eax2-eax) / (double)CPUHZ;


// time consumed.

printf("time delta(msec, usec): %lf, %lf\n", delta, delta*1000.0);

return 0;

}







- 시스템콜 후킹을 수행한 LKM 소스는 다음과 같다.


#include <linux/kernel.h>

#include <linux/mm.h>

#include <linux/module.h>

#include <linux/init.h>

#include <asm/io.h>

#include <linux/sched.h>


#define SCNUM 5    // system call number to hook


int* sct = 0xc0593150;


int (*O)(int, int, int, int, int, int);

int H(unsigned int a, unsigned int b, unsigned int c, unsigned int d, unsigned int e, unsigned int f)

{

printk("system call table hook.\n");

return O(a, b, c, d, e, f);

}


int my_init(void)

{


asm("push %eax");

asm("mov %cr0, %eax");

asm("and $0xFFFEFFFF, %eax");

asm("mov %eax, %cr0");

asm("pop %eax");


O = sct[SCNUM];

sct[SCNUM] = H;


asm("push %eax");

asm("mov %cr0, %eax");

asm("or $0x10000, %eax");

asm("mov %eax, %cr0");

asm("pop %eax");


printk("try...\n");


return 0;

}


void my_exit(void)

{

asm("push %eax");

asm("mov %cr0, %eax");

asm("and $0xFFFEFFFF, %eax");

asm("mov %eax, %cr0");

asm("pop %eax");


sct[SCNUM] = O;


asm("push %eax");

asm("mov %cr0, %eax");

asm("or $0x10000, %eax");

asm("mov %eax, %cr0");

asm("pop %eax");


printk("module unloaded\n ");

}

module_init(my_init);

module_exit(my_exit);




- 실험에 사용된 CPU usage 에 영향을 주기위한 태스크는 아래와 같다

root@declspec-desktop:/var/www# cat task.c

#include <stdio.h>

int main(){

int a=1;

while(1){

a = a<<a + 1;

}

return 0;

}






3. 실험내용


실험 A. RDTSC 타임스탬프로 측정한 원래의 getuid 수행시간

(기본 쉘 로더로 실행함, OS 부팅후 기본상태)


time delta(msec, usec): 0.003230, 3.229891

time delta(msec, usec): 0.002956, 2.956050

time delta(msec, usec): 0.003144, 3.144060

time delta(msec, usec): 0.003112, 3.112384




실험 B. RDTSC 타임스탬프로 측정한 후킹된(printk 추가) getuid 수행시간

(기본 쉘 로더로 실행함, OS 부팅후 기본상태)


time delta(msec, usec): 0.010309, 10.308879

time delta(msec, usec): 0.010456, 10.456017

time delta(msec, usec): 0.009922, 9.921619

time delta(msec, usec): 0.010033, 10.032994




실험 C. RDTSC 타임스탬프로 측정한 원래의 getuid 수행시간

(파이썬 로더로 실행, OS 부팅후 바로 수행)


[r.py]

import os, sys

for i in range(0, 100):

os.system("./app")


root@declspec-desktop:/var/www/module_scthook# python r.py

time delta(msec, usec): 0.001747, 1.746927

time delta(msec, usec): 0.001797, 1.797335

time delta(msec, usec): 0.001814, 1.814365

time delta(msec, usec): 0.001745, 1.745224

time delta(msec, usec): 0.001858, 1.857962



실험 D. RDTSC 타임스탬프로 측정한 후킹된(printk추가) getuid 수행시간

(파이썬 로더로 실행, OS 부팅후 바로 수행)


root@declspec-desktop:/var/www/module_scthook# python r.py

time delta(msec, usec): 0.009759, 9.759154

time delta(msec, usec): 0.003189, 3.189019

time delta(msec, usec): 0.005138, 5.137580

time delta(msec, usec): 0.003295, 3.295285





실험 E. RDTSC 타임스탬프로 측정한 원래의 getuid 수행시간

(기본 쉘 로더로 실행함, task 가 같이 동작하는 환경)


time delta(msec, usec): 0.001205, 1.204695

time delta(msec, usec): 0.001252, 1.251698

time delta(msec, usec): 0.001233, 1.233305

time delta(msec, usec): 0.001162, 1.161780

time delta(msec, usec): 0.001221, 1.221044

...





실험 F. RDTSC 타임스탬프로 측정한 후킹된(printk추가) getuid 수행시간

(기본 쉘 로더로 실행함, task 가 같이 동작하는 환경)


time delta(msec, usec): 0.005136, 5.135536

time delta(msec, usec): 0.004998, 4.997594

time delta(msec, usec): 0.004808, 4.807540

time delta(msec, usec): 0.004981, 4.981245

...








실험 G. RDTSC 타임스탬프로 측정한 원래의 open 수행시간

(기본 쉘 로더로 실행함, OS 부팅후 기본상황)


time delta(msec, usec): 0.012808, 12.808187

time delta(msec, usec): 0.008551, 8.551393

time delta(msec, usec): 0.009117, 9.117467

time delta(msec, usec): 0.010929, 10.929108

time delta(msec, usec): 0.008869, 8.869171




실험 H. RDTSC 타임스탬프로 측정한 후킹된(printk추가) open 수행시간

(기본 쉘 로더로 실행함, OS 부팅후 기본상황)


time delta(msec, usec): 0.012996, 12.996197

time delta(msec, usec): 0.011835, 11.835439

time delta(msec, usec): 0.013100, 13.100420

time delta(msec, usec): 0.014165, 14.165129

time delta(msec, usec): 0.013600, 13.600077







실험 I. RDTSC 타임스탬프로 측정한 원래의 open 수행시간

(기본 쉘 로더로 실행함, task 가 작동하는 상황)


time delta(msec, usec): 0.003214, 3.213542

time delta(msec, usec): 0.003301, 3.301416

time delta(msec, usec): 0.003236, 3.236021

time delta(msec, usec): 0.003193, 3.193106

...







실험 J. RDTSC 타임스탬프로 측정한 후킹된(printk추가) open 수행시간

(기본 쉘 로더로 실행함, task 가 작동하는 상황)


time delta(msec, usec): 0.004880, 4.880088

time delta(msec, usec): 0.005048, 5.047662

time delta(msec, usec): 0.005065, 5.065032

time delta(msec, usec): 0.004830, 4.830020

...




실험 K. RDTSC 타임스탬프로 측정한 원래의 open 수행시간

(기본 쉘 로더로 실행함, OS 부팅후 기본상황)

(libc binding 이후)


/*

close( open("./dummy", O_RDONLY, 0644) );

// time check

rdtsc( &edx, &eax );

// system call

open("./dummy", O_RDONLY, 0644);

// time check2

rdtsc( &edx2, &eax2 );

*/


time delta(msec, usec): 0.002527, 2.526896

time delta(msec, usec): 0.002355, 2.355235

time delta(msec, usec): 0.002187, 2.186639

time delta(msec, usec): 0.002269, 2.269404

time delta(msec, usec): 0.002034, 2.034392











실험 L. RDTSC 타임스탬프로 측정한 후킹된(printk) open 수행시간

(기본 쉘 로더로 실행함, OS 부팅후 기본상황)

(libc binding 이후)


time delta(msec, usec): 0.005201, 5.200931

time delta(msec, usec): 0.004556, 4.556179

time delta(msec, usec): 0.005054, 5.053793

time delta(msec, usec): 0.004765, 4.764625

time delta(msec, usec): 0.004908, 4.907676

...





실험 M. RDTSC 타임스탬프로 측정한 원래의 open 수행시간

(기본 쉘 로더로 실행함, OS 부팅후 기본상황)

(libc binding 이후)


time delta(msec, usec): 0.002171, 2.171312

time delta(msec, usec): 0.002114, 2.114092

time delta(msec, usec): 0.002089, 2.088547

time delta(msec, usec): 0.002174, 2.174377

time delta(msec, usec): 0.002232, 2.231598

...




실험 N. RDTSC 타임스탬프로 측정한 후킹된(additional memory write) open 수행시간

(기본 쉘 로더로 실행함, OS 부팅후 기본상황)

(libc binding 이후)



time delta(msec, usec): 0.002200, 2.199922

time delta(msec, usec): 0.002076, 2.076285

time delta(msec, usec): 0.002235, 2.234663

time delta(msec, usec): 0.002212, 2.212184





실험 O. RDTSC 타임스탬프로 측정한 원래의 open 수행시간

(기본 쉘 로더로 실행함, OS 부팅후 기본상황)

(without libc wrapper)


time delta(msec, usec): 0.003087, 3.086839

time delta(msec, usec): 0.003078, 3.077643

time delta(msec, usec): 0.003355, 3.354549

time delta(msec, usec): 0.003255, 3.255435



실험 P. RDTSC 타임스탬프로 측정한 후킹된(additional memory write) open 수행시간

(기본 쉘 로더로 실행함, OS 부팅후 기본상황)

(without libc wrapper)


time delta(msec, usec): 0.003637, 3.636565

time delta(msec, usec): 0.003554, 3.553799

time delta(msec, usec): 0.003822, 3.821509

time delta(msec, usec): 0.003500, 3.499644

...



실험 Q. RDTSC 타임스탬프로 측정한 후킹된(additional memory write) open 수행시간

(파이썬 로더로 실행함, OS 부팅후 기본상황)

(without libc wrapper)

time delta(msec, usec): 0.001564, 1.563685

time delta(msec, usec): 0.001300, 1.300403

time delta(msec, usec): 0.001645, 1.645088

time delta(msec, usec): 0.001341, 1.340594

time delta(msec, usec): 0.001347, 1.347406

...


실험 R. RDTSC 타임스탬프로 측정한 후킹된(additional memory write) open 수행시간

(파이썬 로더로 실행함, OS 부팅후 기본상황)

(without libc wrapper)

time delta(msec, usec): 0.001167, 1.166889

time delta(msec, usec): 0.001035, 1.035077

time delta(msec, usec): 0.001398, 1.397814

time delta(msec, usec): 0.001589, 1.588890

time delta(msec, usec): 0.001159, 1.158714

...





4. 결론


- 실험 A 와 B 를 비교하였을때 후킹이 된경우 시간이 눈에띄게 증가하는 것을 확인 할 수 있다. 그러나 실험 A-B(getuid) 와 실험 G-H(open) 를 비교해보면 시스템콜이 더 복잡한 것일수록 후킹에 의한 시간지연 효과가 가려진다는 것을 볼 수 있다.


- 실험 A-B(기본로더) 와 C-D(파이썬로더) 를 비교해볼때 시간차이가 확연하게 나타나는데, 이는 캐시의 효과에 따른 차이로 보인다. 결과를 보면 캐시에 의한 시간차이가 후킹에 의한 시간차이와 비슷한 수준임을 확인 할 수 있다.


- 실험 A-B 와 E-F 를 비교해보면 다소 의아한 결과가 나타난다.  CPU 사용률이 100% 에 임박하는 상황에서 오히려 전체적으로 시스템콜의 수행시간이 더 빨라진 것이다.  정확한 원인은 알수가 없으나 CPU Usage 또한 시간에 영향을 준다.


- 실험 G-H 와 K-L 을 비교해보면 리눅스의 Lazy Binding 에 따른 시간지연이 어느정도인지 파악 할 수 있다.  실험 결과를 보면 이 시간 또한 후킹에 따른 시간지연과 비슷한 수준임을 알 수 있다. 그러나 System Call Wrapper 의 경우 순수한 system call 의 시간만을 측정할때는 제외해도 되기때문에 이 실험은 큰 의미는 없다.


- 실험 M-N 을 보면 이전 실험들에서 후킹에 의한 시간지연은 90% 이상 printk 의 IO 연산때문임을 알 수 있다.  후킹함수에서 IO 와 같은 시간이 오래 지연되는 작업을 하지않고 memory 연산만 하고 넘어간다면 후킹에 의한 시간지연은 측정 불가능 할 것이다.


실험을 통해 파악된 Timing 에 영향을 줄수 있는 요인들은 아래와 같다

1. Memory Cache

2. CPU Usage rate

3. IO / non-IO

4. CPU Spec


여러가지 실험들을 통해 악성코드의 Timing 측정에 영향을 줄 수 있는 요소들이 어떠한 것들이 있으며 어느정도 영향을 주는지 어느정도 확인 할 수 있었다.


CPU 명령이 아닌 실제 Network Time 으로 측정할 수 있는 precision 은 본 실험보다 더욱 떨어진다는 점까지 감안한다면 결과적으로 Hooking 함수가 excessive 한 IO 를 하지 않는이상 Hooking 에 대한 Timing Attack 은 불가능하다.



'Programming' 카테고리의 다른 글

md5 sqlinjection hash  (0) 2013.06.12
python post script  (0) 2013.06.12
Android APK Decompile  (0) 2013.05.24
Difference between Ntxxx and Zwxxx API  (0) 2013.05.22
Windows Library Structure  (0) 2013.05.22