반응형

게임 보안: 커플링에 관한 오해들…
by 신영진(YoungJin Shin), codewiz at gmail.com, @codemaruhttp://www.jiniya.net

게임사 미팅을 해보면 흔하게 겪는 일 중에 하나가 커플링에 관한 오해들입니다.
반지는 아니구요. ㅋㅋ 게임과의 결합도가 높은 보안 코드에 대한 이야깁니다.

소프트웨어 공학에서는 모듈간의 의존도 내지는 결합도를 나타내는 척도로 커플링이란 단어를 쓰거든요. 결합도가 낮으면 느슨한 결합이라고 하고 결합도가 높으면 강한 결합이라고 한답니다. 어떤게 좋을까요? 당연히 소프트웨어 공학에서는 커플링이 낮은 수준이 닥치고 갑입니다. 결합도가 높다는 건 덜 모듈화 됐고, 고치기 쉽지 않으며, 수정할 때 오류가 발생하기 쉽고, 유지보수 하기가 까다롭다는 그 모든 것을 의미합니다. 그래서 커플링을 피해야 할 그것으로 간주하죠.

하지만 요사이 많은 부분에서 게임보안 제품과 게임은 커플링이 높아지는 것이 필연적일 수 밖에 없다는 주장이 제기되는 것 같아서 몇 가지 사례별로 생각을 정리해 보았습니다. 자 그럼 과연 커플링이 높아야 해킹툴을 잘 잡는다는 말이 진실인지 아닌지 한 번 살펴볼까요? ㅎㅎ~

#0
언제부턴가 게임보안 업체들 사이에서 서버에 게임보안 코드를 탑재하는 것이 일반화 됐습니다. 저희가 XIGNCODE라는 제품을 개발하던 초기만 해도 서버 쪽에 코드를 탑재하는 것을 두고 게임 서버 개발자들 사이에서 반감이 굉장히 많았습니다. 그랬던 것이 요즘은 대부분 다 기본적으로 코드를 탑재해야 된다고 생각하시니 감사할 따름입니다. 격세지감도 좀 느끼고 그래요. 여튼 이러한 서버 코드 탑재와 함께 따라온 잘못된 관습(?!) 중에 하나가 클라이언트가 빌드될 때마다 서버에 클라이언트 해시 데이터를 업로드하는 것입니다. 보안 코드에서는 서버 쪽과 클라이언트를 비교해서 다른 경우에 끊겠다는 것을 목표로 개발된 기능인 것 같습니다. 매우 강한 결합 중에 하나죠.

이 경우에 실수가 발생할 소지는 굉장히 다분합니다. 관리자가 서버 쪽에 해시 데이터를 업로드 하는 것을 빼먹거나, 서버에 올린 해시 데이터와 다른 클라이언트가 패치 된다거나 하는 등의 실수 개입 요지가 많습니다. 근데 뭐 실수는 안한다고 차치하더라도 이 방법이 그다지 좋지 않은 이유는 대부분의 게임에서는 이런 조치가 전혀 필요하지 않기 때문입니다. 도둑이 모두 창문으로 넘나드는데 창문은 지키지 않고 정문을 유지보수 한다는 느낌일까요? 이런 이야기를 하면 꼭 그럼 정문은 안지켜도 되는거냐? 라고 물으시는 분들이 계신데 현실 세계에서 리소스는 항상 제한적입니다. 따라서 그 정문 지키는 리소스로 도둑이 드나드는 다른 곳을 지키는 것이 백배 더 현명하다는 이야기입니다.

그렇다면 게임 서버에 탑재되는 코드에서 해야 하는 일은 무엇일까요? 바로 실행 보장입니다. 게임보안 제품은 외부 라이브러리 형태로 제공되기 때문에 인터페이스가 노출되면 손쉽게 바이패스될 수 있습니다. 이런 경우를 검증하기 위함입니다. 즉, 모든 것들이 다 제거되고 우회되고 변경되더라도 단 하나 이거는 그래도 실행된다고 보장할 것이 필요하다는 것이죠. 그게 바로 서버에 적용된 코드가 해야 하는 일입니다. 실행 보장만 되면 변경된 지점을 찾아내는 것은 언제든지 할 수 있거든요. 하지만 실행 보장이 되지 않으면 백날 변경 지점을 찾는 코드를 넣어봐야 효과도 없습니다. 변경 지점을 찾는 그 코드가 변경될꺼거든요.

그리고 뭔가를 변경한다는 자체가 굉장히 오래된 발상입니다. 요즘 트렌드는 노모디피케이션입니다. 즉 아무것도 바꾸지 않고 바꾼 것과 동일한 효과를 내는 방법들이라는 것이죠. 왜냐면 해커들도 이제는 다들 알거든요. 뭔가를 바꿔서는 너무 쉽게 탐지된다는 사실을요.

#1
오토, 매크로 프로그램과 특정 기술을 무한대로 사용하게 하는 핵들은 게임 내 특정 함수를 불법적으로 호출하는 기능을 통해서 많이 구현됩니다. 예를 들어서 레이싱 게임이라면 부스터 가속을 시키는 함수를 들 수 있습니다. 외부 해킹툴은 그런 함수를 불법적으로 계속 호출합니다. 이렇게 만들어진 것이 무한 부스터 핵입니다. 이런 기법이 게임 개발자들 사이에도 많이 퍼져서 요즘 흔하게 나오는 질문 중에 하나가 보안 프로그램이 특정 함수를 보호할 수 있는 기능을 제공하는가 물어보십니다. 이 기능이 제공되면 게임 개발자가 생각하기에 위험하다고 생각하는 함수 주소를 전달해서 불법 호출을 막겠다는 생각인거죠.

과연 효과적인 전략일까요? 솔직히 무용지물입니다. 게임 내 랙만 증가시킨다고 보시면 됩니다.

일단 기본적으로 반환 주소 체크 기법은 return address morphing이란 테크닉에 의해서 너무 쉽게 무력화됩니다. 그런 기법을 방어하기 위해서는 코드를 트레이싱해서 추적해야 하는데 해당 게임 함수 런타임에 이런 체크를 깊이 하게 되면 부하가 증가됩니다. 따라서 일정 범위 밖에 체크를 못하죠. 즉 우리가 N Depth를 추척하는데 상대가 N+1 Depth를 사용하면 그냥 우회된다는 겁니다. 참고로 이런 것들을 인식하고 N Depth를 검사하도록 넣은 보안제품도 잘 없답니다. 안타까운 현실이죠.

근데 더 안타까운 사실은 이러한 체크 기법이 후킹이란 방식에 의존하는데 제작자 입장에서는 해당 후킹 포인트를 지나서 함수를 호출하는 것 또한 너무 쉽다는 겁니다. 물론 이것도 방어하기 위해서 동적 코드 분석을 한 다음에 애매한 위치에 알박기를 할 수도 있지만 소프트웨어의 견고성만 떨어질 뿐 크게 효과적이진 못합니다.

진짜 최고로 안습한 사실은 해당 함수 주소를 차단하면 해커는 다른 함수를 찾아낸다는 겁니다. 100% 말이죠. 그래서 효과적이지 않습니다. 여러분이 해커와 같은 속도 내지는 해커보다 빠른 속도로 해커가 다시 찾아낸 그 함수를 찾아낸다 해도 효과는 없습니다. 그냥 하나의 경쟁 루프를 만들어낼 뿐이거든요. 상호 패치 공방 밖에는 벌어질 수 없는 방식이란 거죠.

저희도 중국 유료 해킹툴에 이 기법을 몇 차례 적용했었는데 별 재미를 못 봤습니다. 왜 재미를 못봤냐구요? 우리는 일주일이 걸려 겨우 그 함수를 찾았습니다. 그래서 이제 막으면 원천 차단이 되겠다 생각했죠. 근데 넣고 하루만에 다른 함수를 호출하더군요. 다시 일주일을 분석하느니 그냥 그런 방법은 개나 주는 편이 낫겠다고 생각했습니다. 저희는 다른 방법을 선택했고 그 해킹툴은 3월 2일부로 영원한 수면에 들어가셨습니다. ㅋㅋ~ 이 사례를 통해서 전 교과서에 나오는 방법들이 생각보다 훨씬 더 실전에서 쓸모가 없다는 저의 생각을 좀 더 굳히게 됐습니다.

XINGCODE로 인하여 3월 2일부로 영면하신 그 비운의 해킹툴…

저희가 커플링 높은 인터페이스를 게임 개발자들에게 권하지 않는 이유는 이런 겁니다. 첫째는 게임 개발자가 스터디를 해야하는 보안 제품을 만들어서는 안 된다고 생각하기 때문입니다. 여려분이 자동차를 사는데 그 엔진 구조를 모르고는 시동도 걸 수 없는 차라면 사시겠습니까? 저같으면 그런 건 개나 줄 것 같거든요. 다른 하나는 이러한 테크닉의 경우 자칫 남용되거나 내부 구현을 모르고 사용했다가는 게임 내 부하만 증대 시키고 해킹툴을 막는대는 하나도 보탬이 되지 않기 때문입니다. 결국 출혈을 감수해 가면서까지 가져간 높은 커플링으로 볼 수 있는 재미는 하나도 없다는 이야기입니다.

물론 잘 아시겠지만 여러분이 모르는 그런 곳에 이 반환 주소 체크 기법을 사용해서 재미를 볼 수 있는 곳은 많이 있습니다. 당연히 그런 건 여기다 적진 않겠죠? 단물 좀 더 빼먹고 누구나 알게 될 즈음에 마치 제가 처음 발견한 마냥 교과서에다 공개하겠습니다. ㅋㅋ~

