본문으로 바로가기
728x90
반응형
728x170

왜 Deterministic은 해킹에 취약한지에 대해 알아보도록 하죠.
어떻게 보면 당연한데 Deterministic방식 자체가 Input Set에 기반하고 있습니다.
그러니까 Input을 똑같이 공유하는 형태로 해서 게임 상태를 맞추는 방식이기 때문에 상대방이 보낸 Input에 의존할 수 밖에 없죠.
그래서 상대방이 보낸 Input을 인정하게 되면 상대방이 Input을 조작하더라도 발견하기 힘듭니다.
그래서 해킹에 취약할 수 밖에 없는데 이것을 방지하기 위해서는 이 Input이 정당한지 검사해야 합니다.

 

이 방식은 크게 2가지로 볼 수 있는데 서버를 이용하는 방식이 있고, 서로가 서로를 검사하는 방식, 방장이 검사하는 방식이 있는데

패킷을 서로 상대방에게 보낼 때 상대방이 이 패킷이 정당한 것인지, 지금 상황에서 나올 수 있는 Input인지를 검증하는 것입니다.

서버를 이용하는 방식은 중계서버를 중앙에 두어 중계서버가 심판이 되서 중계서버로 모든 패킷이 가니까 이것을 살포하기전에 검사하는 방식입니다.


이 방식의 문제점이 있는데 패킷을 검사하려면 당연히 현재 게임 상태, 상황 룰에 대해 알고 있어야 합니다.

예를 들어서 FPS게임을 만든다고 쳤을 때 FPS 게임에서 벽이 있고 벽너머에 상대가 있을 때 벽너머에 있는 상대방에게 총을 쏴서 맞추면 정당한 패킷이 되지 못합니다.

그래서 벽너머로 총을 쏠 수 없다는 룰을 서버가 알고 있어야 하고, 두 플레이어 사이에 벽이 있다는 게임 상태를 서버가 알고 있어야 합니다.

그런데 중계서버를 만든다는 것은 게임 상태와 룰을 알 필요가 없어요. 그냥 패킷이 오면 그 패킷을 그냥 모든 클라이언트에게 보내주면 되는 서버기 때문에 게임 상태와 게임 룰을 알 필요가 없죠.

여기서 게임 룰이라고 말했는데 게임 코드를 의미합니다.

그렇기 때문에 중계서버를 State가 없는 Statelss라고 하는데 State가 없는 중계서버를 유지할 수 만 있다면 하나의 중계서버를 가지고 여러 게임을 동시에 서비스 할 수 있습니다.

그런데 중계서버가 게임 상태와 룰을 가져야 한다고 하면 게임마다 중계서버가 달라져야 하기 때문에 같은 중계서버로 여러 게임을 서비스 할 수 없는 단점이 생기게 됩니다.

 

FPS게임을 예로 들었을 때 공격쪽에 유리하게 되어 있습니다.
A라는 사람이 B에게 에임을 두고 총을 쐈을 때 이 때 B가 이동중이라고 쳐보죠.
우리가 보는 상대방의 위치는 항상 Deterministic 네트워크 방식에서는 일정시간 과거의 모습을 보게 됩니다.

A가 쏜 패킷이 B에게 롤백을 해서 적용되는 시점이기 때문에 어느정도 일정 딜레이가 있기 때문에 A가 보는 것은 일정시간 과거의 현재 모습이죠.

그래서 A가 총을 쏜 패킷이 적용 될 때 B의 이동된 시점에서 적용 되기 때문에 내가 쏜 게 현재 캐릭터의 위치가 아닌게 되어 항상 빗나갈 수 밖에 없습니다.

이 문제를 해결하기 위해 공격자를 유리하게 합니다.

그래서 실제 캐릭터의 위치는 다른 곳에 있다 하더라도 쏘는 컴퓨터의 입장에서 맞아야하는 상황이면 맞는 것으로 처리를 해줍니다.

그래서 흔한 버그 중에 '콜 오브 듀티'게임을 보게 되면 스핀샷이라고해서 화면을 막 회전시키다 총을 쏘면 상대방이 헤드샷으로 맞아죽는 그런 상황들이 있죠.

