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 |