#2
끝으로 요즘 가장 핫한 데이터 중에 하나인 DirectX 인터페이스 주소를 좀 살펴보겠습니다. DirectX 가상 함수 변조를 체크하기 위해서 해당 인터페이스의 포인터를 전달 받는 것이 이 커플링 높은 방식의 골격같습니다. 하지만 이 또한 안타깝게도 큰 의미는 없습니다. 우선 전달받지 않고도 주소를 획득할 수 있고, 무조건 전달을 받아야만 막을 수 있는 방식이 한가지 있긴 한데 그 방식은 속도 상의 이유로 실전에서 거의 사용되지 않습니다. 또한 사용한다 하더라도 다른 루트를 통해서 검출할 수 있습니다.

전 이런 방식을 사용해 본 업체들에게 페르미의 역설처럼 한 번 여쭤보고 싶습니다. 달라고 하는 DirectX 인터페이스 주소를 가르쳐 주셔서 재미 좀 보셨습니까? 라구요. 아마 그닥 재미를 못 보셨을 겁니다. 인터페이스를 조작하는 해킹 방식은 제가 지난 번에 썼던 것처럼 Visual C++ 일주일 배우고 게임 커뮤니티에서 DirectX Hook Toolkit을 받아서 월핵 샘플따라 코딩하는 경우 외에는 잘 없거든요. 참 올드 패션드한 테크닉이란 이야기죠. 요즘은 저런 방식 자체가 전문 해커들에게는 거의 사용되지 않습니다. 물론 사용하는 해커가 있다면 그건 B급 해커겠죠. 그리고 그런 B급 해커가 조작하는 것들은 인터페이스를 받지 않아도 다 차단할 수 있답니다. 그러라고 비싼 돈주고 솔루션 사서 쓰는 것 아니겠습니까?

#3
게임보안 제품을 왜 사용하는 걸까요? 해킹툴을 막기 위함일겁니다. 그런데 요 근래에 몇몇 게임 업체와 미팅을 하면서 느낀 점은 보안 제품을 해킹툴을 막는 본연의 목적이 아니라 보안 제품한테서 컨설팅을 받고 계시더군요. 그것도 잘못된 보안 컨설팅을요. 그럴 때 마다 정말 안스러움을 느낀답니다.

해킹툴을 막는 데에는 여러 수천가지 방법이 있습니다. 제가 생각하는 가장 상책은 개발사나 운영사 모두가 전혀 신경 쓸 필요가 없는 방법입니다. 중책은 개발사에서 뭔가 정보를 알려줘야 하는 방법이고, 가장 하책은 게임 업데이트를 보안 업체에 신고해야 하는 방법입니다. 왠만하면 상책을 사용하는 업체와 일을 하세요. 그게 여러분에게도 보안 업체에게도 정신 건강에 이롭습니다. 물론 저희도 과거에 ‘나는 보안이다’라면서 하책을 남발했던 경험이 있습니다. 결과는요? 상호 멘붕상태가 오더군요. 그 때 깨달았습니다. ‘아 진짜 이거 없이는 답이 없겠다 하는 경우를 제외하고는 절대로 하책을 써서는 안되겠다’라고 말이죠.

다시 한 번 기억하세요. 여러분이 사용하는 솔루션은 해킹툴을 차단하기 위한 솔루션입니다. 게임의 보안 결함을 찾아서 나열해 주는 솔루션이 아니라는 이야기죠. 더 나아가서는 해킹툴 분석 보고서를 그럴듯하게 쓰는 솔루션도, 특허를 나열하는 솔루션도 아닙니다. 단지 여러분의 그 지긋지긋하게 골치아픈 해킹툴들을 알아서 잘 차단해 줄 솔루션을 선택하세요. 그럼 게임 내 보안 결함을 찾을 필요도, 해킹툴 분석 보고서를 쓸 필요도, 특허를 나열할 필요도 없거든요. 맞습니다. 카드를 쥐고 있는 한 여러분이 갑입니다. 이제는 갑의 권리를 다시 찾아 올 때 입니다. ㅋㅋ~



right now는 빼도 된다는 거 아시죵? ㅎㅎ~



Read more: http://www.jiniya.net/wp/archives/5823#ixzz3NCTcq8SA

반응형
반응형

끝나지 않은 전쟁: 논클라이언트 봇
by 신영진(YoungJin Shin), codewiz at gmail.com, @codemaruhttp://www.jiniya.net

보안 프로그래밍
끝나지 않은 전쟁: 논클라이언트 봇

소개
영겁의 세월을 두고 진화하는 우주처럼 보안에서의 창과 방패의 싸움도 영원히 이어질 수 밖에 없는 구조로 되어 있다. 여기서는 그런 이슈 중에 하나인 논클라이언트 봇과의 전쟁에 대해서 살펴본다. 논클라이언트 봇의 정의 및 구조적 특징과 함께 그간 논클라이언트 봇에 대응하기 위해서 논의되었던 전략들에 대해서 알아보도록 하자.

필자소개
신영진 codewiz@gmail.com, http://www.jiniya.net
웰비아닷컴을 창업해 게임보안 제품을 개발하고 있다. 시스템 프로그래밍에 관심이 많고 다수의 PC 보안 프로그램 개발에 참여했다. Microsoft Visual C++ MVP, 데브피아 Visual C++ 섹션 시삽으로 활동하고 있다. Steve Barakatt과 그레이 아나토미의 광팬이며 한때는 WoW에 미쳤다. 한마디로 괴짜다.

필자 메모
“보안 프로그래밍”이라는 주제로 글을 부탁 받고는 어떤 내용을 담을지를 한참 고민했다. 정수 연산의 위험성 이라든지, 포맷 스트링 버그 라든지, 버퍼 오버플로우와 같은 보안 프로그래밍을 생각했을 때 누구나 떠올리는 다소 식상한 내용은 담고 싶지 않았다. 달리 생각하면 PC 클라이언트 보안을 하는 업체에서 10년 가까운 세월을 일했지만 감히 “보안”이라는 주제로 글을 쓰기가 두려웠는지도 모르겠다. “보안”이라는 것은 너무도 넓고 너무도 방대한 내용들을 포함하고 있기 때문이다.

식상한 주제는 피하고 싶고, 모르는 것에 대해서 글을 쓰는 것만큼 멍청한 일도 없으니 결국은 내가 그간 했던 고민 중에서 한 꼭지를 끄집어 내 보기로 결정했다. 여기서 설명할 논클라이언트 봇은 과거에도 심각한 문제였고, 현재에도 계속 문제가 되고 있다. 그럼에도 글을 쓰고 있는 지금 이 시점에도 완벽한 대응 전략은 부재중인 상태다. 이 글을 읽고 여러분이 다소 자극을 받아서 혁신적인 새로운 대응 방법을 생각해 낸다면 그 또한 엄청나게 의미 있는 일이 아닐까라는 생각을 하면서 글을 시작해 보려고 한다.

혹시 앞서 언급했던 일반적인 “보안 프로그래밍”이라는 주제에 대해서 공부하고 싶은 독자라면 이 세 권의 책을 꼭 일독하기를 권한다. Writing Secure Code 2/e, 윈도우 비스타 보안 프로그래밍, Secure Coding in C and C++이 그 세 권의 책이다. 그 어떤 책보다도 여러분에게 일반적인 보안 프로그래밍의 관점에 대해서는 가장 많은 영감을 줄 수 있는 책이라고 감히 자부한다.

Introduction
이 문제는 여러분이 잘 알고 있는 튜링 테스트와 유사하다. 여러분의 여자친구 내지는 남자친구를 생각해 보자. 없다면 부모님을 생각해도 되겠다. 벽이 하나 있고, 반대편에 여러분의 사랑스런 그 사람이 앉아 있는 것이다. 여러분은 상대에게 질문을 할 수 있고, 상대는 여러분에게 대답을 할 수 있다. 이 질문과 답변 과정은 대리인을 통해서 이루어지며 그 사람은 벽 반대편 사람이 여러분이 알고 있는 사람인 것처럼 보이게 하기 위해서 질문과 답변 내용을 조작할 수 있다. 과연 이런 상황에서 여러분은 벽 반대편에 있는 상대가 여러분이 알고 있는 그 사람인지 아닌지를 구분할 수 있을까라는 것이 문제다. 물론 질문은 여러분이 확신이 들 때까지 계속 할 수 있다.

이러한 이야기를 하면 대다수 사람들은 제일 먼저 하고 싶은 질문으로 상대와 나만 알고 있는 은밀한 내용을 꼽는다. 처음 데이트 했던 장소 라던지, 상대의 신체적 특징, 기호 등을 묻는 것이다. 하지만 이는 벽 반대편에 어떤 엉뚱한 존재가 우리의 사랑스런 사람을 인질로 잡고 있다면 전혀 의미가 없다. 협박을 해서 답을 얻어낼 수도 있기 때문이다. 이런 극단적인 상황까지 고려한다면 우리는 이 문제를 풀기 위해서 상대에게 어떤 질문을 던져야 할까? 또 과연 그러한 질문을 통해서 벽 반대편에 있는 보이지 않는 대상의 정체를 판별할 수 있을까?

가볍게 설명한 이 문제가 오랫동안 보안 업계에서 이슈가 되고 있는 원격 클라이언트의 인증 문제다. 인증이라는 말만 듣고 반사적으로 아이디와 비밀번호를 생각했다면 번지수를 잘못 짚었다. 여기서는 원격 사용자에 대한 인증이 아니라 원격에서 실행되고 있는 클라이언트 프로그램 그 자체에 대한 인증이기 때문이다. 그렇다면 원격 사용자도 아닌 원격지 클라이언트의 인증이 왜 그토록 중요할까?

