You are on page 1of 9

시각화를 통한 악성코드 분석

· 코드 시각화의 방식

- 1 바이트를 이미지의 1 픽셀로 변화시키는 방법

이 방식은 악성코드 파일의 바이너리 8 비트(1 바이트)를 부호가 없는 정수의 벡터로


읽어 들이고, 그 각 바이트를 이미지의 한 픽셀(0~255 로 전환을 하여 그레이스케일
이미지로 만드는 방식이다. 이 때 1 바이트를 1 픽셀로 변환을 하는 이유는 1
바이트는 2^8 을 나타내는데 이는 256 으로 그레이스케일 이미지의 한 픽셀이
표현을 할 수 있는 범위와 동일하기 때문이다. 이렇게 이미지를 만들 때 이미지의
크기는 파일의 그기에 따라 달라지기 때문에 이미지의 너비를 파일 크기에 따른
적절한 크기로 고정을 시켜야 한다. 이 방식으로 사용한 방법은 만약에 파일의
크기가 S 라고 한다면 이미지의 한 변의 길이를 ⌊√S⌋인 ⌊√S⌋*⌊√S⌋의 정사각형
이미지지를 만든다. 그 후 모든 이미지의 사이즈를 동일하게 만들어주기 위해서
256*256 으로 맞추어주는 방식을 사용을 하였다. 이렇게 사이즈를 동일하게 맞추어
주는 과정에서 이미지를 확장을 할 시에는 interpolation 으로 INTER_LINEAR 방식을
사용을 하였고, 축소를 할 경우에는 interpolation 으로 INTER_AREA 방식을 사용을
하였다.

이렇게 이미지를 만드는 과정은 우선 7500 개의 파일의 labeling 을 해준 csv 파일을


통해서 benign 과 malware data 를 나누어준 후 training data 와 test data 로 나누어
주었다. 그 후 각각의 파일을 바이너리로 읽어준 후 benign_binary, malware_banary
에 각각 train data 에 대한 파일들의 바이너리를 넣어주었다.

이 방식으로 이미지의 사이즈를 ⌊


√S⌋로 만들어주었고, 이렇게 size 를 설정을 하고 각 이미지 픽셀에 파일의 바이너리
바이트의 값을 하나씩 넣어주었다.
이는 benign train data 에 대한 것인데 이미지의 크기를 256*256 으로 설정을 하는
과정으로 interpolation 을 위에서 말한것처럼 설정을 하였다.

이 방식으로 만든 이미지는 이런식으로 그레이스케일 이미지를 가지게 된다.

- 2 바이트를 한 쌍의 좌표로 변환하는 방법

이 방식은 악성코드 파일의 바이너리 2 바이트를 1 바이트씩 변환하여 한 쌍의


좌표로 만드는 방식이다. 1 바이트의 값의 범위는 0~255 기 때문에 이미지의 크기는
256*256 으로 설정을 하고 모든 픽셀의 값을 0 으로 만들어준다. 변환한 좌표가 나올
때마다 그 좌표에 해당하는 픽셀의 값을 1 씩 증가시킨다. 이 과정을 모두 마친
후에는 이 이미지를 그레이스케일로 만들어줘야 하는데 그레이스케일 이미지는 한
픽셀의 값으로 0~255 의 값을 가지기 때문에 픽셀값 중 최대값을 찾고 그 최대값과
0 을 각각 그레이스케일 이미지의 255 와 0 에 대응시키도록 normalize 해준다.
이렇게 이미지를 만든느 과정은 바이너리 파일을 읽어오는 과정까지는 앞의 방식과
동일하다. 하지만 앞의 방식과 다른 것은 이미지를 생성을 할 때 사이즈를 파일
크기에 대응시키지 않고 256*256 의 크기로 만든다는 것이다.

이미지의 생성은 이 방식으로 진행을 하였다. 우선


이미지의 크기를 256*256 으로 맞추고 그 값에 모두 0 을 넣어주었다.

그 후 이런식으로
파일의 바이너리 2 바이트씩 읽어와서 각각을 x, y 좌표로 만든 후 좌표가 나올
때마다 그 좌표에 해당하는 픽셀의 값을 1 씩 더해주었다.

그 후 최대값을
쉽게 구하기 위해 각각의 이미지 배열을 numpy 배열로 만들어 주었다.

그 후에는 이렇게 픽셀값 중


가장 큰 값을 가지고 있는 기준으로 그 크기가 255 가 되게 normalize 를 진행을
해주었다.
이는 이 방식을 사용했을 때 파일이 변환되는 그레이스케일 이미지의 모양이다.

- Local sensitive hashing(LSH)을 통한 방법

이 방식은 입력값이 유사할 경우에는 출력값도 비슷하다는 특징을 갖는 LSH 를


사용하여 바이너리 정보의 문자열이 유사할 경우 이미지 매트릭스에서 유사한
좌표로 매핑하는 방식이다. 이 방식은 바이너리 파일을 직접 읽어서 사용하는 것이
아니라 바이너리 파일을 먼저 디스어셈블을 하여 어셈블리 언어로 만들어주어야
한다. 이 과정은 IDA pro 를 사용하여 가능하다. 이렇게 디스어셈블을 한 후 opcode
만 추출을 한 뒤에는 basic block 으로 나누어야 한다. Basic block 으로 나눌 때는
return 계열이나 jump 계열의 instruction 이 나올 시 block 을 나누어주는 방식을
사용한다. 이 basic block 을 가지고 major block 을 생성을 하였는데 이 과정은 basic
block 중에서 call 명령어가 있는 block 만 남겨주고 call 명령어가 없는 부분은 제거를
해줌으로써 진행을 한다. 그 후 major block 을 통해서 opcode sequence 를 생성을
하였고 그 opcode sequence 에 hash 함수를 적용을 하여서 그 값을 가지고 x, y, r, g,
b 값을 각각 구해준다. 이렇게 좌표 x, y 와 r, g, b 값을 구해준 뒤에는 x, y 좌표를
중심으로 하는 3*3 영역에 각각의 R 채널, G 채널, B 채널에 값에 r, g, b 값을
더해준다. 이렇게 더해주다 보면 그 채널의 한 픽셀의 값이 255 보다 크게 되는
경우가 생기는데 이럴시에는 그 값을 255 로 정한다.

우선 프로젝트를 진행을 해주는 과정에서 디스어셈블 관하여 교수님께 문의를 하자


benign 과 malware file 의 opcode 만 추출해 저장해 놓은 데이터를 보내주셔서 그
데이터를 가지고 진행을 하였다

이는 basic block 으로 block 을 나눠주는 과정이다. 우선 명령어가 j 로 시작을


하거나 ret 로 시작을 하면 그는 jump 계열의 명령어이거나 return 계열의
명령어이기 때문에 이런 명령어가 나오는 index 를 저장을 해주고 이를 통해서 jump
계열이나 return 계열의 명령어가 나오는 구간을 block 의 마지막 부분으로
만들어주는 방식이다.

이는 basic block 중 call 명령어가 없는 block 의 index 를 저장을 해주었다가 그


index 를 사용을 하여서 call 명령어가 있는 basic block 만을 남기고 call 명령어가
없는 basic block 을 날려주어 major block 을 만들어주는 과정이다.
그 후 opcode sequence 는 major block 의 모든 명령어를 하나의 연속된 string 으로
만들어주어 구현을 하였다.

이는 hash
함수를 적용을 한 뒤에 그 값을 16 진법으로 바꾸어서 저장을 해주는 과정이다.

이렇게 16 진법을 저장을 한 뒤에 1


바이트씩을 각각 x, y, r, g, b 값에 넣어주었다

그 후 이미지를 256*256 의 사이즈로 만들어주었는데 이 방식은 r, g, b 세 채널이


있기에 각각의 채널에 대한 배열을 만들어 주었다

. 그 후 이 방식을 통해 각각의 채널에 x,


y 좌표를 기준으로 한 3*3 의 정사각형에 r,g,b 값을 각각 더해주었고 만약에 더해준
값이 255 보다 크게 된다면 그 값을 255 로 설정을 하게 만들어주었다.
이렇게 만들어준 이미지는 위와 같은 r, g, b 세 채널을 가지는 이미지를 생성읅
한다. 이 방식은 위의 두 방식과 다르게 train data 로 benign 1000 개, malware 1000
개, test data 로 benign 250 개, malware 250 개를 사용을 하였다.

그 후 이 세가지 방식에 대해서 각각 CNN 방식으로 train 을 한 후 test 를 했다.


Train 을 할 시에는

층을 이런식으로 구현을 하였다.

1 바이트를 1 픽셀로 변환하는 방식과 2 바이트를 한 쌍의 좌표로 변환하는 방식은


그레이스케일 이미지를 input 으로 갖기에 이런식으로 구현을 하였고
LSH 를 이용한 방식은 r, g, b 세 채널을 갖는 이미지를 input 으로 갖기에
이런식으로 구현을 해주었다.

loss 가 4 번동안 떨어지지 않으면 early_stopping 을 하게 early stopping 을 구현을


하였고 optimizer 로는 Adam, loss function 으로는 sparse categorical crossentropy
를 사용을 해주었다.

결과로는 1 바이트를 1 픽셀로 변환하는 방법의 train data 에 대한 accuracy 는 99.58


이 나왔고 test data 에 대한 accuracy 는 70.56 이 나왔다

2 바이트를 좌표로 사용한 방식으로는 train data 에 대한 accuracy 는 99.23 이


나왔으며 test data 에 대한 accuracy 는 82.84 가 나왔다.

LSH 를 통한 방식으로는 train data 에 대한 accuracy 는 92.85 이 나왔으며 test data


에 대한 accuracy 는 89.20 가 나왔다.

1 바이트를 1 픽셀로 변화하는 방법과 2 바이트를 한 좌표로 변환하는 방법은


ovefitting 이 일어났다. Overfitting 을 방지하기 위해서 early stopping 을 사용을
하였지만 그래도 overfitting 이 일어났다. Overfitting 이 일어난 이유로는 우선
unbalanced 한 데이터 때문이라고 생각을 한다. 주어진 데이터는 7500 개이지만
그중 5250 개가 malware data 고 2250 개가 benign data 이다. 다시 말해 거의 2.5
배나 데이터의 차이가 나기 때문에 데이터가 unbalanced 해 overfitting 이 일어난
것이라고 예측을 하였다. Overfitting 이 일어난 두 번째 이유로는 data 가 그리 많지
않았다는 것이라고 생각을 한다. 데이터의 양을 늘려준다면 어느 정도 overfitting 이
해결될 것이라고 생각을 하였다.

이 두 방식은 같은 데이터를 사용을 했기에 그나마 성능을 비교할 수 있었는데 2


바이트를 한 좌표료 변환하는 방법의 성능이 더 좋았다. 내가 예측하는 이러한
이유가 일어나는 이유는 1 바이트를 1 픽셀로 변화하는 방법은 이미지를 확대/
축소를 해주어야 하는데 이러한 과정을 통해서 이미지의 정보가 손실이 되거나
왜곡이 되서 그럴 것 같으며 또다른 이유로는 1 바이트를 1 픽셀로 변환하는 방법은
이미지의 사이즈를 처음 생성시 ⌊√S⌋*⌊√S⌋로 만들어주는데 이는 가우스의 값이기에
데이터의 마지막 부분에 benign 과 malware 를 비교할 수 있는 중요한 정보가
있을시 그 정보가 손실되기 때문에 2 바이트를 한 좌표로 변환하는 방법이 성능이
더 좋은 것 같다는 생각을 하게 되었다.

앞선 두가지 방식과 마지막 방식인 LSH 를 이용한 방식은 데이터가 다르기에


성능을 비교는 할 수 없지만 대략적으로 비교를 하려 한다. 우선 LSH 를 이용한
방식이 overfitting 이 일어나지 않았는데 이 이유로는 benign 과 malware 를 각각
1000 개씩으로 맞추어 train 하고 250 개씩으로 맞추어 test 를 하였기에 data 가
unbalanced 하지 않았기에 overfitting 이 일어나지 않은 것이라고 예측을 하였다.
또한 앞선 두가지 경우보다 LSH 를 사용한 방식이 성능도 더 좋았는데 이 이유로는
LSH 를 사용한 방식은 major block 을 생성을 하면서 call 명령어가 없는 부분을 다
제거를 해주었는데 이 방식을 통해서 더 specific 한 부분을 추출을 할 수 있었기에
더 잘 예측이 되지 않았나 싶다.

마지막으로 test data 의 성능을 더 높이기 위해서는 pefile 등을 사용을 하여서


코드를 각 section 별로 나누어 준 뒤에 악성코드 전체를 시각화하는 방식이 아니라
중요한 정보가 있을듯한 부분인 .text, .data, .rsc 의 정보들을 추출하여 그 정보들을
가지고 시각화를 하면 중요한 feature 들만 뽑아내서 더 성능이 좋게 나오지 않을까
싶다.

You might also like