ASProtect = VirusMaker
ASProtect 2.2x SKE 버전으로 테스트 하던중, 바이러스로 오인 사실되는 문제가 발견되어
한번 포스팅 해본다.
패킹 완료후 카스퍼스키가 진단 창을 턱~ 보여주길래 놀래서 봤더니 내가 방금 프로텍팅한 파일 -.-
그래서 다른 백신도 이러나 싶어서 바이러스 토탈에 넣어서 돌려봤다.
야 이거 진짜 심하다-.-
참고로 나는 기본 콘솔 프로젝트를 만들어 보았을 뿐이며, 머 소스 내용은 아무것도 없다.
#include "stdafx.h"
#include "windows.h"
#include "Winsock2.h"
#pragma comment(lib, "Ws2_32.lib")
int main(int argc, char* argv[])
{
printf("recv %X\n", recv);
printf("Hello World!\n");
return 0;
}
이런데 바이러스라니 -.-
그래서 좀더 정확한 확인을 위해, 헬로월드 빼고 하나씩 다 제거를 해보았다.
어차피 소켓 링크를 제거하면 recv 번지 출력에서 빌드 오류가 나니까
그것만 냅두고 printf("recv %X\n", recv); 요 한줄만 없애보니
바이러스로 잡히지 않았다 -.-
recv의 번지만 출력하면 바이러스라니 -________-
그래서 이왕 여기까지 해본김에, 혹시나 하고 한번 문제의 printf("a %X\n", recv); 부분을
BYTE형 변수로 바꾸어서 바이너리로 바로 박아보는 짓이 보고 싶어졌다.
대략 0xA1 부터 push offset aRecvX까지 잡으면 될 것 같았다. 그래서 걔를 한번
소스에 그대로 입혀봤다 (물론 별 씨잘데기 없는 짓이겟지만 ㅋ)
#include "stdafx.h"
#include "windows.h"
#include "Winsock2.h"
#pragma comment(lib, "Ws2_32.lib")
int main(int argc, char* argv[])
{
//printf("recv %X\n", recv);
unsigned char data[24] = {
0xA1, 0xB0, 0x60, 0x40, 0x00, 0x50, 0x68, 0x40, 0x70, 0x40,
0x00, 0xE8, 0x20, 0x00, 0x00, 0x00, 0x83, 0xC4, 0x08, 0x68,
0x30, 0x70, 0x40, 0x00
};
printf("Hello World!\n");
return 0;
}
흐흐 결과는?
당연히 바이러스로 잡히지 않았다 -_-
왜냐면 저 변수는 사용하지 않는 변수이기 때문에 컴파일러 최적화 옵션에서
똑똑한 척을 하고 싶은 컴파일러가 안 쓰는 부분이라고 감안하여 없애버렸기 때문이다 -.-
억지로 쓰는 척을 하기 위해서 sizeof를 한줄 더 넣었다 쩝.
#include "stdafx.h"
#include "windows.h"
#include "Winsock2.h"
#pragma comment(lib, "Ws2_32.lib")
static unsigned char data[24] = {
0xA1, 0xB0, 0x60, 0x40, 0x00, 0x50, 0x68, 0x40, 0x70, 0x40,
0x00, 0xE8, 0x20, 0x00, 0x00, 0x00, 0x83, 0xC4, 0x08, 0x68,
0x30, 0x70, 0x40, 0x00
};
int main(int argc, char* argv[])
{
//printf("recv %X\n", recv);
printf("fuck: %d\n", sizeof(data));
printf("Hello World!\n");
return 0;
}
결과는? ㅋ
당연히 될 리 가 없다.
일단 지역변수로 선언했던 data 배열을 static 으로 바꾼 부분부터 눈에 띌 것이다.
저렇게 배열변수를 지역변수로 선언하면 저것이 컴파일이 되었을때 어떤 형태가 될까?
절대 0xA1, 0xB0 이모양으로 들어가지 않는다.
mov 명령어로 한바이트씩 들어가게 된다 -.-
따라서 이런 형태가 된다
mov ebp-X, 0xA1
mov ebp-X, 0xBo
....
그러니 opcode로 바뀌면 더더욱이 이상한 바이너리가 된다.
그래서 static으로 넣어줘야 한다. static으로 바꿔주면 바이너리가 그대로 들어가니까
다음과 같이 정상적으로 된다.
하지만 이번 역시 바이러스로 잡히지 않았다.
초반에 테스트 해봤을때 분명했던 것은, recv 번지를 찍는 부분 때문에 바이러스로 진단이
되었다는 것이다. 따라서 저 패턴이 문제가 있거나, 아니면 기타 휴리스틱 엔진에 문제가 있거나
할 것이 분명하다.
하지만 이번엔 분명 저 패턴이 들어갔음에도 불구하고 바이러스로 진단되지 않았다.
이유는, 따지고 보면 굉장히 많을 것 같지만 당장은 세가지 정도밖에 생각이 나지 않기에
일단 함 적어본다
예상되는 이유
1) 강제로 주입한 번지 저기는 데이터 섹션이다. 카스퍼스키 등의 백신이 이번 진단을 위해
데이터 섹션을 건드리지는 않을 것이다
2) SKE로 프로텍팅을 하면서 저 바이너리가 미묘하게 꼬여버려 영 다른 넘으로 바뀌었다.
3) 0x4060B0 번지는 프로텍팅 하기 전의 IAT다. 따라서, 저 IAT는 프로텍팅 후 완전히 뒤바뀌어져
버릴 것이며, 저 shell code는 recv의 번지를 출력하는 기능과는 전혀 무관한 코드가 될 것이다.
따라서 Kaspersky의 관심 밖이 될 것이다.
내 생각으로는 3번의 이유가 가장 유력할 것 같다.
만약 1번의 이유라면, 코드 작성시, 코드 섹션에 빈 공간을 적당히 만들어놓고, 바이너리 제작 후에
그 빈공간에 강제로 shell code를 주입해 보면 진단되는 모습을 볼 수 있을 것이다.
2번은 3번과 관련이 있으므로 별로 논할 내용이 없다.
3번이라면 억지로 재현하기에는 상당히 골때리는 상황이 된다. SKE로 프로텍팅하면 IAT가
흩어질 뿐 아니라, 중간중간에 인터럽트가 삽입되는 각종 안티 디버깅 트릭, 그리고 re-direct되는
API의 콜을 다 감안해서 작업을 해야 하기 때문이다.
그냥 단순히 패턴을 알아보고 싶었던 것인데, 일을 너무 크게 벌렸다 -.-
걍 적당한 순에서 정리해야겠다.
소켓 링크를 하고 recv의 함수 번지를 찍은후 SKE로 패킹하면 Kaspersky는 바이러스로 잡는다.
하지만 SKE로 패킹하지 않으면 바이러스로 잡지 않는다.
나의 예상으로는 관련된 패턴이 있는 실제 바이러스 샘플 중에, SKE로 패킹된 것이 있으며
그 샘플은 세계 백신 협회를 거쳐 전 백신사에 전달되었을 것이다.
여기사, 엔진이 구린 백신은 단순히 몇줄 시그너쳐만 삽입했을 것이지만,
휴리스틱 엔진을 구현해 놓은 곳에서는 (안에서 무슨 짓을 하는지 모르지만) 다양한 각도와
각양각색의 진단 방법론으로 너무 열심히 잡으려다 보니 이와 같은 오탐이 일어나게 되었다.
그리고 나의 경험으로는 그런 오탐은 SKE로 패킹한 것들 중에서 많았다.
결론,
AS SKE는 바이러스들이 너무 많이 사용하는 프로텍터이다(실제로도 그렇다)
그리고 그에 걸맞게 백신들도 열심히 오탐을 내주고 있다.
백신사를 탓해야 겠지만, 이미 퍼져버린 엔진을 우짜겠는교, 아무리 고친다 해도 구 엔진을
계속 사용하는 유저는 반드시 나오게 되어 있다.
따라서 상용 프로그램이 쓰기에는 추천할 만한 프로텍터는 아니다.
트랙백 보낼 주소 : http://window31.com/trackback/38
-
정상프로그램도 악성코드가 될 수 있다?
from 하늘과 바람과 별과 시2007/08/03 10:05악의적인 기능을 가지고 있지 않은 정상프로그램도 악성프로그램으로 백신에서 삭제될 수 있는 경우가 많아지고 있어 프로그래머들의 주의가 필요할 것 같다. 실행압축(Packer) 프로그램들로 파일용량을 줄이거나 다른 사람에 의해서 프로그램 변조(Crack)를 막기 위해서 암호화 용도로 사용할 경우가 있는데, 이러한 경우 악성코드(?)가 될 수 있는 것이다. 그 이유는 바로,,, 악성코드의 분석을 어렵게 하기 위해서 바이러스 제작자들이 주로 사용하는 실행압..