이 문제가 주요 이슈로 부각된 분야는 다름아닌 게임이다. 게임의 경우에는 게임 플레이를 사람이 할 수도 있지만 기계가 할 수도 있기 때문이다. 생각해 보자. 여러분이 온라인으로 철수와 바둑을 두고 있는데 사실은 알고 봤더니 철수가 둔 것이 아니라 철수 컴퓨터가 뒀다면 어떨까? 물론 여러분은 졌다고 가정했을 때 말이다. 별 느낌이 들지 않는다면 다른 사례를 생각해보자. 요즘 유행하는 MMORPG 게임은 해당 가상 세계 속에서의 행위를 통해서 캐릭터를 육성하는 방식으로 진행되는 게임이다. 이러한 캐릭터 육성 과정에는 필연적으로 시간과 노력이 들어간다. 그런데 여러분은 그런 것들을 실제 여러분의 시간과 노력을 투입해서 얻은 반면에 철수는 철수 컴퓨터가 대신 게임을 해서 그런 것들을 자동으로 얻었다고 한다면 어떤 느낌이 들까?

물론 두 가지 사례 모두 여러분이 게임을 하지 않는다면 단지 기분 나쁜 사건 정도로 생각할 수 있는 문제다. 자본주의 세상답게 진짜 문제는 머니(money)가 개입했을 때 발생한다. MMORPG같은 온라인 게임의 경우에는 해당 가상 세계에서 통용되는 화폐가 실제 화폐와 교환 가지를 가진다. 즉, 가상 세계의 1골드가 실제 세계에서 100원에 팔릴 수 있다는 말이다. 실제로 소위 유명한 게임들의 경우에는 이를 환율과 같은 시세로 표기하는 사례도 있다. 이러한 가상 세계에서 골드를 획득하는 방법은 보통의 경우 사냥을 통해서인데, 이는 상당히 단순한 반복 작업을 필요로 한다. 이러한 반복 작업을 기계가 대신하고 그것을 운영한 사람이 부가적인 수익을 얻는다면 그것은 과연 공정한 일일까? 더욱이 이런 기계 때문에 가상 세계 속에서 사람이 획득할 수 있는 리소스가 줄어든다면 말이다.

여기에는 두 가지 중요한 이슈가 내포되어 있는데 하나는 이러한 부정 게임 방식이 게임 운영에 나쁜 영향을 미친다는 점이다. 생각해 보자. 누구나 부정 행위를 통해서 게임을 한다면 과연 그 게임을 누가 멍청하게 자신의 힘으로 하려고 하겠는가? 아마 아무도 하지 않고 전부 기계들만 게임을 하다가 결국 그 게임은 아무도 하지 않는 게임이 되고 말 것이다. 다른 한 가지는 이런 프로그램들이 아이템 거래 시장을 왜곡 시킨다는 점이다. 아이템 거래 시장이란 앞서 말했던 게임 머니를 실제 머니와 교환하는 시장을 말한다. 일반적인 경우라면 그 시장을 통해서 통용될 수 있는 게임 리소스는 제한적일 수 밖에 없다. 하지만 봇 프로그램의 등장으로 그 속에서 획득할 수 있는 게임 리소스가 폭발적으로 증가했고, 그것들은 별 노력 없이 획득한 것이기 때문에 아이템 거래 시장에서 낮은 가격에 팔릴 수 있다. 이는 결과적으로 정직하게 노력해서 해당 리소스를 획득한 사용자들에게는 자신이 획득한 리소스에 대한 가치를 불법적으로 하락 시키는 행위가 되는 셈이고 결국 시장은 공정하지 않은 상태가 되고 만다.

앞서 언급한 여러 가지 이유로 부정 클라이언트 프로그램이 자동으로 게임을 하는 행위는 좁게는 게임 업체에게 그리고 넓게는 게임 사용자들에게 다시 피해가 가기도 하는 것이다. 따라서 이런 부정 클라이언트 프로그램을 막는 것은 게임 업체에게는 굉장히 중요한 당면 과제라고 할 수 있다.

논클라이언트 봇
게임 업계에서는 앞서 설명한 것과 같이 벽 반대 편에 앉아서 마치 우리가 알고 있는 사람인척 하는 나쁜 프로그램을 두고 논클라이언트 봇(Non-client Bot)이라고 부른다. 좀더 엄밀히 말하자면 논클라이언트 봇이란 게임 프로토콜을 모방해 게임 클라이언트가 아닌 별도의 클라이언트가 게임 서버에 접속해서 플레이어와 같은 행위를 하는 것을 말한다. 즉, 논클라이언트라는 말은 클라이언트가 없다는 것을 봇이라는 말은 자동으로 게임 플레이를 한다는 것을 의미한다.

이렇게 복잡하게 프로토콜을 모방해서 논클라이언트 봇을 제작하는 이유는 게임 클라이언트에서 로딩하는 복잡한 그래픽 리소스 등이 필요하지 않아서 한 PC에서도 여러 개의 논클라이언트 봇 프로그램을 띄워서 동시 작업을 원활하게 할 수 있기 때문이다. 동시 실행이 원활하다는 말은 달리 표현하면 단위 시간당 더 많은 게임 리소스를 획득할 수 있음을 의미한다. 이러한 이유로 논클라이언트 봇은 주로 작업장이라고 불리는 곳에서 은밀하게 제작되기 때문에 실제로 구동이 되는 프로그램을 구하기는 쉽지 않다.

<그림 1>에는 정상적인 게임 클라이언트와 서버의 통신 구조가 나와 있다. C는 게임 클라이언트를 S는 게임 서버를 의미한다. 이 그림이 나타내는 의미는 서버가 Q라는 패킷을 전달하면 클라이언트가 R이라는 응답을 하는 구조로 통신이 이루어진다는 것이다. <그림 2>에는 이 구조를 도용한 논클라이언트 프로그램의 구조가 나와있다. 클라이언트는 변조되거나 새롭게 만든 NC이지만 통신 구조를 그대로 모방했기 때문에 게임 서버 입장에서는 이 클라이언트가 정상적인 게임 클라이언트 프로그램인지 아닌지를 판별할 방법이 없다. 논클라이언트 봇을 구현하는 핵심 기술은 이러한 게임 서버와 클라이언트의 통신 구조를 도용하는 것이다.


그림 1 정상 클라이언트 통신 구조


그림 2 논클라이언트 통신 구조

프로토콜 변형
이러한 논클라이언트 봇의 등장을 맞이한 게임 개발자들이 취한 그 첫 번째 대응은 프로토콜 변형이었다. <그림 3>에는 이렇게 통신 방법이 변경된 그림이 나와 있다. 기존의 Q와 R이 Qa, Ra라는 새로운 값들로 변경되었다. 따라서 기존 논클라이언트 봇은 이 서버와는 통신을 할 수가 없다.


그림 3 프로토콜 변조 통신 구조

이 방식은 일견 기존의 논클라이언트 봇을 일망타진하면서 동시에 논클라이언트 봇 제작자들에게 새롭게 프로토콜을 분석해야 한다는 짐까지 던져주는 셈이기 때문에 굉장히 효과적인 대응으로 생각되기 쉽다. 하지만 이는 큰 착각이다. 그 사실이 잘못된 가장 큰 이유는 기존의 서비스되고 있는 서버, 클라이언트의 프로토콜 구조 전체를 새롭게 설계하는 작업은 전혀 소프트하지 않기 때문이다. 사실상 이러한 작업을 한다는 것은 거의 미친 짓이나 다름 없다. 보통의 경우에 서비스되고 있는 프로그램에서 선택할 수 있는 방법의 최대치는 새로운 패킷 추가나 기존 패킷의 상수 값을 변경하는 정도가 전부다. 하지만 이러한 것들은 이미 프로토콜 전체가 분석된 논클라이언트 봇 제작자 입장에서는 전혀 어렵지 않은 과제다. 단순히 변경된 부분만 추적하면 되기 때문이다. 결과론적으로 이 방법은 막는 입장에서는 굉장한 노력이 필요하지만 공격하는 입장에서는 그렇게 많은 노력이 필요하지 않다고 할 수 있다.

여기까지 이야기를 듣고 보통 나오는 첫 번째 반응은 그러면 패킷을 암호화하면 되지 않냐는 질문이다. 그런데 이 말은 지금까지의 논의를 전혀 이해하지 못해서 나오는 반응이다. 논클라이언트 봇 제작자가 날패킷(raw packet)을 살펴보지 않는 것은 아니지만 어차피 그들은 게임의 거의 모든 코드를 꿰고 있으므로 암호화는 그들에게 큰 장애물이 되지 못한다. 필연적으로 해독하는 코드가 클라이언트에 같이 존재할 수 밖에 없기 때문이다. 즉, 아주 뛰어난 해커를 가정했을 때 패킷 암호화라는 것은 그 효과가 0이라고 할 수 있다. 물론 현실세계에 존재하는 수많은 어중이떠중이 해커를 고려한다면 하지 않는 것 보다는 하는 것이 효과가 있지만 실제로 논클라이언트 봇을 제작할 수 있는 수준의 실력자들에게는 큰 효과를 보기는 힘들다.

