SlideShare a Scribd company logo
1 of 100
Download to read offline
넥슨 라이브인프라실
하재승 (ipknhama@gmail.com, @ipkn)
점검 없이 실시간으로 코드 수정 & 게임 정보 수집하기
NEXON ZERO
“이상한거 만드는거 좋아하는 덕후”
개발 10년차
바람의 나라, 메틴2, 빅샷, 던전앤파이터
서버 버너, JYP, ZERO
Crow, 페블 한글화, 약속, 버블러, WDD
발표자: 하재승, ipknHama
What’s ZERO?
여러 기술을 조합하여
라이브 서비스의 관심 있는 정보들을
개발팀 부하 없이 빠르게 수집
기능
Realtime Monitoring
User Behavior Analytics
On-demand Data Extractor
Realtime Monitoring
유저의 실시간 플레이 상황 및 그래프
(시연 영상 : 다음 슬라이드)
Realtime Monitoring 편집
수집하려는 내용
실시간 편집 및 적용
C++ 코드 지원
User Behavior Analytics
OnChangeRegion 호출 시
ga_screenview( … 이동한 지역)
실시간 편집 및 적용
On-demand Data Extractor
RM / UBA로 수집가능한 데이터를
지정한 기간 동안 수집하여
.csv 또는 RDBMS 테이블로 추출
개발팀 외부에서도 사용할 수 있게 기능을 분리
개발팀 – 변수 편집
외부팀 – 사용할 변수를 선택하여 보고서 생성
Why “ZERO”?
ZERO development cost
추가 개발 없이 원하는 정보를 바로 수집하도록 설정
ZERO integration cost
개발팀이 연동을 위해 한 일: ZERO 연동 라이브러리 추가 및 초기화 함수 호출
ZERO additional patch
시연으로 확인 하였듯이, 새로운 내용을 수집하려 할 때 패치가 필요 없음
ZERO delay
실시간으로 새로운 정보를 수집하고, 실시간으로 확인 가능
ZERO risk
실수를 하더라도 게임 서비스에는 장애가 발생하지 않게 여러 겹의 보호 장치
NDC 2017, <즉시, 바로 살펴보자>, 송창규
Part 1 목차: Introduction
1. 시연
2. 소개
3. 구성 요소
4. 적용 사례
5. 향후 작업
Part 2 목차: Technical Details
1. PDB Inspect
2. C++ Expression Evaluator
1.Basic Operation
2.Function Call
3. Function Hooking
4. Minor issues
Part 1
Introduction
필요성
라이브 본부 밑의 다양한 프로젝트
ETL 과정 자동화
개발팀 부하 없이
이벤트 진행 상황과 결과 빠른 확인
시작 아이디어
덤프파일을 통한 사후 디버깅 실용 테크닉(김이선님, 2011)
풀덤프 dump 파일에서 특정 클래스의 오브젝트 추적
런타임에도 할 수 있지 않을까?
관련 기능을 API로 제공한다면?
서비스 중인 게임으로 확장한다면?
무엇을 더 할 수 있을까?
관심 있는 정보를 빠르게 살펴보고 싶다
“무엇”을 “어떻게” 뽑아야 하는가?
“어떻게” – 수집할 방법
대다수 프로젝트가 C++로 작성됨
익숙한 C++ 코드를 바로 쓸 수 있게 하자
패치하더라도 다시 작업하지 않아야 한다
실시간으로
실수 해도 장애가 생기지 않게
보안 수준을 낮추지 않게
“무엇” – 수집할 시점 + 값
주기적으로 수집되는 값
현재 위치, 레벨, 직업, …
특정 함수가 불리는 시점에서 수집된 값
사망, 사용한 스킬, 이동한 맵, …
조합하여 표현할 수 있는 값
시간당 데미지, 시간당 돈 획득량, …
“무엇” – 수집할 시점 + 값
주기적으로 수집되는 값
현재 위치, 레벨, 직업, …
특정 함수가 불리는 시점에서 수집된 값
사망, 사용한 스킬, 이동한 맵, 퀘스트 완료, …
조합하여 표현할 수 있는 값
시간당 데미지, 시간당 돈 획득량, …
“무엇” – 수집할 시점 + 값
주기적으로 수집되는 값
현재 위치, 레벨, 직업, …
특정 함수가 불리는 시점에서 수집된 값
사망, 사용한 스킬, 이동한 맵, 퀘스트 완료, …
조합하여 표현할 수 있는 값
시간당 데미지, 시간당 돈 획득량, …
“무엇” – 수집할 시점 + 값
주기적으로 수집되는 값
현재 위치, 레벨, 직업, …
특정 함수가 불리는 시점에서 수집된 값
사망, 사용한 스킬, 이동한 맵, 퀘스트 완료, …
조합하여 표현할 수 있는 값
시간당 데미지, 시간당 돈 획득량, …
Realtime Monitoring
“무엇”을 실시간으로 보여줌
한눈에 반하게!
User Behavior Analytics
“무엇”을 Google Analytics에 연동
On-demand Data Extractor
“무엇”을 활용하기 편한 형태로 제공
구성 요소
Realtime
Monitoring
User Behavior
Analytics
On-demand
Data Extractor
PDB Inspect
C++ Expression
Evaluator
Function
Hooking
ZERO 서버
게임
클라이언트
PDB
ZERO Web
C++ EE 변환 C++ EE 실행
Function
Hooking
PDB Inspect명령 지시 및 확인
* 게임 서버를 거치지 않고 게임 클라이언트가 직접 연결하여
관련 정보를 수집하게 구현되었습니다. 게임 서버에 연동하여
서버의 내용을 얻어오는 방식도 차후 구현될 예정입니다.
적용 사례
신년 방물장수 이벤트
Part 2
Technical Details
Part 2 목차: 기술 소개
1. PDB Inspect
2. C++ Expression Evaluator
1.Basic Operation
2.Function Call
3. Function Hooking
4. Minor issues
전체 구조
ZERO 서버
게임
클라이언트
PDB
ZERO Web
C++ EE 변환 C++ EE 실행
Function
Hooking
PDB Inspect명령 지시 및 확인
ZERO 서버
게임
클라이언트
PDB
ZERO Web
C++ EE 변환
C++ EE 실행
PDB Inspect
RM 변수 추가 보관
일정시간 마다
가공Canvas에 렌더링
PDB Inspect
요약
PDB 내용을 확인하면
g_status
virtualAddress 149584
type
baseType btInt
length 4
g_status 값을 알려면 149584 에서 4바이트를 읽자
PDB
Program Database file
컴파일러가 어떻게 실행 파일을 생성했는지 저장
Visual Studio를 통해 디버깅하기 위해 필요
각 심볼의 이름, 타입, 주소 값 등을 저장함
dbghelp 또는 DIA SDK 를 통해 값을 읽을 수 있다.
DIA SDK
PDB 파일을 읽기 위한 API
COM 기반
트리 형태로 심볼 정보를 접근 + 심볼 ID로 검색
source = CoCreateInstance(CLSID_DiaSource)
session = source->LoadDataForExe(“Game.exe”, …)
session->get_globalScope(&global)
http://github.com/ipkn/wdd Symbols.cpp
IDiaSymbol 속성
symTag 어떤 종류의 심볼인지
symIndexId session->symbolById로 검색가능한 ID
name 심볼 이름 (optional)
typeId 이 심볼의 타입을 나타내는 심볼의 ID
virtualAddress 심볼의 주소
session->put_loadAddress 로 기준 주소를 설정가능
dataKind
locationType
registerId
offset
트리 구조
get_globalScope()로 얻은 심볼로부터
함수 .symTag = SymTagFunction
전역 변수 .symTag = SymTagData, dataKind = DataIsStaticMember
클래스 .symTag = SymTagUDT, udtKind
트리 구조 - 함수
.symTag == SymTagFunction
.type 함수 타입에 대한 심볼
.type.type 함수 리턴 타입
.virtualAddress 함수 시작 주소
인자
SymTagData, dataKind = DataIsParam
.type 인자의 타입
로컬 변수
SymTagData, dataKind = DataIsLocal
트리 구조 - 클래스
부모 클래스, 멤버 변수, 멤버 함수, …
class C : public A, public B { … } c;
(A*)&c
(B*)&c 서로 다른 주소값
UDT(C)
BaseClass(A), .offset = (A*)로 변환할때 this에 더해줘야 하는 값
BaseClass(B), .offset
std::string (SymTagUDT)
std::_String_alloc<…> (SymTagBaseClass)
…
size_type (SymTagTypeDef)
value_type (SymTagTypeDef)
…
size (SymTagFunction) 인자가 없는 멤버함수: this만 존재
this (SymTagData)
(SymTagFuncDebugStart, SymTagFuncDebugEnd, SymTagInlineSite, SymTagCallee)
resize (SymTagFunction) 인자가 2개
this (SymTagData)
_Newsize (SymTagData)
_Ch (SymTagData)
C++용 인터페이스 작성
각 속성을 멤버변수로, (optional 타입)
findChildren을 iterator로 래핑
for(auto base : s.AllBaseClass())
{
if (c.name)
*c.name;
}
DIA SDK 주의점
msdia140.dll 등록 필요 (버전에 따라 120, 100, …)
symsrv.dll
심볼 서버 사용시 필요
없으면 심볼을 찾지 못한다는 에러가 리턴
symIndexId는 세션 별로 부여됨
함수 호출 순서에 따라 다른 값이 주어짐
다른 언어/환경으로의 확장
모바일 기기 지원 고려 중
DWARF 포맷 – gcc, clang
clang은 PDB도 지원 (mostly)
C# (Unity) – 리플렉션 활용
C++ Expression Evaluator
세 줄 요약
TSingleton<CInterfaceMgr>::GetInstance().GetPlayer
()->GetCurrentRegion()
read(((call(va_to_a(17022576),9,take_addr(at(read(ca
ll(va_to_a(13211376),9,take_addr(at(
read(call(va_to_a(194568),8), 'p')))), 'p')))))), 'i', 4)
C++ 코드를 어느 클라이언트에서든 실행할 수 있게 루아 코드로 변환 및 실행
* 이 발표에서 공개된 모든 코드는 보안상 실제 코드와는 다르게 변형되었습니다.
변환: C++ 표현식 파서
C++의 매우 작은 일부분만 지원
간단한 Recursive Descent Parser (링크)
필요한 기능 만큼만 직접 구현
* 파서를 만드는 방법에 대한 내용은 컴파일러 책 등을 참고 바랍니다.
C++ 식 → Lua 트랜스파일러
변수 참조, 캐스팅, 함수 호출
실행: 변환된 코드를 전송 후 실행
바로 계산이 이루어지는 게 아니라
접속된 클라이언트 내에서 실행 되어야함
실행할 내용을 동적 언어 코드(Lua)로 변환 후 전송
C++ Expression Evaluator
실행 파트
값의 표현
at(주소) 또는 Lua 값
take_addr(at(100)) === 100
at(100):member(4) === at(104)
read(100, ‘i’) === 100
read(at(100), ‘i’) === *(int*)100
API 확장
va_to_a Symbol 주소를 실제 메모리 주소로 변환
take_addr 주소값 얻기. & 연산자 구현에 필요
at.member 멤버에 해당하는 객체 리턴
read 메모리 읽기
reg 레지스터 값 읽기
get/set 계산에 필요한 값 불러오기/저장하기
read_mbcs, read_ucs2, ga_...,
va_to_a
심볼에 저장된 주소
= 해당 모듈이 메모리 주소 0에 로딩 되었을 때 기준
ASLR
Address space layout randomization
해킹 방지 기법 중 하나
각 클라이언트는 다른 메모리 주소에 로딩될 수 있다
GetModuleHandle(nullptr) 실행중인 exe가 로딩된 주소
C++ Expression Evaluator
변환 파트
변환 결과물: LuaValue
`C++ expr` → Lua code + type
`1` → 1, IntType
A a
`a` → at(addr of a), A
a는 전역 변수, 로컬 변수, 함수(함수처리는 이후에!)
재귀적 정의
X → code(X), type(X) B X::b
`&X` → take_addr(code(X)), type(X)*
`*X` → at(code(X)), type(X).pointing_type()
`X->b` → at(read(code(X),’p’)):member(
offset of b from type(X)), B
at(at(…)) 꼴을 없애려고 read 사용
`X.b` → ?
C가 유저 타입이면
`(C)X` → code(X), C
이런 식으로 double을 int로 캐스팅 한다면 버그가
발생하지만, 작업량 대비 효과가 적어 차후 구현.
타입 변환
C++ Expression Evaluator
변환 – 함수 호출
함수 호출 필요성
단순히 변수 읽는 것 만으로는 부족
프로그래머가 익숙한 방식으로 사용할 수 있게됨
예) 싱글턴
Singleton<A>::m_spInstance
Singleton<A>::GetInstance()
* 함수 스태틱으로 구현한 경우 함수 호출 없인 아예 접근 불가
실행단에선 호출할 함수의 프로토타입을 모름
- 어떻게 인자를 넘기고
- 어떻게 결과값을 받고
- Calling convention은 뭔지
모두 알려줘야 한다.
함수 호출 (1)
`F(A,B)` →
call(
type(F).addr, FunctionOption:int,
code(A), ArgAOption:int,
code(B), ArgBOption:int,
),
type(type(F))
함수 호출 (2)
FunctionOption = [CC, size]
Calling Convention(CC)
cdecl / thiscall
size = (length of Return value)
함수 호출 (3)
ArgOption = [size, reg]
size = 1,2,4,8 -> size-1을 3비트로 인코딩
레지스터로 전달되는 경우만 표현 가능
reg = 22개 -> 5비트로 인코딩
ES, DS, FS, GS, CS, SS,
AX, BX, CX, DX,
BP, SP, SI, DI,
R8, R9, R10, …, R15
함수 호출 (4)
TSingleton<CInterfaceMgr>::GetInstance()
read(((call(va_to_a(199568),8))),'p') CInterfaceMgr&
* 클래스 스태틱 함수이므로 인자가 없는 전역 함수.
해당 주소를 찾아 호출하면 싱글턴의 주소를 얻을 수 있다.
TSingleton<CInterfaceMgr>::GetInstance()
read(((call(va_to_a(199568),8))),'p') CInterfaceMgr&
TSingleton<CInterfaceMgr>::GetInstance().GetPlayer()
(read(((call(va_to_a(13261376),9,take_addr(at(read(call(va
_to_a(199568),8),'p')))))),'p')) CCharacter*
* 앞에서 얻는 CInterfaceMgr&의 값이 this 인자로 전달됨
TSingleton<CInterfaceMgr>::GetInstance()
read(((call(va_to_a(199568),8))),'p') CInterfaceMgr&
TSingleton<CInterfaceMgr>::GetInstance().GetPlayer()
(read(((call(va_to_a(13261376),9,take_addr(at(read(call(va
_to_a(199568),8),'p')))))),'p')) CCharacter*
TSingleton<CInterfaceMgr>::GetInstance().GetPlayer()
->GetCurrentRegion()
read(((call(va_to_a(18022576),9,take_addr(at(read(call(va_
to_a(13261376),9,take_addr(at(
read(call(va_to_a(199568),8),'p')))),'p')))))),'i',4) int
구현 - 함수 주소 얻기
오버로딩
const 버전,인자가 다른 경우
PDB에 동일한 순서로 저장된다는 보장이 없음
타입 Hinting 지원
GetField#[int]
GetField#[char*]
구현 - x86/x64
x86
여러 calling convention
스택, 레지스터 각각으로 인자 전달
인라인 어셈블리로 신중히 구현
x64
calling convention이 단 하나
어셈 파일 따로 작성 (inline assembly 미지원)
구현 - Calling convention (x86)
cdecl
caller clean-up
thiscall
ecx : this
callee clean-up
caller clean-up / callee clean-up
Out parameter (구현 중)
bool GetPosition(vector3& pos)
Lua 단에서 따로 메모리를 할당한 후 해당 포인터를 전달
계산 종료 후 관련 메모리 해제
local buf = buffer(12)
call(`code for GetPosition`, buf)
buf:member_at(4)
User defined type (미해결)
포인터, 레퍼런스로는 문제없이 활용
value type으로 사용시 어려움 발생
// COW 최적화 적용된 전용 스트링 클래스 String
String GetName()
소멸자를 불러주지 않으면 좀비 스트링이 생기게 된다.
인자로 넣는 경우라면? (더욱 복잡)
한계
Inline 되어 사라진 함수
최적화를 통해 생각보다 많은 함수가 inline됨
버전, 컴파일러 옵션에 따라 비일관적인 동작
LTCG와 WPO 켜면
VC버전에 따라
PDB와 실제 출력된 코드가 다른 경우가 있었음 (매우 가끔)
손상된 PDB 파일이 나온 경우도 있었음 (1번)
아마도 컴파일러 버그? ㅠㅠ
C++ EE 추가 활용
활용 - 심볼 탐색 및 식 계산
PDB와 C++ EE를 쉽게 활용하기 위한 툴 작성 및 적용
ZEROConsole x.exe x.pdb
실행중인 프로세스에 연결
ZEROConsole x.dmp x.pdb
풀덤프에 연결
ZEROConsole - x.pdb
PDB 정보만 확인
활용: 풀덤프 탐색 (1)
라이브 게임 서버 덤프
던전 container에서 특정 던전을 찾아 처리 중 크래쉬
원소가 남아있다면 → 메모리 덮어쓰기 버그
원소가 없다면 → 멀티 쓰레드 버그
활용: 풀덤프 탐색 (2)
전역 해시테이블에 특정 원소가 있는지 확인해야 하는 상황
원소 80만개 – 손으로 확인 불가
소스 분석
STLPort 기반 unordered_map
전체 데이터는 Singly Linked List로 표현됨
활용: 풀덤프 탐색 (3)
Full Dump는 모든 메모리 정보를 가짐
C++ EE를 구현함에 따라
Lua 코드로 덤프를 자유자재로 탐색할 수 있게 됨
루프 돌면서 해시테이블을 확인
디버깅 시나리오 확인에 도움
Function Hooking
함수 후킹
임의의 함수의 동작을 런타임에 변경하는 기술
함수 인자에 조작을 가하거나
아예 함수 동작을 변경
Detour, MinHook, EasyHook, ...
미리 정해진 함수로 대체하는 형태를 주로 지원
런타임에 임의의 함수를 후킹하여
Lua 코드를 실행해야함
함수별로 실행할 Lua code도 다르다
관련 정보를 알려줄 방법이 필요
기존 후킹 라이브러리의 한계
프롤로그 함수 동적 생성
후킹으로 대체할 함수를 동적으로 생성
처리할 내용을 담은 포인터를 스택에 담아 같이 전달
함수 내용:
push <address of HookInfo>
jmp <HookHelper>
* 아직 32비트만 구현
HookHelper
함수 진입 시의 인자 값 등을 확인
C++ EE 활용
사용하기 전에 필요한 레지스터 저장하고 시작
Return address를 미리 조작해두면
함수 종료시점도 알 수 있음
훅을 제거할 때
이미 내부 처리 코드를 실행 중일 수 있음
공유한 정보가 포인터 하나
락 등으로 해결하기 어렵다
삭제시 먼저 훅을 해제한 후
관련 데이터 삭제에는 5초 딜레이 주는 것으로 우회
멀티스레드 이슈
여러 후킹을 합치기
여러 기능에서 같은 함수를 동시에 후킹하고 싶음
후킹된 함수를 다시 후킹하는 건 낭비
서버에서
후킹을 사용하는 모든 기능의 코드를 모아서 보냄
실제 실행하는 시점에선
하나가 에러가 나더라도
다른 기능에 영향을 주지 않게 구현
그 외
유저 접속 시 랜덤 키 할당, 해당 키로 분배
수집 내용별 비율이 따로 존재
특정 유저에게만 수집 내용이 몰리지 않을까 하는 우려
합쳐진 키를 사용하여 분배하게 작성
(유저 키 + 수집 내용 키)
유저 샘플링
MySQL JSON column
On-demand Data Extractor를 위한
비정형 데이터 저장에 사용
MySQL 5.7.8 이상
VIEW를 만들면 Readonly SQL 테이블 처럼 활용 가능
• * 다른 RDBMS들도 JSON 타입을 지원하나
• 사용성이 MySQL이 좋았음
장애 대비 (1)
장애가 발생해선 안됨
개발팀 외부에서 만들어진 라이브러리
아무리 좋은 물건이더라도
크래쉬를 유발하거나 성능 문제를 일으키면
적용을 유보하거나 롤백할 수 밖에 없음
장애 대비 (2)
수집 쓰로틀링
지나치게 많이 수집되면 자동으로 수집양을 줄인다
의도치 않은 경우를 대비
크래쉬 상황 대비
SEH로 감싸기
기능별로 lua_State를 나눠서 사용
장애 대비 (3)
여전히 실수하면 문제가 발생할 여지는 있음
적용 대상을 적은 비율부터 점차적 확대 가능하게
개발자가 배포된 클라이언트로
ZERO 기능을 테스트 해 볼 수 있는 환경 제공
Future Works (1)
Hot Patch (실행 중 코드 수정)
실시간 버그 수정
예: 비정상적인 인자가 들어올 때 return 해버리게
JYP와 연동되어
컨텐츠별 성능 정보 추적 및 활용
Future Works (2)
Monitoring: Time-lapse view
장기간의 유저 흐름을 살펴보기
쌓인 데이터를 분석할 수 있는 툴 제공
사실상 무궁무진
Future Works (3)
특허 출원 진행 중
감사합니다.