내가 움직인 거리, 돌린 거리만큼 오차를 보정해주는 게 있는데 그게 잘못 들어가서 상대방에게 맞아버리는 버그가 있고,

또 에임핵이라는 게 있는데 쏘는 대로 헤드샷을 맞춰주는 것입니다.

 

또한 심판이 있다 해도 정확하게 판정을 내릴 수 없습니다.

공격에 유리하게 판정되는 부분도 있고, 이동핵이라 쳐도 이동할 수 있는 거리가 있는데 초당 움직일 수 있는 거리가 10M다 했을 때 10M범위 안에선 얼마든지 이동할 수 있죠.

그런데 네트워크 딜레이가 있기 때문에 12 ~ 13M정도는 이동하게 해줍니다. 네트워크 딜레이가 있기 때문에 정확할 순 없어요.

그러면 해킹하는 프로그램은 움직일 때 항상 1초에 13M씩 움직이게 해주는데 그럴 때 심판이 이것을 보고 정당한 이동거리인지 아닌지를 판단하기가 어려워지는 문제가 있습니다.

이렇게 볼 수 있는데요. 내가 이동한 거리가 초당 13M를 이동했지만 한시간을 놓고 봤을 때13KM를 이동하는게 정상이다 했을 때 15KM를 이동하게 될 때 제제를 가할 수 있게 되는데 그 때 해킹툴은 13M까지만 이동하게 됩니다.

범위를 알아내는건 금방알아낼 수 있죠. 그래서 이 오차범위를 최대한 해킹하는 툴들이 나와서 상대방보다는 유리하지만 서버나 방장들이 이 패킷을 검증하기가 정말 어려워요.

 

그리고 어떤 플레이어가 헤드샷을 맞추는데 그 확률이 92%라고 했을 때 정말 그 사람이 에임이 좋아서 그런건지, 에임핵을 써서 그런건지 알 수가 없습니다.

만약 100%라 할 때 누가봐도 핵인것을 알겠지만 92%라 한다면 핵인지 아닌지 애매하게 되는 것이죠.

그래서 해킹툴도 이것을 알기 때문에 알아내기 어렵게 하기 위해서 어느정도 게임이 인정하는 부분만 이득을 얻는 것입니다.

그렇기 때문에 Deterministic방식에서 (물론 Deterministic방식이 아니라 server authority방식도) 해킹에 취약하게 됩니다.

 

그러면 이 해킹을 막는 방법이 무엇인지 알아보죠.

심판이 있겠고, 오차범위 안에서 할 수 있는 것들을 판단하게 될 것입니다.
헤드샷율이 98%가 넘어간다던지, 이동거리가 초당 10M가 정상인데 20M씩 이동한다던지의 기준점을 둬서 체크하는 방법이 있는데 이 경계까지만 이득을 취하기 때문에 이 방법도 좋은 방법이 아닙니다.

 

또는 해킹을 보면 실행 파일을 실행한 상태에서 해킹 툴을 띄워서 사용하는 외부 해킹이 있고, 실행파일 자체를 조작하는 내부 해킹이 있습니다.

외부 해킹방식은 외부 해킹툴을 못 띄우게 하는 블랙아웃 방식을 쓰는데 흔히 보이는 것들이 mProtect인데 검증되지 않는 툴들이 뜰 경우 자동으로 죽여버리는 역할을 하고 있습니다.

그래서 mProtect가 안떴을 때 게임이 실행이 안된다던지 하는 방식을 쓰고 있죠.

내부 해킹은 아까도 말했듯이 내부 코드를 조작하는 것을 말하는데 해킹과 게임과의 싸움에서 가장 문제가 되는 것은 적에게 인질이 잡혀있다는 것입니다.

이 인질은 게임 클라이언트(.exe 파일)가 인질이 됩니다.

이것은 기계어이고, 프로그램 코드인데 해커들은 이 것을 역 어셈블리하는 툴을 써서 인간이 조금이나마 알아보는 코드로 바꿔서 그런다음 노하우가 생기면 이 부분들이 어떤 역할을 하는지 대충 유츄할 수 있습니다.