키인증
본격적으로 보안 업체가 관여하면서 나온 첫 번째 생각은 키인증이다. 키인증이란 클라이언트와 서버만이 알고 있는 비밀스런 질문과 답변을 주고 받자는 것이었다. 이 방법은 서버에서 클라이언트로 특정 키를 전송하고 클라이언트에서는 그 키에 대해서 아주 특수한 연산을 한 다음 그 값을 서버로 전송한다. <그림 4>에 이러한 방식의 통신 구조가 나와 있다. 서버와 클라이언트는 사전에 약속된 아주 비밀스러운 연산 E를 각자 가지고 있다. 이 상태에서 서버는 클라이언트에게 Q를 보내고 클라이언트는 그것을 자신이 가진 연산 E의 인자로 전달해서 나온 결과, R을 서버로 전송한다. 서버에서는 다시 동일한 연산을 수행해서 클라이언트가 응답한 R이 자신이 계산한 답과 맞는지를 비교해서 정상 클라이언트인지 아닌지를 판단한다.


그림 4 키인증 통신 구조

은행에서 사용하는 보안카드를 게임 클라이언트에 적용했다고 생각하면 쉽게 이해할 수 있다. 서버가 보내는 Q는 보안 카드의 인덱스를 의미하고 E는 보안 카드에서 인덱스에 대응하는 값을 찾는 과정을 R은 해당 보안 카드에 기록된 보안 코드 4자리 숫자를 의미하는 것이다. 상당히 유사한 만큼 이 방식은 은행 보안카드와 동일한 맹점을 가지고 있다. 보안카드 자체를 분실하면 무용지물이라는 점이다. 즉, 우리가 아주 은밀하다고 앞서 언급했던 연산 E가 도용되면 해킹툴도 얼마든지 정상 클라이언트로 둔갑할 수 있는 것이다. <그림 5>에는 이러한 연산 함수 E 코드를 도용한 논클라이언트 봇의 통신 구조가 나와있다.


그림 5 키인증 함수를 도용한 논클라이언트 통신 구조

연산을 도용한다는 것은 무엇을 의미할까? 컴퓨터에서 말하는 연산은 결과론적으로는 수행되는 코드의 집합. 즉, 함수가 된다. 앞서 <그림 4>에서 살펴본 연산 E가 아주 단순하게는 XOR이라고 생각할 수 있다. Q, R이 4바이트 정수이고 E는 XOR인 것이다. 그 규칙을 모르면 통신 구조가 어려워 보이지만 규칙을 알면 쉽게 도용할 수 있다. 물론 논클라이언트 봇 제작자들이 이렇게 날패킷(raw packet)을 보고 규칙을 추론해 내는 것은 아니다. 그들은 클라이언트 게임 코드를 보고 그 코드를 직접 가져다 사용한다.

<리스트 1>에는 원본 클라이언트에서 사용했다고 생각할 수 있는 SomeFantasticFunction이라는 연산이 나와 있다. 여기서는 간단하게 산술 연산을 수행한 후에 결과 값을 돌려준다. 이 코드를 컴파일해서 실행 코드를 생성하면 해당 코드에 대한 어셈블리어 코드가 어딘가에는 존재한다. 논클라이언트 봇 제작자들은 그 코드를 찾는 것이다.

리스트 1 원본 연산 함수

  1. ULONG SomeFantasticFunction(ULONG Q)  
  2. {  
  3.     ULONG R;  
  4.     R = (Q >> 13) * 34 + 1573;  
  5.     return R;  
  6. }  

<그림 6>에는 IDA와 같은 정적 분석 툴을 사용해서 해당 코드를 찾아본 화면이 나와 있다. 코드를 보면 오른쪽에는 어셈블리어 코드가 나와 있고 왼쪽에는 바이트 코드가 나와있다. 뛰어난 논클라이언트 봇 제작자라면 함수 자체의 기능도 이해하겠지만, 사실은 그럴 필요도 없다. 이 함수가 특정 시점에 수행된다는 사실만 확인하고 나면 그냥 바이트 코드를 그대로 긁어다 사용하면 되기 때문이다. <리스트 2>에는 이렇게 도용한 함수 코드가 나와 있다. 보면 알겠지만 함수는 바이트 코드를 저장해놓고 실행 시점에 가상 메모리에 복사한 다음 해당 함수를 다시 호출하는 구조로 되어 있다. 여기서는 간단하게 컨셉만 보여주기 위해서 이렇게 코드로 만들었지만 실제로는 이렇게 복잡하게 코드로 구성하지 않고도 손쉽게 도용할 수 있는 다양한 방법이 존재한다. 어쨌든 이 이야기의 결론은 특정 코드가 무엇을 하는지, 내지는 어느 시점에 수행되는 지만 판단하고 나면 그 코드를 훔치는 것은 식은 죽 먹기라는 사실이다.


그림 6 IDA로 살펴본 SomeFantasticFunction

리스트 2 도용한 함수 코드

  1. typedef ULONG (*SFFT)(ULONG Q);  
  2. ULONG SomeFantasticFunction(ULONG Q)  
  3. {  
  4.     UCHAR code[] =  "\x55\x8b\xec\x81\xec\xcc\x00\x00\x00"  
  5.                     "\x53\x56\x57\x8d\x8d\x34\xff\xff\xff"  
  6.                     "\xb9\x33\x00\x00\x00\xb8\xcc\xcc\xcc\xcc"  
  7.                     "\xf3\xab\x8b\x45\x08\xc1\xe8\x0d\x6b\xc0\x22"  
  8.                     "\x05\x25\x06\x00\x00\x89\x45\xf8\x8b\x45\xf8"  
  9.                     "\x5f\x5e\x5b\x8b\xe5\x5d\xc3";  
  10.   
  11.     PVOID cptr = VirtualAlloc(NULL  
  12.                                 , sizeof(code)  
  13.                                 , MEM_COMMIT | MEM_RESERVE  
  14.                                 , PAGE_EXECUTE_READWRITE);  
  15.     if(cptr)  
  16.     {  
  17.         memcpy(cptr, code, sizeof(code));  
  18.         SFFT sfft = (SFFT) cptr;  
  19.         ULONG R = sfft(Q);  
  20.         VirtualFree(cptr, 0, MEM_FREE);  
  21.         return R;  
  22.     }  
  23.   
  24.     return 0;  
  25. }  

무결성 검사
E를 도용 당한 보안 업계의 반격은 무결성 검사로 이어졌다. 이는 말 그대로 클라이언트의 내용물을 검사해서 진짜인지 아닌지, 변조가 있었는지 없었는지를 판별하겠다는 말이다. 앞선 키 인증이 사랑하는 사람과 둘만 알고 있는 기억에 대한 질문이었다면, 이는 사랑하는 사람이 가진 고유한 신체적인 특징을 묻는 것이라고 할 수 있겠다.

<그림 7>에는 이러한 경우의 통신 구조가 나와 있다. 서버는 Q라는 랜덤한 값을 보내고 클라이언트는 그 값과 클라이언트 프로그램 자체를 해시 함수 H에 입력으로 넣는다. 그러면 H 함수는 클라이언트에서 Q로 표기된 부분의 해시 값을 구해서 R에 저장한다. 서버에서는 구해진 해시 함수를 가지고 변조된 클라이언트인지 아닌지를 판단하는 것이다.


그림 7 클라이언트 무결성 검사 통신 구조

이 방식의 가장 큰 특징은 연산 H의 입력으로 클라이언트 프로그램 그 자체를 넣도록 만들었다는 점이다. 따라서 클라이언트에 가해진 사소한 수정 사항도 모두 검사할 수 있다. 하지만 과연 이 방법으로 논클라이언트 봇을 막을 수 있었을까? 안타깝게도 아니다. 앞선 키 인증과 마찬가지로 논클라이언트 봇이 클라이언트 프로그램 자체를 도용한다면 여전히 무용지물이 되기 때문이다. <그림 8>에는 이러한 방식으로 게임 클라이언트 프로그램 C와 해시 함수 H를 도용한 논클라이언트 봇의 통신 구조가 나와 있다.


그림 8 클라이언트 및 해시 함수를 도용한 논클라이언트 통신 구조

앞서 함수를 도용하는 것에 대해서는 설명했다. 그렇다면 게임 클라이언트를 도용한다는 것은 무슨 의미일까? 그 말에 대한 해답이 <리스트 3>과 <리스트 4>에 나와 있다. <리스트 3>은 정상 게임 클라이언트의 응답 함수가, <리스트 4>에는 논클라이언트 봇의 응답 함수가 나와 있다. 이 둘의 차이를 보면 알겠지만 정상 클라이언트는 H 함수에 현재 실행되고 있는 프로그램을 입력으로, 논클라이언트 봇은 자신이 도용한 클라이언트 프로그램을 입력으로 넣는 차이를 가지고 있다. 즉, 이 경우에는 앞선 함수 도용처럼 Reply 함수를 통째로 도용하는 경우에는 동작을 하지 않겠지만 <리스트 4>에 나와 있는 것처럼 약간의 수정을 가한 후에 사용한다면 서버와 통신이 가능하다.

리스트 3 원본 응답 함수 코드

  1. ULONG Reply(pvoid received_data)  
  2. {  
  3.     R = H(GetModuleHandle(NULL), data);  
  4.     send(R);  
  5. }  

리스트 4 논클라이언트 봇 응답 함수 코드

  1. GameClient = LoadGameToMemory();  
  2.   
  3. ULONG Reply(pvoid received_data)  
  4. {  
  5.     R = H(GameClient, data);  
  6.     send(R);  
  7. }  