More Related Content

What's hot

온라인 게임 처음부터 끝까지 동적언어로 만들기
온라인 게임 처음부터 끝까지 동적언어로 만들기온라인 게임 처음부터 끝까지 동적언어로 만들기
온라인 게임 처음부터 끝까지 동적언어로 만들기
Seungjae Lee
 
Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심
흥배 최
 
이승재, 실버바인 서버엔진 2 설계 리뷰, NDC2018
이승재, 실버바인 서버엔진 2 설계 리뷰, NDC2018이승재, 실버바인 서버엔진 2 설계 리뷰, NDC2018
이승재, 실버바인 서버엔진 2 설계 리뷰, NDC2018
devCAT Studio, NEXON
 
홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019
홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019
홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019
devCAT Studio, NEXON
 
양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012
양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012
양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012
devCAT Studio, NEXON
 
윤석주, 신입 게임 프로그래머가 되는 법 - 넥슨 채용 프로세스 단계별 분석, NDC2019
윤석주, 신입 게임 프로그래머가 되는 법 - 넥슨 채용 프로세스 단계별 분석, NDC2019윤석주, 신입 게임 프로그래머가 되는 법 - 넥슨 채용 프로세스 단계별 분석, NDC2019
윤석주, 신입 게임 프로그래머가 되는 법 - 넥슨 채용 프로세스 단계별 분석, NDC2019
devCAT Studio, NEXON
 
