아침에 눈을 떴는데 이상한 느낌이 들었다. 말을 하려고 하는데 목소리가 나오지 않는 기이한 현상이 벌어진 것이다. 침 삼킬 때 따끔 거리는 일은 있었어도 목소리가 아예 나오지 않는 이런 경우는 첨이라 어이가 없었다. 조금 이러다 말겠지 했는데 생각보다 오래간다. 래리 페이지가 목소리를 잃었다며 두문불출할 때만 했었어도 그냥 먼 산 불구경 하듯 딴나라 이야기라 생각했는데, 막상 내가 당해 보니 이게 보통 일이 아니다. 단 한 차례도 말하는 걸 특별하다 생각하지 않았는데 말이 나오지 않으니 일상 생활이 거의 불가능할 지경, A부터 Z까지 되는 게 하나도 없다. 멘붕 ㅠㅜ~
#0
이 모든 사단의 발단은 요상한 해킹툴 하나에 있다. 그 녀석이 정체를 드러낸 것은 지난 주였다. 해킹툴이 잔뜩 올라오는 사이트에 우리 보안 제품이 적용된 게임의 해킹툴도 올라왔다. 평소에 그냥 왜 다른 업체들이 그 해킹툴을 안 막을까 너무 궁금했던 차였다. 왜냐하면 파일도 구하기 쉬우니 그냥 패턴으로라도 막으면 될텐데 왜 저렇게 버젓이 운영되도록 놔둘까하는 호기심이 있었다. 사이트가 너무 많아서 관심의 사각지대에 있나 보다라고 혼자 생각해보기도 했었다.
평소 생각처럼 패턴 차단을 먼저 시도했는데, 그 다음이 가관이었다. 그 해킹툴이 전혀 다른 모습으로 돌아온 것이다. 그것도 2시간만에. 그때 진실을 마주하게 되었다. 다른 업체들이 안 막는 게 아니라 못 막는 거였구나. 다시 패치를 했다. 또 2시간. 또 패치를 했다. 또 2시간. 그맘때쯤 녀석이 그리 호락호락한 놈은 아니라는 사실을 인지하게 되었다.
어쨌든 그때부터 개발팀은 무한분석, 불꽃코딩, 밤샘, 지원팀은 매일같은 업데이트를 하게 되었다. 물론 업체 측에서도 해킹툴 척결의 의지가 있었기에 업데이트를 상당히 우호적으로 적용해 주었고 일정 수준 이상의 성과를 보이고 있다. 오늘이 꼭 10일째 되는 날인데, 우리는 그 사이에 무려 7번의 업데이트를 했다. 사실 주말 빼고는 매일 업데이트를 했다고 할 수 있다.
#1
이번 해킹툴 차단에 결정적 기여를 한 사람은 다름아닌 막내였다. 녀석과 2시간 짜리 공방을 벌이던 때 면역 체계 이론에 빠삭한 막내가 HIV(에이즈 바이러스)에 대응하는 전략에서 가져온 새로운 아이디어를 제안했다. 평소 나도 생명체에서 답을 찾아야 한다는 생각이 강했던터라 귀담아 듣게 되었는데 생각보다 좋은 전략이었다. 그래서 막내의 아이디어를 당장 구현했다. 사실 크게 구현이 어려운 부분은 없는 아이디어라 다음날 구현할 수 있었고 패치에 적용했다.
내심 막내도 나도 기대했다. 난 6시간만 버티면 좋겠다고 했는데 결국 그날은 10시간을 버텼다. 물론 대박은 그 시간이 아니었다. 녀석의 실수를 끌어낸 것이다. 지속적인 패치로 피로도가 누적됐는지 해커가 핵심 구성 모듈 중의 하나를 날것 그대로 방출한 것이다. 이 또한 막내가 발견했다. 막내의 발견 이후에도 난 다른 작업 때문에 바빠서 그 모듈을 분석하지 않고 있었는데 막내가 그 모듈의 핵심적인 기능 대부분을 분석해서 알려주었다. 막내가 분석하지 못한 내용은 같이 앉아서 분석을 했는데 우리는 그날 야근을 하면서 녀석의 수법을 모두 알아낼 수 있었다. 몇가지 참신한 기법도 있었지만 역시나 리버싱엔 능하지만 코딩은 잘 못하는 전형적인 해커였다.
지피지기면 백전백승이라고 했던가? 녀석의 수법을 회피할 수 있도록 전체적인 구조를 재구성 하는 작업을 했다. 이건 생각보다 빡쎈 작업이라 다소 시간이 걸렸다. 그리고 그 주 주말엔 XIGNCODE3의 가장 강력한 기능 중에 하나를 준비했다. 4년 만이었다. 여기까지 기어온 네오는 녀석이 두 번째. 그 패치는 14일 적용됐고, 해커는 아직도 47.5차원 어디쯤에서 방황하는 중이다.
#2
해커의 주된 공격 방식은 코드 조작(tampering)이다. 우리 코드를 제멋대로 패치해서 정상적으로 기능들이 동작하지 않도록 만드는 것이다. 이런 작업은 주로 패치할 대상 주소가 몇 개나 되는지에 의존하는데 우리는 다년간 이런 공격을 많이 받았기 때문에 폭탄을 제거하기 위해서는 여기저기 잘라야 하는 선들이 상당히 많다. 하지만 리버싱에 능한 그 녀석은 그 모든 선을 다 찾아서 잘라 버렸다. 이러면 VM 코드니, CRC 체크니 말하는데 그냥 그런 것들을 수행하는 부분 모두가 세트 메뉴로 다 패치됐다고 생각하는 게 속 편하다. 우리가 날것 그대로 방출된 모듈을 분석하기 전까지는 정말이지 발가벗고 녀석과 대적하는 느낌이었다. 그 정도로 녀석의 리버싱 능력은 탁월했다. 우리는 그 능력을 봉쇄하기 위해 다양한 하드닝 전략을 생각했는데 여기에 10개 정도만 추려서 소개해보면 다음과 같다.
- 필요한 코드는 필요한 시점에 로드되야 한다. 보통 프로그램은 실행과 동시에 모든 필요한 코드가 로딩된다. 이래서는 문제가 있다. 한번에 패치 대상 주소를 모두 확보할 수 있기 때문이다. 핵심 코드는 그 코드가 필요한 그 순간에만 존재하도록 만들어야 한다.
- 모든 코드는 언제든지 재기동 될 수 있어야 한다. 우리 몸의 세포는 매일 새롭게 태어난다. 심지어 뼈도 매일 조금씩 변화해서 10년이 지나면 완전 새로운 뼈로 태어난다. 이것과 마찬가지로 모든 구성 요소는 필요한 시점에 언제든지 새롭게 재기동 될 수 있어야 한다. 재기동 시키면 지속적으로 해당 컴포넌트들에 가해진 패치를 무력화 시킬 수 있다. 마치 드래곤볼의 피콜로 처럼 팔이 잘리면 조금 있다 다시 팔이 살아나는 것과 같은 시스템을 만들어야 한다.
- 해커에게 동일한 형태로 표출되는 코드가 다른 동작을 수행할 수 있도록 구성해야 한다. 세포가 동일한 구조로 되어 있지만 눈과 코와 귀와 입처럼 다른 기관으로 분화되는 것과 같은 이치다. 해커에겐 표면적으로 동일하게 보여지지만 실제로는 다른 동작을 하도록 만들어야 한다는 말이다.
- 코드 변조 여부를 스스로 인지할 수 있어야 한다. 보통은 CRC 체크 코드 등을 별도의 작업 단위로 구성하는 경우가 많다. 이런 건 그냥 소용없는 짓이다. 마치 그 코드가 원래 수행하려고 했던 일 자체가 CRC 체크인 것처럼 만들어야 한다. CRC 체크 코드와 원래 수행하는 코드 사이의 결합도를 아주 높여야 한다는 것이다. 샴쌍둥이들처럼 떼는 것 자체가 불가능하도록 만들어야 한다.
- 때로는 느슨한 랜덤 시드가 효과적이다. 하이엔트로피 짱짱맨을 외치는 사람들이 많다. 하지만 몇몇 부분에 있어서는 해커에게 그건 변하지 않는다는 것을 인식시키는 것이 훨씬 효과적이다. 변하지 않는다고 생각하고 내 PC에서 잘 되니까 릴리즈 했는데 그 순간 변해버린다면 제 아무리 날고기는 해커도 당황할 수 밖에 없다. 그래서 아주 느슨한 랜덤 시드를 섞어 쓰는 것이 효과적이다.
- 스레드 경계, 예외 경계, IPC 경계, 운영체제 콜백 경계를 활용해야 한다. 보통 코드는 직렬화된 순서로 진행된다. 이런 구조는 아주 쉽게 트레이스되고 분석된다. 그 트레이스 흐름을 끊을 수 있는 것들이 이런 장치들이다. 코드 내에서 이런 장치가 지속적으로 자주 등장하도록 만들어야 한다.
- 전체 코드가 메모리 상의 특정 영역에 집중돼 있으면 곤란하다. 분산 배치시켜야 한다. 이는 관련 코드를 스캔하는 속도를 떨어뜨리고 더 찾기 어렵게 만든다.
- 리버싱을 어렵게 만드는 방법 보다는 분석을 다 한 다음에도 프로그래밍을 어렵게 만드는 방법이 효과적이다. 탐지 기반 설계보다는 회피 기반 설계가 더 효과적이라는 의미다.
- 탐지와 보고의 거리는 멀수록 효과적이다. 둘이 가깝다는 건 그냥 그 루틴은 쓸모가 없다는 것과 같다. 최대한 멀게 만들어라.
- 포인터 전역 변수는 피해라. 모든 데이터는 아주 은밀한 방식으로 숨겨야 한다. 전역 변수는 고양이에게 생선을 던져주는 것과 같은 형태다. 인코딩 같은 건 소용도 없으니 아주 은밀한 데이터 저장 방식을 고려해야 한다. 7번과 같은 이치다. 코드 뿐만 아니라 데이터도 분산 배치되어야 한다는 것을 의미한다.
#3
실무를 주로 하다보면 이론적인 부분이 취약해지기 마련이다. 앞서 우리가 생각한 방법들도 사실 경험적인 추론에 의한 것들이지 수학적으로 코드 조작에 대응할 수 있는 방법들은 아니다. 잘 알겠지만 가장 효과적인 방법은 모든 코드를 알고, 그 원리를 알더라도 공격하기가 힘든 수학적인 근간이 있는 방법들이다. 이런 점에서 이번 싸움을 하면서 난 anti tampering이라든가 tamper proofing 같은 기법들에 관심이 생기기 시작했다. 그 과정중에 2009년에 발간된 기념비적인 책을 하나 찾게 되었는데 바로 Surreptitious Software란 책이다. 조작 방지에 관한 내용만 전문적으로 다루고 있는 책인데 실무적인 지식 보다는 이론적인 부분에 포커스가 있다. 사실 뭐 반반 짬뽕이라고 보면 된다. 나같은 사람이 보기에 편하게 쓰여진 책인 것 같다.
중간에 Arxan 이야기도 잠시 나와서 나름 반가웠다. 내가 그토록 애타게 찾아 헤맸던 수학적인 근간이 있는 알고리즘도 몇 개 발견할 수 있었다. 대박~ 대부분의 수학적인 기반의 코드들이 그렇듯이 결과론적으론 아주 심플한데 해커 입장에서는 구토를 유발케하는 그런 코드들이다. 알고리즘이 궁금한 분들은 책을 사서 읽어보도록 하자. 여기 공간이 협소해서 알고리즘을 다 소개하기에는 무리가 따를 것 같다. 당연한 소리겠지만 여러분이 코드 보안에 신경써야 하는 분야에서 일하고 있다면 100만원을 주고 사도 돈이 아깝지 않은 책이다. 너무 싼가? ㅋ~
#4
보안 코드들이 다 그렇지만 우리 코드도 점점 더 필요한 기능을 수행하는 코드보다 메타 코드가 늘어나고 있다. 주로 매크로 기능을 하는 코드들인데 더미 코드를 삽입한다거나 코드 구조를 변경한다거나 CRC 체크를 한다거나 하는 코드들이다. 메타 코드가 늘어날수록 원본 코드의 가독성은 떨어지고 버그는 코드 속에 더 은밀하게 숨겨진다. 요즘은 이런 것들을 매크로가 아닌 다른 방법으로 할 수 없을까 궁리중이다. 코드 위빙(code weaving)이라 불리는 기법들인데 C/C++같은 저급 언어의 컴파일 타임 내지는 컴파일된 결과물에 수행하기에는 다소 어려움이 있는 것 같다. 노가다가 답인가? ㅠㅜ~
#5
어쨌든 그놈 덕분에 우리는 훨씬 더 강력한 기능들을 많이 추가할 수 있었다. 고맙긴 하지만 그래도 이젠 우리가 헤어져야 할 시간. 겁나 디버깅 중인 것 같은데 너도 ROE를 생각해야 하지 않겠니? 단언컨대 그 시간에 다른 게임 디버깅 하면 훨씬 수월할거야. 그럼 이제 바이, 짜이찌엔, 그리고 사요나라 ㅋㅋㅋ~