이 방법을 우회하는 논클라이언트 봇의 핵심은 <리스트 4>에 나와 있는 LoadGameToMemory 함수다. 이 함수는 게임 클라이언트를 로딩하는 역할을 한다. Windows 실행 포맷인 PE 파일의 구조는 이미 분석이 많이 되었기 때문에 이러한 별도의 실행 파일을 메모리에 올리는 작업은 무척이나 간단하다. 물론 실행 파일 자체에 자가 수정 코드나 폴리모픽, 메타모픽 등의 기법을 적용해서 단순히 로딩 만으로는 실제 구동되는 단계의 클라이언트와 다르도록 만들 수 있다. 하지만 이 또한 VM 환경을 사용하면 간단하게 해결할 수 있다. VM 환경이란 실제 CPU를 사용해서 구동하는 것이 아닌 가상의 메모리 공간과 가상의 CPU를 사용해서 게임 클라이언트 코드를 에뮬레이팅해서 푸는 작업을 말한다.

타이밍 체크
이 방법은 실상 필드에서는 큰 효과가 없는 방법이나 논문에서는 흔히 등장하는 해법 중에 하나다. <그림 9>에 나온 것처럼 해당 연산을 수행하는데 소요된 시간을 같이 보낸다는 것이 핵심 개념이다. 그림에서는 t가 E라는 연산을 수행하는데 소요된 시간을 나타낸다. 물론 t 값은 R안에 은밀하게 포장될 수도 있다.


그림 9 타이밍 체크를

우선 이 방법의 가장 큰 문제점은 범용 OS가 보통은 RTOS가 아니라는 점이다. 이는 시간 측정이 정확할 수 없다는 이야기다. 모든 스레드나 프로세스는 다른 스레드나 프로세스에 의해서 선점될 수 있고, 이는 곧 정상 환경에서도 실제 동작 시간이 지연될 수 있음을 의미한다. 또한 클라이언트가 구동되는 실행 환경은 천차만별이라는 점도 문제점이다. 같은 게임을 셀러론 CPU에서 실행하는 사용자도 있고, i7 쿼드 코어 컴퓨터에서 실행하는 사용자도 있다. 이 둘의 실행 시간은 아주 당연하게 전혀 같지 않다.

그리고 결정적으로 앞선 무결성 검사에서 살펴보았던 것처럼 연산 E는 도용뿐만 아니라 변조도 당할 수가 있다. 따라서 이 경우에는 정상의 경우 t가 일정한 값으로 나올 것이기 때문에 조작을 해서 실행 시간에 상관 없이 같은 시간을 보내도록 하면 손쉽게 우회할 수 있다.

물론 이렇게 여러모로 실전에서 사용하기에는 다소 부족한 점이 많은 방법이지만 이 방법을 소개한 이유는 이 방법의 경우 기존 방법과 달리 직접적인 값 보다는 간접적인 값을 통해서 논클라이언트 봇을 진단하려고 했다는 점이다. 즉, 기존 방식들이 예, 아니오의 답을 원하는 닫힌 질문이라면 이 방법은 주관식으로 답할 수 밖에 없는 열린 질문이라는 점이다. 즉, 시간이라는 변수 외에도 이러한 열린 질문을 통해서 정상 클라이언트를 판단을 할 수 있는 방법이 존재한다면 이 테크닉은 논클라이언트 봇에 대한 좋은 대응 방법이 될 수도 있다.

CAPTCHA
지금까지 우리는 이 문제를 논클라이언트와 정상 클라이언트를 판단하는 것으로 파악했다. 하지만 이것들 전혀 새로운 관점에서 기계와 인간을 구분하는 관점에서 바라본 해법이 CAPTCHA다. CAPTCHA(Completely Automated Public Turing test to tell Computers and Humans Apart)는 흔히 인터넷에서 자동 가입 방지용으로 사용되는 사람인지 기계인지를 테스트하는 방법을 말한다. <그림 10>와 같이 아주 이상하게 생긴 문자열을 보여주고는 그대로 다시 입력하라고 하는 방법이 대표적이다. 사실 사람도 이해하기가 쉽지 않다.


그림 10 사람도 이해하기 힘든 인증

이 방식을 논클라이언트 봇 문제에 적용하는 것은 간단하다. 위와 같은 이미지 코드가 Q가 되고 사용자가 입력한 문자열이 R이 되는 것이다. 물론 입력 받는 함수가 연산 E가 되겠다. 이 경우에는 CAPTCHA라는 방식 자체가 기계가 자동으로 답을 판단할 수 없도록 고안되었기 때문에 이론적으로는 사람이 개입할 수 밖에 없다. 즉, 자동으로 입력하는 것이 불가능해진다는 의미이고, 그렇다면 논클라이언트 봇은 자연스럽게 의미가 없어진다.

물론 이 방법도 논클라이언트 봇이 작정하고 자동화를 하겠다고 다짐을 한다면 할 수 있는 방법이 전혀 없는 것은 아니다. 서버가 가지고 있는 모든 이미지 코드에 대한 결과 값을 수집한 다음 해당 값들에 대한 매핑 함수를 제작하는 것이다. <그림 11>에 이러한 방법이 나와 있다. 모든 가능한 Q들에 대한 R을 구한 다음 그것 사이를 매핑하는 M 함수를 제작해서 자동으로 대답하는 방법이다. 하지만 이 경우에는 앞선 프로토콜 변경과 마찬가지로 논클라이언트 봇 제작자 입장에서는 무척 힘든 작업이지만 서버 쪽에서 요청의 내용을 교체하는 것은 쉽다는 특징이 있다. 즉, 서버 쪽에서는 데이터만 교체하면 순식간에 모든 논클라이언트 봇이 무력화되고, 논클라이언트 봇 제작자는 모든 요청과 결과에 대한 내용을 다시 수집해야 한다는 말이다. 더욱이 이런 교체 작업은 자동화 시킬 수 있기 때문에 논클라이언트 봇 제작자 입장에서는 이러한 M을 만들어서 대응하는 것은 사실상 의미가 없다.


그림 11 매핑 함수를 사용한 논클라이언트 봇

CAPTCHA 방식이 가지는 문제점은 이러한 논클라이언트 봇에 대한 대응력이 아닌 다른 곳에 있다. 바로 저 문자열을 입력하라는 귀찮은 과정을 게임 플레이 중간 중간에 하게 한다면 과연 어떤 사용자가 그 게임을 하고 있겠냐는 점이다. 사실 인터넷 사이트도 CAPTCHA가 적용된 곳은 진짜 필요한 곳이 아니면 가입이 꺼려지는 것이 사실이다. 더 중요한 사실은 이런 입력의 경우 기계뿐만 아니라 사람도 이해하기 힘들며 종종 틀린다는 점이다. 따라서 실제로 이를 게임이나 특정 클라이언트 프로그램에 적용하는 것은 실용적인 관점에서의 한계가 존재한다.

그렇다면 사람이 귀찮아 하지 않는 CAPTCHA 방식을 사용하면 괜찮을까? 그런 것들 중에 하나가 게임 상에서 GM(Game Master)들이 하는 말걸기가 있다. GM들이 필드에서 이상 행위를 반복하는 사람들을 보면 말을 걸어보고 제대로 된 대답을 하지 않는다면 자동 플레이로 간주하고 해당 사용자를 퇴장 시키는 방법이다. 그럴듯해 보이지만 요즘 나오는 논클라이언트 봇은 GM의 질문에 대한 몇 가지 대답들을 미리 준비해 놓거나 주변에 GM이 오면 자동으로 다른 곳으로 움직이는 기능을 추가하고 있는 경우도 있다. 따라서 이러한 비강제성 CAPTCHA 방식은 효과를 가지기가 쉽지 않다.

그럼에도 이 방식이 의미가 있는 것은 새로운 CAPTCHA 방식이 강제성을 가지지만 사람은 전혀 귀찮음을 느끼지 않고, 오히려 흥미를 느끼며, 사람이라면 거의 99.9%의 확률로 틀리지 않는 방식이라면 논클라이언트 봇을 손쉽게 무력화 시킬 수 있는 가장 강력한 방법이라는 특징 때문이다.

데이터 마이닝
끝으로 소개할 방법은 데이터 마이닝이다. 이 방법은 논클라이언트 봇 문제를 정상 플레이어와 비정상 플레이어의 문제로 바라본 해결책이다. 간단하게 소개하자면 서버 쪽에서 개별 플레이어의 행위를 모두 파악한 다음 그것을 토대로 데이터 마이닝을 해서 해당 플레이어가 정상의 범주에 속하는지 비정상의 범주에 속하는지를 판별한다는 것이다.

이론적으로는 굉장히 깔끔해 보이지만 실상 이 방법은 많은 단점을 가지고 있다. 우선 플레이어의 모든 행위를 기록내지는 실시간으로 판별해야 한다는 점이다. 이를 위해서는 추가적인 많은 리소스가 투입될 수 밖에는 없다. 그리고 그것을 자동적으로 판별하는 함수를 제작하는 것도 쉬운 일은 아니다. 논클라이언트 봇이 문제가 되는 MMORPG 류의 게임에서 정상의 플레이어 임에도 논클라이언트 봇처럼 플레이를 하는 사용자들도 많기 때문이다.

하지만 그런 모든 단점을 극복했다고 하더라도 정말 이 방법이 효과가 없을 수 밖에 없는 마지막 약점이 있다. 논클라이언트 봇이 정상의 범주와 같이 플레이를 한다면 이 방법으로는 절대로 논클라이언트 봇을 잡을 수 없다는 점 때문이다. 물론 그것이 문제가 되지 않을 수 있다고 생각할 수 있겠지만 기계가 사람을 대신하여 게임을 자동으로 플레이한다는 관점에서는 여전히 공평하지 않다.