송창규, unity build로 빌드타임 반토막내기, NDC2010
송창규, unity build로 빌드타임 반토막내기, NDC2010송창규, unity build로 빌드타임 반토막내기, NDC2010
송창규, unity build로 빌드타임 반토막내기, NDC2010
devCAT Studio, NEXON
 
김동건, 할머니가 들려주신 마비노기 개발 전설, NDC2019
김동건, 할머니가 들려주신 마비노기 개발 전설, NDC2019김동건, 할머니가 들려주신 마비노기 개발 전설, NDC2019
김동건, 할머니가 들려주신 마비노기 개발 전설, NDC2019
devCAT Studio, NEXON
 
[KGC2011_박민근] 신입 게임 개발자가 알아야 할 것들
[KGC2011_박민근] 신입 게임 개발자가 알아야 할 것들[KGC2011_박민근] 신입 게임 개발자가 알아야 할 것들
[KGC2011_박민근] 신입 게임 개발자가 알아야 할 것들
MinGeun Park
 
덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012
덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012
덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012
Esun Kim
 
[0903 구경원] recast 네비메쉬
[0903 구경원] recast 네비메쉬[0903 구경원] recast 네비메쉬
[0903 구경원] recast 네비메쉬
KyeongWon Koo
 

What's hot (20)