암호화 코드를 심어놨다고 쳤을 때 그 부분이 파악되었을 때 그냥 그 부분을 By-Pass해버립니다.

그래서 이미 적에게 클라이언트가 있기 때문에 암호화를 한다던지 어떤 조작을 한다던지는 금방 뚫리게 됩니다.

그래서 내부해킹을 막는 방법은 조작여부를 판단해야 하는데 이것을 판단하는 방법은 파일 사이즈가 변경되는 지를 확인하는 방법이죠. 이것이 가장 흔한 방법 입니다.

배포를 했을 때 파일 사이즈가 120KB라 했을 때 누군가 조작을 해서 파일 사이즈가 119.998KB로 바뀌었다 했을 때 조작이 된 것으로 보고 차단시키는 방법 입니다.

그런데 이 방법도 파일 사이즈가 더 더해지면 빼주고, 파일 사이즈가 더 빠지면 더미로 채워서 파일 사이즈를 정확히 맞출 수 있는데 그럴 때 사용하는 것이 Checksum입니다.

 

이것은 파일 내용을 전부 다 읽어서 이것을 Hash 함수를 통해서 128bit나 256bit의 Hash를 뽑아내는데요.
그러면 이 Hash는 조금만 바뀌어도 Hash값은 크게 변하여 금방 알아낼 수 있기 때문에 파일 조작을 알아낼 수 있습니다.

그래서 이 Hash값을 해커가 발견되기 쉽지 않은 어딘가에 .EXE파일에 적어 두죠.

그 다음 외부툴인 mProtect같은 해킹방지 툴들이 처음 프로그램을 띄울 때 그 EXE파일을 쭉 읽어서 같은 방법으로 Hash를 합니다.
Hash를 했을 때의 결과와 내가 어딘가에 적어놓은 Hash값의 결과가 다를 경우 조작이 되었다고 판단하여 프로그램을 강제 종료 시켜 버립니다.

 

또 다른 방식으로 코드 암호화 하는 방식이 있는데 내가 특정 코드를 암호화 하고 싶을 때(해커에게 들통나면 안되는 코드) 이런 코드들을 다른것으로 바꾸어 놓습니다.

그 때 코드 상태는 코드가 바뀌었기 떄문에 실행이 안되는데 게임이 실행 될 때 그 암호를 풀어서 실행을 시키기 때문에 파일만 봤을 때 암호화가 되어 있는 상태로 되어있는 것으로 보여지는데

단점으로는 게임이 실행 될 때 암호를 풀어야 하기 때문에 실행이 좀 느리다는 단점이 있습니다.

 

그런데 문제는 뭘하든 중요한건 창 VS 방패 문제인데요. 방패가 강해지면 창도 강해집니다. 성공은 창에게 있습니다.

창이 성공하면 방패를 강하게 만들고, 방패를 강하게 만들면 또 창이 강해져서 성공을 하는 구조로 반복이 되기 때문에 아무리 방패가 강해지더라도 언젠가는 뚫린다는 것을 알아 두어야 합니다.

계속 반복해서 막아내는 방법 밖에는 없습니다. 그래서 양파껍질처럼 해킹공격을 하면 계속 뚫어야 할 것이 생기게 해서 해커를 귀찮게 만드는 방법밖에 없죠.

 

오늘 Deterministic방식에서 해킹이 취약한 이유에 대해 알아보았습니다. 굳이 Deterministic방식이 아니더라도 Server Authority방식도 해킹에 취약하기는 마찬가지 입니다.

그런데 Deterministic방식은 Input Set을 의존하고 Server Authority는 기본적으로 서버와 로직을 들고 있고, 서버가 명령하는대로 수행하기 때문에 기본적으로 서버가 심판역할을 하기 때문에 Server Authority방식이 Input Set을 조작하는 형태의 해킹에서는 강력합니다.

 

 

ckdqja135/Typescript-restful-starter

Contribute to ckdqja135/Typescript-restful-starter development by creating an account on GitHub.

github.com

 

728x90
반응형
그리드형

댓글을 달아 주세요