결론적으로 다시 설명하자면 이렇다. 이 방법에서 생각하는 정상과 비정상을 구분하는 방법을 담고 있는 함수를 P라고 가정하자. 이 P라는 함수가 정상의 범주를 좁히면 좁힐수록 정상 플레이어도 차단되는 경우가 생기는 문제가 발생한다. 반면에 이 P라는 함수가 정상의 범주를 넓히면 넓힐수록 비정상 플레이어가 차단되지 않는 경우도 생긴다. 결정적으로 논클라이언트 봇 제작자는 이 P의 범주를 파악해서 딱 그 범주의 최대치만큼만 부정적인 방법으로 플레이를 한다면 이 방법은 무용지물이 된다. 즉, 이 P의 판단 범주를 어떤 범위에 두더라도 논클라이언트 봇은 항상 그것을 통과할 수 있다는 점에서 이 방법은 좋은 대안이 되기는 힘들다.

끝은 없다
여기까지가 그간 논클라이언트 봇에 대응하는 게임 업계, 보안 업계, 그리고 학계의 생각들이다. 하지만 개별 항목에서 설명한 것처럼 하나같이 단점 없는 방법이 없으며, 완전하게 논클라이언트 봇을 탐지할 수 있는 방법은 더더욱 없다. 그래서 이 문제가 더욱 매력적인지도 모르겠다.

다시 제일 앞 소개 글에서 했던 질문을 해보자. 과연 여러분은 질문을 통해서 벽 반대편에 있는 사람의 정체를 판단할 수 있을까? 결론은 불가능하다. 왜냐하면 질문과 답변이라는 메커니즘 자체가 이미 결정돼 있는 것들이기 때문이다. 여러분이 질문을 던지는 순간 그 해답은 이미 나와 있는 것이라는 말이다. 이는 결국 그 질문들을 미리 다 알고 있는 상대라면 얼마든지 여러분의 판단을 흐리게 만들 수 있다는 것을 의미한다.

하지만 이러한 이론적인 결론 때문에 기죽을 필요는 없다. 실제 현실 세계에 존재하는 논클라이언트 봇 제작자가 우리가 가정한 신적인 존재는 아니라는 점 때문이다. 그들도 결국 사람이기 때문에 모든 코드들을 한번에 다 파악할 수는 없다. 문제는 얼마나 적은 리소스를 사용해서 효과적으로 그들을 괴롭히는가에 달려있다.

이런 이야기가 끝날 즈음엔 사람들은 항상 왜 보안 업체가 완벽하게 막을 수 없는지를 궁금해 한다. 그러면서 잘 생각해 내면 뭔가 마법 같은 방법으로 이 모든 것을 해결해 낼 은탄환(silver bullet)이 있지 않을까라는 생각도 덧붙인다. 이 싸움이 원숭이나 개나 고양이와의 싸움이었다면 그런 은탄환이 존재했을지도 모른다. 하지만 이 싸움은 적어도 우리와 동등한 내지는 우리보다 더 나은 지능을 가진 사람들과의 싸움이다. 우리가 완벽하게 막을 방법을 생각해 낸다는 건 그들이 완벽하게 우회할 방법을 생각해 낼 수 있다는 말과 같은 의미다. 결국 이 싸움은 뫼비우스의 띠처럼 끝없이 반복될 수 밖에 없는 구조로 되어 있는 것이다.



Read more: http://www.jiniya.net/wp/archives/4372#ixzz3NCSwNayB

반응형
반응형

게임 보안: 인력에 관한 불편한 진실…
by 신영진(YoungJin Shin), codewiz at gmail.com, @codemaruhttp://www.jiniya.net

국내 게임보안 업체 중에서 우리 회사가 가장 영세하다. 가장 후발 주자다. 그러다보니 업체 미팅을 하다보면 으례 듣는 이야기가 있다. 그 인력을 가지고 지원이 되겠냐는 이야기다. 그런데 아이러니한 사실은 그렇게 이야기 했던 많은 업체들이 사용하고 난 다음에는 지원이 참 좋다는 이야기를 한다. 해킹툴을 잘 잡는다는 이야기도 한다. 왜 그럴까?

여기에는 게임 업체에서는 전혀 생각하지 못하는 정말 불편한 진실이 숨어있기 때문이다. 바로 MPS다. MPS가 뭐냐고? Man Per Site다. 사이트당 몇 명의 인력이 대응하는지를 나타내는 수치라고 생각하면 되겠다. 과연 이 수치가 얼마일까? 1정도는 될까? 당연히 안된다. 1이면 게임보안 업체는 아마 한 십년 전에 모두 망했을 것이다. 그렇다면 어느 정도일까? 여러분이 생각하는 것보다 훨씬 낮다. 0.1이 되는 업체가 잘 없다. 즉, 게임 10개 사이트에 한 명이 채 되지 않는다는 말이다. 반대로 말하면 한 사람이 열 개 이상의 사이트를 커버해야 한다는 말이다. 이러니 무슨 보안이되고 무슨 해킹툴 컨트롤이 되겠는가? 당연히 되지 않을 수 밖에 없다. 메이저 업체들이 그렇게 해킹툴을 못막는 이유가 바로 여기 있는 것이다. 관심이 없어서가 아니다. 사람이 없는데 무슨 수로 해킹툴을 막겠는가? 당연히 우리는 0.1은 넘는다. 아직까지는. 그러니 다른 업체보다는 잘 할 수 밖에 없는 것이다.

그렇다면 이 MPS 수치가 어느 정도면 해킹툴 제어가 가능할까? 내가 생각하는 최소 수치는 0.57이다. 무슨 근거로 그런 이야기를 하느냐고? 바로 4명이서 7개 사이트를 커버하는 수준이다. 그렇다면 그 4명과 7개 사이트는 어디서 나왔을까? 우선 해킹툴이 출몰하는 정도로 인기있는 온라인 게임의 경우 통상 수출하면 7개국 정도는 깔고 가는 경우가 많다. 따라서 7개 사이트라는 말은 하나의 게임을 4명이서 컨트롤 한다는 말과 동일하다. 그렇다면 4명은 어떤 사람일까? 검색 한명, 분석 한명, 개발 한명, QA 한명이다. 이걸로 될까? 뻥안치고 저정도 인력 투입하면 거의 모든 해킹툴 사이트를 다 다운시킬 수 있다. 물론 기반 시스템은 모두 갖추어져 있다는 전제 하에서다. 기반 시스템 없이 4명이서 무얼 하겠는가? 또 물론 그 4명이 겁나 똑똑하다는 전제하에서다.

그런데 정말 안타까운 현실은 많은 게임 업체들이 해킹툴 차단에 관심은 많다고 하지만 MPS 0.57에 대한 투자 조차도 인색한 경우가 많다. 국내 게임보안 업체들은 모두 연간 구독(annual subscription) 형태로 제품을 판매한다. 그런데 이 구독료가 십년 전이나 지금이나 별반 차이가 없기 때문이다. 지금 시세로 따져보면 그 일년 구독료가 통상 MPS 0.1에서 0.2 사이다. 그러니 회사 입장에서 차떼고 포떼고 하면 MPS 0.1 이상 투입하기가 힘든 것이다. 흙. 상황이 이러면 메이저 게임보안 업체에서라도 인식을 바꾸도록 테이블을 새로 구성해야 함에도 현실은 그렇지가 못하다. 같이 치킨 게임을 하는 경우가 다반사다. 이러니 게임보안 제품이 좋아지고 싶어도 좋아질 수가 없는 환경인 것이다.

자자. 여기저기서 웅성 거리는 소리가 들려오는 것 같다. 맞다. 규모의 경제가 있다. 일정 규모를 넘어서면 깔고가는 인력이 당연히 생기게 마련이다. 하지만 그 깔고가는 수많은 인력 중에서 여러분의 게임 게시판 내지는 해킹툴 포럼을 모니터링해주는 인력이 있다고 생각하면 정말 큰 오산이다. 안한다. 왜냐고? 그 깔고가는 수많은 인력은 말 그대로 공유인력이기 때문이다. 그들은 그런 일외에도 할 일이 너무도 많기 때문이다. 그러면 스스로 알아서 한 번은 하지 않을까? 라고 생각할 수도 있는데. 뻥안치고 안한다. 사람이기 때문이다. 잠깐 짬나면 나가서 담배펴야지 그거 모니터링 하겠는가? 난 다른 일도 많이 했는데, 라고 생각하면서 신나게 나가서 담배피게 돼 있다. 그러니 공유인력을 계산에 넣는다는 자체가 넌센스다.

그러면 다음으로 나오는 이야기들이 있다. 기술을 개발해서 원천 차단을 하면 되지 않겠냐는 것이다. 근데 이게 진짜 정말 골때리는 이야기다. 아니면 게임 업체들이 하는 오해일 수도 있겠다. 그런 오해를 하는 많은 분들을 위해서 조금 원천차단이라는 말의 실제 의미를 풀이해보자면 이렇다. 모든 게임보안 업체에서 말하는 원천차단이라는 말은 Visual C++을 일주일 배우고 나서 해킹툴 커뮤니티에서 소스를 퍼다가 변수 오프셋을 조금 수정해서 해킹툴을 만드는 경우에 대한 대응을 자동으로 할 수 있는 기술이라고 이해하면 되겠다. 물론 어러분도 잘 알고 있는 것처럼 안타깝지만 이것 조차도 잘 안되는 경우도 많이 있긴 하다. 그러니 어떤 업체와 미팅을 하는데 원천차단에 대해서 이야기를 한다면 ‘아 이건 코흘리게들을 막는 기술인데 다른 업체는 이것도 안되나 보구나’라고 이해하면 되겠다.