[NDC2016] TERA 서버의 Modern C++ 활용기
[NDC2016] TERA 서버의 Modern C++ 활용기[NDC2016] TERA 서버의 Modern C++ 활용기
[NDC2016] TERA 서버의 Modern C++ 활용기
 
실시간 게임 서버 최적화 전략
실시간 게임 서버 최적화 전략실시간 게임 서버 최적화 전략
실시간 게임 서버 최적화 전략
 
온라인 게임 처음부터 끝까지 동적언어로 만들기
온라인 게임 처음부터 끝까지 동적언어로 만들기온라인 게임 처음부터 끝까지 동적언어로 만들기
온라인 게임 처음부터 끝까지 동적언어로 만들기
 
Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심Modern C++ 프로그래머를 위한 CPP11/14 핵심
Modern C++ 프로그래머를 위한 CPP11/14 핵심
 
Next-generation MMORPG service architecture
Next-generation MMORPG service architectureNext-generation MMORPG service architecture
Next-generation MMORPG service architecture
 
이승재, 실버바인 서버엔진 2 설계 리뷰, NDC2018
이승재, 실버바인 서버엔진 2 설계 리뷰, NDC2018이승재, 실버바인 서버엔진 2 설계 리뷰, NDC2018
이승재, 실버바인 서버엔진 2 설계 리뷰, NDC2018
 
[150124 박민근] 모바일 게임 개발에서 루아 스크립트 활용하기
[150124 박민근] 모바일 게임 개발에서 루아 스크립트 활용하기[150124 박민근] 모바일 게임 개발에서 루아 스크립트 활용하기
[150124 박민근] 모바일 게임 개발에서 루아 스크립트 활용하기
 
홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019
홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019
홍성우, 게임 서버의 목차 - 시작부터 출시까지, NDC2019
 
〈야생의 땅: 듀랑고〉 서버 아키텍처 Vol. 3
〈야생의 땅: 듀랑고〉 서버 아키텍처 Vol. 3〈야생의 땅: 듀랑고〉 서버 아키텍처 Vol. 3
〈야생의 땅: 듀랑고〉 서버 아키텍처 Vol. 3
 
양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012
양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012
양승명, 다음 세대 크로스플랫폼 MMORPG 아키텍처, NDC2012
 
게임서버프로그래밍 #8 - 성능 평가
게임서버프로그래밍 #8 - 성능 평가게임서버프로그래밍 #8 - 성능 평가
게임서버프로그래밍 #8 - 성능 평가
 
윤석주, 신입 게임 프로그래머가 되는 법 - 넥슨 채용 프로세스 단계별 분석, NDC2019
윤석주, 신입 게임 프로그래머가 되는 법 - 넥슨 채용 프로세스 단계별 분석, NDC2019윤석주, 신입 게임 프로그래머가 되는 법 - 넥슨 채용 프로세스 단계별 분석, NDC2019
윤석주, 신입 게임 프로그래머가 되는 법 - 넥슨 채용 프로세스 단계별 분석, NDC2019
 
송창규, unity build로 빌드타임 반토막내기, NDC2010
송창규, unity build로 빌드타임 반토막내기, NDC2010송창규, unity build로 빌드타임 반토막내기, NDC2010
송창규, unity build로 빌드타임 반토막내기, NDC2010
 
김동건, 할머니가 들려주신 마비노기 개발 전설, NDC2019
김동건, 할머니가 들려주신 마비노기 개발 전설, NDC2019김동건, 할머니가 들려주신 마비노기 개발 전설, NDC2019
김동건, 할머니가 들려주신 마비노기 개발 전설, NDC2019
 
[야생의 땅: 듀랑고] 서버 아키텍처 - SPOF 없는 분산 MMORPG 서버
[야생의 땅: 듀랑고] 서버 아키텍처 - SPOF 없는 분산 MMORPG 서버[야생의 땅: 듀랑고] 서버 아키텍처 - SPOF 없는 분산 MMORPG 서버
[야생의 땅: 듀랑고] 서버 아키텍처 - SPOF 없는 분산 MMORPG 서버
 
사설 서버를 막는 방법들 (프리섭, 더이상은 Naver)
사설 서버를 막는 방법들 (프리섭, 더이상은 Naver)사설 서버를 막는 방법들 (프리섭, 더이상은 Naver)
사설 서버를 막는 방법들 (프리섭, 더이상은 Naver)
 
[KGC2011_박민근] 신입 게임 개발자가 알아야 할 것들
[KGC2011_박민근] 신입 게임 개발자가 알아야 할 것들[KGC2011_박민근] 신입 게임 개발자가 알아야 할 것들
[KGC2011_박민근] 신입 게임 개발자가 알아야 할 것들
 
덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012
덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012
덤프 파일을 통한 사후 디버깅 실용 테크닉 NDC2012
 
[NDC 2018] 신입 개발자가 알아야 할 윈도우 메모리릭 디버깅
[NDC 2018] 신입 개발자가 알아야 할 윈도우 메모리릭 디버깅[NDC 2018] 신입 개발자가 알아야 할 윈도우 메모리릭 디버깅
[NDC 2018] 신입 개발자가 알아야 할 윈도우 메모리릭 디버깅
 
[0903 구경원] recast 네비메쉬
[0903 구경원] recast 네비메쉬[0903 구경원] recast 네비메쉬
[0903 구경원] recast 네비메쉬
 

Similar to NDC 2017 하재승 NEXON ZERO (넥슨 제로) 점검없이 실시간으로 코드 수정 및 게임 정보 수집하기

Boost 라이브리와 C++11
Boost 라이브리와 C++11Boost 라이브리와 C++11
Boost 라이브리와 C++11
OnGameServer
 
김민욱, (달빛조각사) 엘릭서를 이용한 mmorpg 서버 개발, NDC2019
김민욱, (달빛조각사) 엘릭서를 이용한 mmorpg 서버 개발, NDC2019김민욱, (달빛조각사) 엘릭서를 이용한 mmorpg 서버 개발, NDC2019
김민욱, (달빛조각사) 엘릭서를 이용한 mmorpg 서버 개발, NDC2019
min woog kim
 
[NDC2015] 언제 어디서나 프로파일링 가능한 코드네임 JYP 작성기 - 라이브 게임 배포 후에도 프로파일링 하기
[NDC2015] 언제 어디서나 프로파일링 가능한 코드네임 JYP 작성기 - 라이브 게임 배포 후에도 프로파일링 하기[NDC2015] 언제 어디서나 프로파일링 가능한 코드네임 JYP 작성기 - 라이브 게임 배포 후에도 프로파일링 하기
[NDC2015] 언제 어디서나 프로파일링 가능한 코드네임 JYP 작성기 - 라이브 게임 배포 후에도 프로파일링 하기
Jaeseung Ha
 
[shaderx6]8.2 3d engine tools with c++cli
[shaderx6]8.2 3d engine tools with c++cli[shaderx6]8.2 3d engine tools with c++cli
[shaderx6]8.2 3d engine tools with c++cli
종빈 오
 
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
Esun Kim
 
NDC 2011, 네트워크 비동기 통신, 합의점의 길목에서
NDC 2011, 네트워크 비동기 통신, 합의점의 길목에서NDC 2011, 네트워크 비동기 통신, 합의점의 길목에서
NDC 2011, 네트워크 비동기 통신, 합의점의 길목에서
tcaesvk
 
카사 공개세미나1회 W.E.L.C.
카사 공개세미나1회  W.E.L.C.카사 공개세미나1회  W.E.L.C.
카사 공개세미나1회 W.E.L.C.
Ryan Park
 
Mongo db 시작하기
Mongo db 시작하기Mongo db 시작하기
Mongo db 시작하기
OnGameServer
 
[0312 조진현] good bye dx9
[0312 조진현] good bye dx9[0312 조진현] good bye dx9
[0312 조진현] good bye dx9
진현 조
 

Similar to NDC 2017 하재승 NEXON ZERO (넥슨 제로) 점검없이 실시간으로 코드 수정 및 게임 정보 수집하기 (20)

Boost 라이브리와 C++11
Boost 라이브리와 C++11Boost 라이브리와 C++11
Boost 라이브리와 C++11
 
김민욱, (달빛조각사) 엘릭서를 이용한 mmorpg 서버 개발, NDC2019
김민욱, (달빛조각사) 엘릭서를 이용한 mmorpg 서버 개발, NDC2019김민욱, (달빛조각사) 엘릭서를 이용한 mmorpg 서버 개발, NDC2019
김민욱, (달빛조각사) 엘릭서를 이용한 mmorpg 서버 개발, NDC2019
 
[NDC2015] 언제 어디서나 프로파일링 가능한 코드네임 JYP 작성기 - 라이브 게임 배포 후에도 프로파일링 하기
[NDC2015] 언제 어디서나 프로파일링 가능한 코드네임 JYP 작성기 - 라이브 게임 배포 후에도 프로파일링 하기[NDC2015] 언제 어디서나 프로파일링 가능한 코드네임 JYP 작성기 - 라이브 게임 배포 후에도 프로파일링 하기
[NDC2015] 언제 어디서나 프로파일링 가능한 코드네임 JYP 작성기 - 라이브 게임 배포 후에도 프로파일링 하기
 
[shaderx6]8.2 3d engine tools with c++cli
[shaderx6]8.2 3d engine tools with c++cli[shaderx6]8.2 3d engine tools with c++cli
[shaderx6]8.2 3d engine tools with c++cli
 
GKAC 2014 Nov. - 안드로이드 스튜디오로 생산성 올리기
GKAC 2014 Nov. - 안드로이드 스튜디오로 생산성 올리기GKAC 2014 Nov. - 안드로이드 스튜디오로 생산성 올리기
GKAC 2014 Nov. - 안드로이드 스튜디오로 생산성 올리기
 