저 수많은 초록색 Undetected!!! 현기증이 ㅠㅜ~
하지만 MPS 0.57에 좋은 게임보안 제품을 사용한다면 저 목록에서 빠지는 것도 불가능 한 일은 아니다. 

그럼 게임 업체들을 고민에 빠뜨리는 진짜 해킹툴들은 어떤 것들일까? 저런 꼬맹이들이 코흘리면서 만드는 장난감일까? 당연히 아니다. 게임 업체를 고민에 빠뜨리고, 게임 서비스 자체를 종료시키게 만드는 유료핵들은 수년간 Visual C++을 갈고 닦았고 (<== 해킹툴 소스가 더럽다고 생각하면 오산이다.) 어셈블리에도 능통하면서, 리버싱도 잘하는 그런 애들이 알바해서 돈을 벌려고 만드는 것들이다. 물론 심한 경우에는 기업에서 하는 경우도 있다. 심지어는 보안회사, 게임회사보다 그 기업이 더 클 수도 있다. 여기에다 원천 차단이란 말을 쓴다는 건 내년까지 단 한번도 비가 오지 않을 거라는 걸 보장하는 것과 주식이 5000까지 쭉 갈거라는 걸 보장하는 것과 똑같은 의미다. 미래에 일어날 일에 대한 보장이란 것이다. 당연히 말도 안되는 소리라는 것을 알 수 있다. 여기엔 원천 차단이란 있을 수도 없고, 그런 말을 쓰는 것도 넌센스다.

그렇다면 무슨 차이가 있는가? 단지 해킹툴이 나왔을 때 얼마나 신속하고 정확하게 대응하느냐의 차이 밖에는 없다. 당연히 여기서 대응한다는 말은 그 지겹도록 매일 올라와 있는 초록색 Undetected라는 말을 적어도 이틀 이상은 빨간색 Detected라는 말로 바꾸는 경우를 말한다. 이 정도 억제력을 우습게 생각할 수 있다. 그렇다면 여러분이 사용하는 보안 제품 업데이트 이후에 해킹툴 페이지를 꼭 방문해 보길 바란다. 그러면 이 이틀이란 시간이 얼마나 영겁의 시간인지를 금방 알 수 있을 것이다. 물론 여러분이 선택한 보안 제품이 진짜 탁월하다면 그 목록에서 여러분의 게임이 영원히 제거되는 기적을 경험케 해 줄 것이다.

누구나 공정한 게임을 하고 싶어한다. 그래야 재미있기 때문이다.

게임 시장은 점점 더 커져가고 있다. 당연히 이러한 블랙마켓도 점점 커지고 있다. 더불어 요즘 온라인 게임의 출시에는 항상 이런 질문이 따라 다닌다. 안티 치트 솔루션은 무엇을 사용하는가? 치트 대책은 무엇인가? 이런 것들이 이제는 일반화 됐다는 이야기고 또 온라인 게임에 있어서는 굉장히 중요한 이슈라는 의미일 것이다. 자 이제 결론을 내려보자. 그렇다. 회사에 직원이 많다는 이야기가 여러분의 게임에 관심을 가지는 사람이 많다는 것을 의미하는 건 아니다. 또 해킹툴을 더 잘 막고 관리하기 위해서는 MPS 0.57이라는 최소한의 투자는 이루어져야 한다. 이런 것들이 이루어진다면 클린한 온라인 게임 환경을 만드는 일이 결코 불가능한 일은 아니다.

아직 이 모든 이야기의 맥락을 눈치채지 못한 분들을 위해서 딱 한 마디만 더 하자면, ㅋㅋ~
게임보안 하면 XIGNCODE, XIGNCODE하면 게임보안이라는 거. 답 딱 나온다. ㅋㅋ~


고기도 먹어 본 놈이 잘 먹고, 연애도 해 본 놈이 잘 하고, 해킹툴도 막아 본 놈이 잘 막는다.
우리는 최고의 팀을 가지고 있다. 그게 바로 우리가 이런 이야기를 들을 수 있는 이유다.





Read more: http://www.jiniya.net/wp/archives/6174#ixzz3NCS7CdOg

반응형
반응형

게임 보안: 데이터 마이닝에 관한 소고
by 신영진(YoungJin Shin), codewiz at gmail.com, @codemaruhttp://www.jiniya.net

요즘 게임 보안 업계에 부는 바람 중에 하나가 데이터 마이닝이다. 빅데이터와 클라우드같은 요즘 가장 핫한 키워드와 어느 정도 관련성을 가지고 있기도 하고 학계에서 몇 해 전부터 꾸준히 밀고 있는 방법이기도 해서 업계의 조명을 한몸에 받고 있다.

그렇다면 데이터 마이닝 과연 효과적일까? 사실 난 현재 수준의 데이터 마이닝에 대해서는 조금 회의적이다. 지난 글에서도 밝힌 것처럼 지금 시도하는 내지는 주장되는 데이터 마이닝이라는 기법은 그 태생적인 한계를 가지고 있기 때문이다. 그럼에도 요즘 게임 업계에서는 마치 기존 게임 보안 솔루션보다는 데이터 마이닝 쪽의 접근이 훨씬 더 근본적인 문제 해결 방법이라는 인식을 가지는 것 같아서 몇 가지 생각을 좀 덧붙여 보려고 한다.

#0. 비용
엔씨소프트의 경우 리니지, 아이온, 블소와 같은 게임 로그 데이터가 하루에 2-3TB 가량이 쌓이고, 이를 2개월 가량 저장할 수 있는 데이터 마이닝 시스템을 구축했다고 한다. 이 시스템을 통해서 오토나 작업장 같은 부정 행위를 저지르는 사용자들을 색출해 내고 있다고 한다. 2-3TB를 60일 저장할 수 있다면 그것만해도 180TB의 저장 공간이 필요하다. 오류 등을 생각한다면 더 큰 저장 공간이 필요할 것이다. 이를 안정적으로 저장하고 관리하는 시스템을 구축하는 일만해도 보통이 아니다.

하지만 문제는 데이터 저장이 아니다. 그 저장된 자료에서 의미있는 데이터를 뽑아내는 과정이 또 보통이 아니다. 오픈 소스 플랫폼을 사용해서 구축했다고 하는데 관련 분야의 인력이 부족한 실정을 고려해 본다면 결국 데이터 마이닝 시스템을 구축하는데에는 생각보다 많은 비용이 든다는 것을 알 수 있다. 게다가 아직까지는 게임 데이터를 마이닝하기 위한 맞춤형 솔루션을 제공하는 업체도 없기 때문에 바닥부터 모든 것을 직접 다 만들어야 한다.

이쯤되면 결론이 나온다. 선도적인 업체에서 투자적인 목적, 내지는 훨씬 더 높은 수준의 사용자 감시 체계를 만들고 싶은 때에는 유효하겠지만 그렇지 않은 대다수 게임 업체제에서는 비용적인 측면 때문에 도입 자체가 쉽지 않다는 것이다.

#1. 속도
데이터 마이닝, 말만 들으면 뭔가 우아하고 근사해 보인다. 하지만 이 데이터 마이닝이 힘들 발휘하려면 결국 데이터가 쌓여야 한다. 데이터가 없으면 이 시스템은 그냥 깡통이다. 데이터가 쌓여야 한다는 말은 다르게 설명하면 플레이어가 시스템 기준치에 부합하는 부정 행위를 지속적으로 저질러 주어야 한다는 것을 의미한다. 이렇게 상당한 시간동안 시스템의 감시망에 부합하는 부정 행위를 저지르면 그 윤곽이 마이닝 결과를 토대로 드러나게 되고 그 사용자에 대한 제재를 할 수 있다. 현재 데이터 마이닝 시스템은 속도가 느리고, 그마저도 아주 느린 사후 대응을 할 수 밖에 없는 구조다.

데이터 마이닝을 사용하는 업체들이 가지는 유일한 제재권은 계정 블럭이다. 계정 블럭도 좀 생각을 해 볼 필요가 있다. 크게 게임 결제 방식에 따라 두 가지로 나뉜다. 부분 유료화 게임과 계정비를 받는 정액제 게임이다.

부분 유료화 게임의 경우 계정 블럭은 아무런 효용가치도 없다. 다시 계정을 생성하면 그만이다. 이 경우에 다시 세부적으로 나뉘게 되는데 우리 나라처럼 주민 번호라는 희소화된 식별 체계를 가진 경우에는 그나마 다행이다. 하지만 이런 체계를 가진 해외 국가는 거의 없다. 따라서 국내에서 계정 블럭을 사용할 수 있더라도 해외에서는 이를 사용하기가 쉽지는 않다. 물론 XIGNCODE3 와 같은 요즘 게임 보안 솔루션의 경우에는 이런 계정 블럭을 도와줄 수 있는 보조 수단들을 제공하고 있다. 그런 기능을 이용하면 좀 더 효과적으로 대응할 수 있지만 부정 행위를 저지르는 시점에 차단할 수 없다는 근본적인 문제점은 여전히 존재한다.

다음으로 유료 게임이 있다. 유로 게임의 경우 계정 블럭이 의미하는 바는 블럭 당하는 사용자가 계정비를 날리게 되는 것을 의미한다. 즉, 그 계정비 만큼의 손해가 얼마나 큰지가 이 시스템이 유용성을 판단하는 근거가 된다고 할 수 있다. 그런데 이런 해킹 행위가 벌어지는 인기있는 유료 게임의 경우 계정비가 해킹을 시도하는 사용자 입장에서 의미있는 페널티가 되지 못한다. 실례로 부정 행위를 식별하는데 2일 정도의 시간이 필요하다고 가정 해보자. 그렇다면 이 2일의 시간동안 그 부정행위를 저지르는 사용자가 계정비 이상의 이익을 해킹을 통해서 창줄할 수 있다면 지속적으로 부정 행위를 저지를 수 있는 구조가 된다.

결국 여기서 중요한 쟁점은 ROE다. 안타깝게도 부정 행위를 색출해기 위해서 걸리는 시간 동안에 작업장에서는 그 계정비를 회수하고도 남는 만큼의 부가가치를 창출할 수 있는 경우가 많다. 만약 이 부가가치를 창출할 수 없다고 판단되면 작업장이나 해커는 사라진다. 그런데 진짜 정말 안타깝게도 그들이 사라지는 이유는 데이터 마이닝 시스템이 제공하는 계정비 + 캐릭 성장 비용 때문이 아니라 해당 게임의 인기가 시들해졌거나 작업장 과다 경쟁으로 아이템 가격이 지나치게 떨어진 경우가 대부분이다.

#2. 판단 기준
한 플레이어가 게임에서 한 시간내에 연달아 100번의 승리를 하는 것이 사실상 불가능 하다고 생각해보자. 게임 업체는 이를 토대로 기준을 세운다. 마이닝 시스템에서 한 시간내에 연달아 100번의 승리를 한 사용자를 찾는 것이다. 그런데 이런 모든 기준은 정상 사용자가 우연찮게 해낼수도 있기 때문에 게임 업체는 여기에다 허용 오차를 추가한다. 즉, 초범이라던지 어쩌다 한 번인 경우는 패스시켜준다는 의미다. 그걸 넘어서 지속적으로 한 경우에는 실질적인 계정 블럭을 시킨다.

말만 들으면 그럴듯 해 보인다. 하지만 말만 들었을 때 이야기다. 해커들이 어떻게 할 것 같은가? 영원히 저 룰을 못찾아서 방황하면서 모든 계정을 다 블럭당할까? 절때 아니다. 99번의 승리만 가져가면 잡히자 않는다는 것이 순식간에 퍼진다. 이걸 찾는데 아주 오랜 시간이 걸릴 것 같지만 아이러니하게도 생각보다 아주 손쉽게 발견된다. 심지어는 해커가 그걸 찾으려는 시도를 하지 않음에도 밝혀진다. 바로 사용자에 의해서 말이다. 인기있는 게임의 경우에는 해킹툴 사용자도 광범위하게 퍼져있고 그 사용자들은 제각각 다른 방식으로 해킹툴을 사용한다. 따라서 이런 집단 지성이 모이게 되면 모호할 것 같았던 게임사의 제재 룰도 연립 방정식이 풀리는 것처럼 손쉽게 단 하나의 해가 밝혀진다. 여기에 사람 심리가 더해지면 정보 공유 속도는 더 빨라진다. 해킹툴 사용자 중에 계정 블럭을 당하지 않은 사용자가 있다면 그걸 자랑하고 싶어하는 사람 심리가 생긴다. 마치 대단한 비밀을 알고 있는 것처럼 게시판에서 자랑질을 하게 되도, 그걸 또 증명하기 위해서 소수 사람들과 공유한다. 여기까지 가면 이미 모든 사람이 그 방법을 알게되는 것이나 다름 없다.

다시 돌아가보자. 게임 업체가 그래서 기준을 조정했다. 90번으로 줄이고 허용 오차도 좀 더 늘리도록 했다. 이번에도 해커는 기준치를 찾는다. 다시 조정한다. 70번에 허용 오차를 왕창 늘린다. 당연히 이번에도 찾는다. 그렇게 기준을 조정하다 보면 어느 순간에는 아주 일반적인 플레이어들이 심심치 않게 해낼 수 있는 수준까지 낮아진다. 마이닝 결과에 노이즈가 너무 많이 섞이고, 결국 데이터는 쓸모없게 되는 구조다.

#3. 치명적인 마이너리티 리포트
오진은 항상 문제다. 근데 이게 데이터 마이닝에서는 더 치명적이다. 예를들어 사소한 게임 보안 제품의 오진 같은 경우에는 정상적인 사용자의 경우에 다른 컴퓨터를 사용하거나 컴퓨터를 포맷하거나 하는 형태로 플레이를 이어갈 수 있다. 하지만 데이터 마이닝의 결과로 계정 블럭을 당하게 된다면 그 페널티는 엄청나다. 물론 사용자가 소명 절차를 걸쳐서 계정을 다시 복구할 수도 있겠지만 대다수 사용자는 그렇게 하지 않는다. 여기에는 사람의 감정적인 요인이 더 많이 작용하는데 정상적인 나를 이렇게 영구정지 시킨 게임을 내가 돈내고 해야 하는 이유를 찾을 수 없다고 생각하는 사람 심리 때문이다.

데이터 마이닝을 통한 계정 블럭은 진짜 최후의 수단이며 사용하지 않는 편히 오히려 게임 동접 건전성을 유지하는데 도움이 될지도 모른다. 왜냐하면 해커는 계정 블럭을 당해도 바로 새로운 계정으로 해킹을 이어갈 것이고, 정상 사용자는 사람 심리 때문에 그 게임을 떠날 것이기 때문이다. 결국 오토만 남아 있다가 그마저도 블랙 마켓이 힘을 잃는 순간 게임 속에는 아무도 없는 처참한 현실이 도래할 수 있다. 실제로 작년 하루에 2-3만 개씩의 오토 사용자를 색출해 낸다는 패기를 보여준 온라인 게임이 있었다. 하지만 패기는 패기로 끝난 것 같다. 오픈하고 얼마 지나지 않아서 인기가 시들해졌기 때문이다.

#4. 데이터 마이닝 그럼 쓸모 없을까?
약점들을 이야기 했지만 현재 수준의 데이터 마이닝도 완전히 쓸모 없지는 않다. 데이터 마이닝 시스템의 가장 큰 장점인 알려지지 않은 해킹툴에 대해서 높은 대응력을 구축할 수 있다는 점 때문이다. 마이닝 시스템을 페널티 없이 모니터링 용도로만 사용하더라도 의미가 있다는 말이다. 어느 정도의 해킹툴 사용자가 존재하는지 아는 것과 모르는 것은 천지 차이기 때문이다. 또 그 사용자의 시스템을 좀 더 집중적으로 분석 한다면 알려지지 않은 해킹툴의 실체에 대해서 판단할 수 있고, 더 나아가서는 모니터링 단계에서 색출한 사용자에 대해선 일반적인 경우와 다른 높은 수준의 보안 정책을 제공할 수도 있다.

몇해전 우리가 야심차게 진행했다가 포기한 내용 중에 하나가 화이트 기반 정책이 있다. 세상의 모든 깨끗한 바이너리 파일이 웰비아닷컴을 통하도록 만들자는 계획으로 야심차게 진행했지만 게임 업체들의 이해 관계와 깨끗한 바이너리 파일이 우리가 당초 예상한 것보다 너무 많다는 이유 때문에 결국 우리는 방향을 틀었다. 하지만 제한된 환경에서는 이런 것들이 의미있을 수 있다. 일례로 최근에 우리는 서비스하고 있는 게임 퍼블리셔로부터 온라인 게임 대회 서버에만 좀 더 높은 수준의 보안 정책을 유지할 수 있냐는 질문을 받은 적이 있다. 대회 서버에는 제한된 사용자가 참여하고 악성코드와 같은 엉뚱한 것들을 허용할 필요가 없기 때문에 화이트 정책을 사용하기에 안성 맞춤이다. 마찬가지로 데이터 마이닝 시스템의 결과물을 게임 보안 솔루션에 접목한다면 좀 더 효과적인 페널티 정책과 함께 높은 수준의 게임 보안 서비스를 유지할 수 있을지도 모른다.

세상은 0.1%의 프론티어와 0.9%의 동조자들 그리고 99%의 잉여로 돌아간다고 한다. 그러니 쓸모에 관계없이 여유 있는 게임 업체라면 이러한 방향의 연구를 지속적으로 수행하는 것이 바람직하다. 그래야 지금의 우리는 아닐지 몰라도 우리의 자식 세대, 내지는 입는 컴퓨터를 초딩때부터 착용할 자식의 자식 세대에는 보다 유쾌하고 공정한 온라인 게임 환경을 접할 수 있을테니 말이다.

덧) XIGNCODE3 개발팀에서는 기존 방식과는 다른 데이터 마이닝과 같은 대안적 게임 보안 솔루션에 대해서도 관심이 많습니다. 이런 백엔드 시스템 구축에 관심이 있거나 경험이 있는 분, 파이썬을 다룰줄 알고 웹 프로그래밍을 어느 정도 할 수 있는 분을 찾고 있습니다. 적어놓고 보니 뭐 맥가이버군요. 작은 회사들이 찾는 사람이 다 그렇겠지만 ㅠㅜ~ 어쨌든 이런 일들에 관심 있으신 분들은 codewiz at wellbia.com으로 연락 주세요.


xigncode-t

해커는 피하고 게이머는 원하는…

평판은 하루 아침에 만들어지지 않습니다.

XIGNCODE3는 여러분의 게임을 공정하게 만드는 가장 손쉬운 방법입니다.



Read more: http://www.jiniya.net/wp/archives/10597#ixzz3NCRfTFtx

반응형

+ Recent posts