[KGC2014] 두 마리 토끼를 잡기 위한 C++ - C# 혼합 멀티플랫폼 게임 아키텍처 설계
[KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계[KGC2014] 두 마리 토끼를 잡기 위한 C++ - C#  혼합 멀티플랫폼 게임 아키텍처 설계
[KGC2014] 두 마리 토끼를 잡기 위한 C++ - C# 혼합 멀티플랫폼 게임 아키텍처 설계
 
About Visual C++ 10
About  Visual C++ 10About  Visual C++ 10
About Visual C++ 10
 
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
 
소셜게임 서버 개발 관점에서 본 Node.js의 장단점과 대안
소셜게임 서버 개발 관점에서 본 Node.js의 장단점과 대안소셜게임 서버 개발 관점에서 본 Node.js의 장단점과 대안
소셜게임 서버 개발 관점에서 본 Node.js의 장단점과 대안
 
NDC11_김성익_슈퍼클래스
NDC11_김성익_슈퍼클래스NDC11_김성익_슈퍼클래스
NDC11_김성익_슈퍼클래스
 
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
 
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
 
NDC 2011, 네트워크 비동기 통신, 합의점의 길목에서
NDC 2011, 네트워크 비동기 통신, 합의점의 길목에서NDC 2011, 네트워크 비동기 통신, 합의점의 길목에서
NDC 2011, 네트워크 비동기 통신, 합의점의 길목에서
 
강의자료 2
강의자료 2강의자료 2
강의자료 2
 
Node.js in Flitto
Node.js in FlittoNode.js in Flitto
Node.js in Flitto
 
카사 공개세미나1회 W.E.L.C.
카사 공개세미나1회  W.E.L.C.카사 공개세미나1회  W.E.L.C.
카사 공개세미나1회 W.E.L.C.
 
llvm 소개
llvm 소개llvm 소개
llvm 소개
 
Mongo db 시작하기
Mongo db 시작하기Mongo db 시작하기
Mongo db 시작하기
 
[0312 조진현] good bye dx9
[0312 조진현] good bye dx9[0312 조진현] good bye dx9
[0312 조진현] good bye dx9
 
파이썬 데이터과학 레벨1 - 초보자를 위한 데이터분석, 데이터시각화 (2020년 이태영)
파이썬 데이터과학 레벨1 - 초보자를 위한 데이터분석, 데이터시각화 (2020년 이태영) 파이썬 데이터과학 레벨1 - 초보자를 위한 데이터분석, 데이터시각화 (2020년 이태영)
파이썬 데이터과학 레벨1 - 초보자를 위한 데이터분석, 데이터시각화 (2020년 이태영)
 

NDC 2017 하재승 NEXON ZERO (넥슨 제로) 점검없이 실시간으로 코드 수정 및 게임 정보 수집하기

  • 1.
  • 2. 넥슨 라이브인프라실 하재승 (ipknhama@gmail.com, @ipkn) 점검 없이 실시간으로 코드 수정 & 게임 정보 수집하기 NEXON ZERO
  • 3. “이상한거 만드는거 좋아하는 덕후” 개발 10년차 바람의 나라, 메틴2, 빅샷, 던전앤파이터 서버 버너, JYP, ZERO Crow, 페블 한글화, 약속, 버블러, WDD 발표자: 하재승, ipknHama
  • 5. 여러 기술을 조합하여 라이브 서비스의 관심 있는 정보들을 개발팀 부하 없이 빠르게 수집
  • 6. 기능 Realtime Monitoring User Behavior Analytics On-demand Data Extractor
  • 7. Realtime Monitoring 유저의 실시간 플레이 상황 및 그래프 (시연 영상 : 다음 슬라이드)
  • 8. Realtime Monitoring 편집 수집하려는 내용 실시간 편집 및 적용 C++ 코드 지원
  • 9. User Behavior Analytics OnChangeRegion 호출 시 ga_screenview( … 이동한 지역) 실시간 편집 및 적용
  • 10.
  • 11.
  • 12. On-demand Data Extractor RM / UBA로 수집가능한 데이터를 지정한 기간 동안 수집하여 .csv 또는 RDBMS 테이블로 추출 개발팀 외부에서도 사용할 수 있게 기능을 분리 개발팀 – 변수 편집 외부팀 – 사용할 변수를 선택하여 보고서 생성
  • 14. ZERO development cost 추가 개발 없이 원하는 정보를 바로 수집하도록 설정 ZERO integration cost 개발팀이 연동을 위해 한 일: ZERO 연동 라이브러리 추가 및 초기화 함수 호출 ZERO additional patch 시연으로 확인 하였듯이, 새로운 내용을 수집하려 할 때 패치가 필요 없음 ZERO delay 실시간으로 새로운 정보를 수집하고, 실시간으로 확인 가능 ZERO risk 실수를 하더라도 게임 서비스에는 장애가 발생하지 않게 여러 겹의 보호 장치
  • 15. NDC 2017, <즉시, 바로 살펴보자>, 송창규
  • 16. Part 1 목차: Introduction 1. 시연 2. 소개 3. 구성 요소 4. 적용 사례 5. 향후 작업
  • 17. Part 2 목차: Technical Details 1. PDB Inspect 2. C++ Expression Evaluator 1.Basic Operation 2.Function Call 3. Function Hooking 4. Minor issues
  • 19. 필요성 라이브 본부 밑의 다양한 프로젝트 ETL 과정 자동화 개발팀 부하 없이 이벤트 진행 상황과 결과 빠른 확인
  • 20. 시작 아이디어 덤프파일을 통한 사후 디버깅 실용 테크닉(김이선님, 2011) 풀덤프 dump 파일에서 특정 클래스의 오브젝트 추적 런타임에도 할 수 있지 않을까? 관련 기능을 API로 제공한다면? 서비스 중인 게임으로 확장한다면? 무엇을 더 할 수 있을까?
  • 21. 관심 있는 정보를 빠르게 살펴보고 싶다 “무엇”을 “어떻게” 뽑아야 하는가?
  • 22. “어떻게” – 수집할 방법 대다수 프로젝트가 C++로 작성됨 익숙한 C++ 코드를 바로 쓸 수 있게 하자 패치하더라도 다시 작업하지 않아야 한다 실시간으로 실수 해도 장애가 생기지 않게 보안 수준을 낮추지 않게
  • 23. “무엇” – 수집할 시점 + 값 주기적으로 수집되는 값 현재 위치, 레벨, 직업, … 특정 함수가 불리는 시점에서 수집된 값 사망, 사용한 스킬, 이동한 맵, … 조합하여 표현할 수 있는 값 시간당 데미지, 시간당 돈 획득량, …
  • 24. “무엇” – 수집할 시점 + 값 주기적으로 수집되는 값 현재 위치, 레벨, 직업, … 특정 함수가 불리는 시점에서 수집된 값 사망, 사용한 스킬, 이동한 맵, 퀘스트 완료, … 조합하여 표현할 수 있는 값 시간당 데미지, 시간당 돈 획득량, …
  • 25. “무엇” – 수집할 시점 + 값 주기적으로 수집되는 값 현재 위치, 레벨, 직업, … 특정 함수가 불리는 시점에서 수집된 값 사망, 사용한 스킬, 이동한 맵, 퀘스트 완료, … 조합하여 표현할 수 있는 값 시간당 데미지, 시간당 돈 획득량, …
  • 26. “무엇” – 수집할 시점 + 값 주기적으로 수집되는 값 현재 위치, 레벨, 직업, … 특정 함수가 불리는 시점에서 수집된 값 사망, 사용한 스킬, 이동한 맵, 퀘스트 완료, … 조합하여 표현할 수 있는 값 시간당 데미지, 시간당 돈 획득량, …
  • 27. Realtime Monitoring “무엇”을 실시간으로 보여줌 한눈에 반하게! User Behavior Analytics “무엇”을 Google Analytics에 연동 On-demand Data Extractor “무엇”을 활용하기 편한 형태로 제공
  • 29. Realtime Monitoring User Behavior Analytics On-demand Data Extractor PDB Inspect C++ Expression Evaluator Function Hooking
  • 30. ZERO 서버 게임 클라이언트 PDB ZERO Web C++ EE 변환 C++ EE 실행 Function Hooking PDB Inspect명령 지시 및 확인 * 게임 서버를 거치지 않고 게임 클라이언트가 직접 연결하여 관련 정보를 수집하게 구현되었습니다. 게임 서버에 연동하여 서버의 내용을 얻어오는 방식도 차후 구현될 예정입니다.
  • 33.
  • 35. Part 2 목차: 기술 소개 1. PDB Inspect 2. C++ Expression Evaluator 1.Basic Operation 2.Function Call 3. Function Hooking 4. Minor issues
  • 37. ZERO 서버 게임 클라이언트 PDB ZERO Web C++ EE 변환 C++ EE 실행 Function Hooking PDB Inspect명령 지시 및 확인
  • 38. ZERO 서버 게임 클라이언트 PDB ZERO Web C++ EE 변환 C++ EE 실행 PDB Inspect RM 변수 추가 보관 일정시간 마다 가공Canvas에 렌더링
  • 40. 요약 PDB 내용을 확인하면 g_status virtualAddress 149584 type baseType btInt length 4 g_status 값을 알려면 149584 에서 4바이트를 읽자
  • 41. PDB Program Database file 컴파일러가 어떻게 실행 파일을 생성했는지 저장 Visual Studio를 통해 디버깅하기 위해 필요 각 심볼의 이름, 타입, 주소 값 등을 저장함 dbghelp 또는 DIA SDK 를 통해 값을 읽을 수 있다.
  • 42. DIA SDK PDB 파일을 읽기 위한 API COM 기반 트리 형태로 심볼 정보를 접근 + 심볼 ID로 검색 source = CoCreateInstance(CLSID_DiaSource) session = source->LoadDataForExe(“Game.exe”, …) session->get_globalScope(&global) http://github.com/ipkn/wdd Symbols.cpp
  • 43. IDiaSymbol 속성 symTag 어떤 종류의 심볼인지 symIndexId session->symbolById로 검색가능한 ID name 심볼 이름 (optional) typeId 이 심볼의 타입을 나타내는 심볼의 ID virtualAddress 심볼의 주소 session->put_loadAddress 로 기준 주소를 설정가능 dataKind locationType registerId offset
  • 44. 트리 구조 get_globalScope()로 얻은 심볼로부터 함수 .symTag = SymTagFunction 전역 변수 .symTag = SymTagData, dataKind = DataIsStaticMember 클래스 .symTag = SymTagUDT, udtKind
  • 45. 트리 구조 - 함수 .symTag == SymTagFunction .type 함수 타입에 대한 심볼 .type.type 함수 리턴 타입 .virtualAddress 함수 시작 주소 인자 SymTagData, dataKind = DataIsParam .type 인자의 타입 로컬 변수 SymTagData, dataKind = DataIsLocal
  • 46. 트리 구조 - 클래스 부모 클래스, 멤버 변수, 멤버 함수, … class C : public A, public B { … } c; (A*)&c (B*)&c 서로 다른 주소값 UDT(C) BaseClass(A), .offset = (A*)로 변환할때 this에 더해줘야 하는 값 BaseClass(B), .offset
  • 47. std::string (SymTagUDT) std::_String_alloc<…> (SymTagBaseClass) … size_type (SymTagTypeDef) value_type (SymTagTypeDef) … size (SymTagFunction) 인자가 없는 멤버함수: this만 존재 this (SymTagData) (SymTagFuncDebugStart, SymTagFuncDebugEnd, SymTagInlineSite, SymTagCallee) resize (SymTagFunction) 인자가 2개 this (SymTagData) _Newsize (SymTagData) _Ch (SymTagData)
  • 48. C++용 인터페이스 작성 각 속성을 멤버변수로, (optional 타입) findChildren을 iterator로 래핑 for(auto base : s.AllBaseClass()) { if (c.name) *c.name; }
  • 49. DIA SDK 주의점 msdia140.dll 등록 필요 (버전에 따라 120, 100, …) symsrv.dll 심볼 서버 사용시 필요 없으면 심볼을 찾지 못한다는 에러가 리턴 symIndexId는 세션 별로 부여됨 함수 호출 순서에 따라 다른 값이 주어짐
  • 50. 다른 언어/환경으로의 확장 모바일 기기 지원 고려 중 DWARF 포맷 – gcc, clang clang은 PDB도 지원 (mostly) C# (Unity) – 리플렉션 활용
  • 52. 세 줄 요약 TSingleton<CInterfaceMgr>::GetInstance().GetPlayer ()->GetCurrentRegion() read(((call(va_to_a(17022576),9,take_addr(at(read(ca ll(va_to_a(13211376),9,take_addr(at( read(call(va_to_a(194568),8), 'p')))), 'p')))))), 'i', 4) C++ 코드를 어느 클라이언트에서든 실행할 수 있게 루아 코드로 변환 및 실행 * 이 발표에서 공개된 모든 코드는 보안상 실제 코드와는 다르게 변형되었습니다.
  • 53. 변환: C++ 표현식 파서 C++의 매우 작은 일부분만 지원 간단한 Recursive Descent Parser (링크) 필요한 기능 만큼만 직접 구현 * 파서를 만드는 방법에 대한 내용은 컴파일러 책 등을 참고 바랍니다. C++ 식 → Lua 트랜스파일러 변수 참조, 캐스팅, 함수 호출
  • 54. 실행: 변환된 코드를 전송 후 실행 바로 계산이 이루어지는 게 아니라 접속된 클라이언트 내에서 실행 되어야함 실행할 내용을 동적 언어 코드(Lua)로 변환 후 전송
  • 56. 값의 표현 at(주소) 또는 Lua 값 take_addr(at(100)) === 100 at(100):member(4) === at(104) read(100, ‘i’) === 100 read(at(100), ‘i’) === *(int*)100
  • 57. API 확장 va_to_a Symbol 주소를 실제 메모리 주소로 변환 take_addr 주소값 얻기. & 연산자 구현에 필요 at.member 멤버에 해당하는 객체 리턴 read 메모리 읽기 reg 레지스터 값 읽기 get/set 계산에 필요한 값 불러오기/저장하기 read_mbcs, read_ucs2, ga_...,
  • 58. va_to_a 심볼에 저장된 주소 = 해당 모듈이 메모리 주소 0에 로딩 되었을 때 기준 ASLR Address space layout randomization 해킹 방지 기법 중 하나 각 클라이언트는 다른 메모리 주소에 로딩될 수 있다 GetModuleHandle(nullptr) 실행중인 exe가 로딩된 주소
  • 60. 변환 결과물: LuaValue `C++ expr` → Lua code + type `1` → 1, IntType A a `a` → at(addr of a), A a는 전역 변수, 로컬 변수, 함수(함수처리는 이후에!)
  • 61. 재귀적 정의 X → code(X), type(X) B X::b `&X` → take_addr(code(X)), type(X)* `*X` → at(code(X)), type(X).pointing_type() `X->b` → at(read(code(X),’p’)):member( offset of b from type(X)), B at(at(…)) 꼴을 없애려고 read 사용 `X.b` → ?
  • 62. C가 유저 타입이면 `(C)X` → code(X), C 이런 식으로 double을 int로 캐스팅 한다면 버그가 발생하지만, 작업량 대비 효과가 적어 차후 구현. 타입 변환
  • 63. C++ Expression Evaluator 변환 – 함수 호출
  • 64. 함수 호출 필요성 단순히 변수 읽는 것 만으로는 부족 프로그래머가 익숙한 방식으로 사용할 수 있게됨 예) 싱글턴 Singleton<A>::m_spInstance Singleton<A>::GetInstance() * 함수 스태틱으로 구현한 경우 함수 호출 없인 아예 접근 불가
  • 65. 실행단에선 호출할 함수의 프로토타입을 모름 - 어떻게 인자를 넘기고 - 어떻게 결과값을 받고 - Calling convention은 뭔지 모두 알려줘야 한다. 함수 호출 (1)
  • 66. `F(A,B)` → call( type(F).addr, FunctionOption:int, code(A), ArgAOption:int, code(B), ArgBOption:int, ), type(type(F)) 함수 호출 (2)
  • 67. FunctionOption = [CC, size] Calling Convention(CC) cdecl / thiscall size = (length of Return value) 함수 호출 (3)
  • 68. ArgOption = [size, reg] size = 1,2,4,8 -> size-1을 3비트로 인코딩 레지스터로 전달되는 경우만 표현 가능 reg = 22개 -> 5비트로 인코딩 ES, DS, FS, GS, CS, SS, AX, BX, CX, DX, BP, SP, SI, DI, R8, R9, R10, …, R15 함수 호출 (4)
  • 69. TSingleton<CInterfaceMgr>::GetInstance() read(((call(va_to_a(199568),8))),'p') CInterfaceMgr& * 클래스 스태틱 함수이므로 인자가 없는 전역 함수. 해당 주소를 찾아 호출하면 싱글턴의 주소를 얻을 수 있다.
  • 72. 구현 - 함수 주소 얻기 오버로딩 const 버전,인자가 다른 경우 PDB에 동일한 순서로 저장된다는 보장이 없음 타입 Hinting 지원 GetField#[int] GetField#[char*]
  • 73. 구현 - x86/x64 x86 여러 calling convention 스택, 레지스터 각각으로 인자 전달 인라인 어셈블리로 신중히 구현 x64 calling convention이 단 하나 어셈 파일 따로 작성 (inline assembly 미지원)
  • 74. 구현 - Calling convention (x86) cdecl caller clean-up thiscall ecx : this callee clean-up caller clean-up / callee clean-up
  • 75. Out parameter (구현 중) bool GetPosition(vector3& pos) Lua 단에서 따로 메모리를 할당한 후 해당 포인터를 전달 계산 종료 후 관련 메모리 해제 local buf = buffer(12) call(`code for GetPosition`, buf) buf:member_at(4)
  • 76. User defined type (미해결) 포인터, 레퍼런스로는 문제없이 활용 value type으로 사용시 어려움 발생 // COW 최적화 적용된 전용 스트링 클래스 String String GetName() 소멸자를 불러주지 않으면 좀비 스트링이 생기게 된다. 인자로 넣는 경우라면? (더욱 복잡)
  • 77. 한계 Inline 되어 사라진 함수 최적화를 통해 생각보다 많은 함수가 inline됨 버전, 컴파일러 옵션에 따라 비일관적인 동작 LTCG와 WPO 켜면 VC버전에 따라 PDB와 실제 출력된 코드가 다른 경우가 있었음 (매우 가끔) 손상된 PDB 파일이 나온 경우도 있었음 (1번) 아마도 컴파일러 버그? ㅠㅠ
  • 78. C++ EE 추가 활용
  • 79. 활용 - 심볼 탐색 및 식 계산 PDB와 C++ EE를 쉽게 활용하기 위한 툴 작성 및 적용 ZEROConsole x.exe x.pdb 실행중인 프로세스에 연결 ZEROConsole x.dmp x.pdb 풀덤프에 연결 ZEROConsole - x.pdb PDB 정보만 확인
  • 80.
  • 81. 활용: 풀덤프 탐색 (1) 라이브 게임 서버 덤프 던전 container에서 특정 던전을 찾아 처리 중 크래쉬 원소가 남아있다면 → 메모리 덮어쓰기 버그 원소가 없다면 → 멀티 쓰레드 버그
  • 82. 활용: 풀덤프 탐색 (2) 전역 해시테이블에 특정 원소가 있는지 확인해야 하는 상황 원소 80만개 – 손으로 확인 불가 소스 분석 STLPort 기반 unordered_map 전체 데이터는 Singly Linked List로 표현됨
  • 83. 활용: 풀덤프 탐색 (3) Full Dump는 모든 메모리 정보를 가짐 C++ EE를 구현함에 따라 Lua 코드로 덤프를 자유자재로 탐색할 수 있게 됨 루프 돌면서 해시테이블을 확인 디버깅 시나리오 확인에 도움
  • 85. 함수 후킹 임의의 함수의 동작을 런타임에 변경하는 기술 함수 인자에 조작을 가하거나 아예 함수 동작을 변경 Detour, MinHook, EasyHook, ...
  • 86. 미리 정해진 함수로 대체하는 형태를 주로 지원 런타임에 임의의 함수를 후킹하여 Lua 코드를 실행해야함 함수별로 실행할 Lua code도 다르다 관련 정보를 알려줄 방법이 필요 기존 후킹 라이브러리의 한계
  • 87. 프롤로그 함수 동적 생성 후킹으로 대체할 함수를 동적으로 생성 처리할 내용을 담은 포인터를 스택에 담아 같이 전달 함수 내용: push <address of HookInfo> jmp <HookHelper> * 아직 32비트만 구현
  • 88. HookHelper 함수 진입 시의 인자 값 등을 확인 C++ EE 활용 사용하기 전에 필요한 레지스터 저장하고 시작 Return address를 미리 조작해두면 함수 종료시점도 알 수 있음
  • 89. 훅을 제거할 때 이미 내부 처리 코드를 실행 중일 수 있음 공유한 정보가 포인터 하나 락 등으로 해결하기 어렵다 삭제시 먼저 훅을 해제한 후 관련 데이터 삭제에는 5초 딜레이 주는 것으로 우회 멀티스레드 이슈
  • 90. 여러 후킹을 합치기 여러 기능에서 같은 함수를 동시에 후킹하고 싶음 후킹된 함수를 다시 후킹하는 건 낭비 서버에서 후킹을 사용하는 모든 기능의 코드를 모아서 보냄 실제 실행하는 시점에선 하나가 에러가 나더라도 다른 기능에 영향을 주지 않게 구현
  • 92. 유저 접속 시 랜덤 키 할당, 해당 키로 분배 수집 내용별 비율이 따로 존재 특정 유저에게만 수집 내용이 몰리지 않을까 하는 우려 합쳐진 키를 사용하여 분배하게 작성 (유저 키 + 수집 내용 키) 유저 샘플링
  • 93. MySQL JSON column On-demand Data Extractor를 위한 비정형 데이터 저장에 사용 MySQL 5.7.8 이상 VIEW를 만들면 Readonly SQL 테이블 처럼 활용 가능 • * 다른 RDBMS들도 JSON 타입을 지원하나 • 사용성이 MySQL이 좋았음
  • 94. 장애 대비 (1) 장애가 발생해선 안됨 개발팀 외부에서 만들어진 라이브러리 아무리 좋은 물건이더라도 크래쉬를 유발하거나 성능 문제를 일으키면 적용을 유보하거나 롤백할 수 밖에 없음
  • 95. 장애 대비 (2) 수집 쓰로틀링 지나치게 많이 수집되면 자동으로 수집양을 줄인다 의도치 않은 경우를 대비 크래쉬 상황 대비 SEH로 감싸기 기능별로 lua_State를 나눠서 사용
  • 96. 장애 대비 (3) 여전히 실수하면 문제가 발생할 여지는 있음 적용 대상을 적은 비율부터 점차적 확대 가능하게 개발자가 배포된 클라이언트로 ZERO 기능을 테스트 해 볼 수 있는 환경 제공
  • 97. Future Works (1) Hot Patch (실행 중 코드 수정) 실시간 버그 수정 예: 비정상적인 인자가 들어올 때 return 해버리게 JYP와 연동되어 컨텐츠별 성능 정보 추적 및 활용
  • 98. Future Works (2) Monitoring: Time-lapse view 장기간의 유저 흐름을 살펴보기 쌓인 데이터를 분석할 수 있는 툴 제공 사실상 무궁무진
  • 99. Future Works (3) 특허 출원 진행 중