선요약 : 글이 길다.
json 파일이 암호화 된거 조심하자.
정제안된 원문을 그냥 그대로 번역해서 가져온 긴 글이니 원한다면 ai한테 글 통채로 던져서 요약해달라고 해보자
최근에 뭐 json 암호화 되어서 이거 어떻게 하냐고 하는글 때문에
전에 봤던 MZ 1.10부터 암호화를 다르게 해놨네 라고 누군가가 쓴글을 보고 대체 겜파일에 뭔짓을 해놓은겨 라고 생각하고
처음엔 누군가 장난쳐놓고 'ㅈ뺑이쳐라 이놈들아.' 하는거처럼 겜을 지가 사서 암호화(장난질)하고 배포하나 싶었다가
이거 암호화를 했으면 그걸 푸는방법도 있겠네 하고 생각하던와중에 유틸탭에 두개의 디스크립트 게시글도 보고
그거 보면서 혼자서 곰곰히 생각하니 이건 누군가가 뭔 의도를 가지고 이렇게 하는거 아닐까 라는 생각이 들길래 해외 사이트에서 찾아봤는데
영어권 야겜사이트에서 json descript 이 두 단어로 검색돌리다가 찾게된 게시글을 보고 여기에 글을씀
밑에서부턴 크롬 웹 확장프로그램의 ai번역으로 해석한 내용. 닉은 풀로 표시안하고 대략적인 내가 훑어본 흐름에 따라 작성
============================
Mo : 이곳에 게시된 " RJ01588706 "에 관하여 (이 파일만 확인했습니다),해당 사용자가 게시한 모든 신작 게임의 JSON 파일과
기타 파일들이 심하게 난독화되어 있는 것으로 보아 원본 출처는
https://www.tokyotosho.info/search.php?username=Reeffress 인 것 같습니다.
단순히 파일을 난독화만 한 것인지 아니면 다른 의도가 있는지는 알 수 없으므로 다운로드 시 주의하시기 바랍니다.
이에 대한 Claude의 분석 내용입니다:
**난독화 작동 방식**
확인된 도구: MZDataCrypt v1.6.0
이것은 잘 알려진 RPG Maker MZ 데이터 암호화 도구입니다. "bid" 필드("bid":"1.6.0")는 문자 그대로 도구의 버전입니다.
보호 체계는 데이터 암호화와 변조 방지의 두 계층으로 나뉘며, 두 가지 모두 rmmz_managers.js에 직접 주입되어 있습니다.
**레이어 1 — 데이터 암호화**
주입 지점: rmmz_managers.js의 DataManager.onXhrLoad가 완전히 교체되었습니다 (약 7KB의 단일 행으로 압축됨).
형식: 모든 data/*.json 파일은 다음과 같이 변경됩니다:
{ "uid": "<8자리 16진수>", "bid": "1.6.0", "data": "" }
키 유도 알고리즘:
MASTER_KEY = 0xAB (171, window._K로 하드코딩됨)
per_file_key(파일명):
t = 파일명의 아스키(ASCII) 문자 코드 합계 (확장자 제외)
return MASTER_KEY XOR (t & 0xFF)
decrypt(base64_data, 파일명):
bytes = base64_decode(base64_data)
key = per_file_key(파일명)
for i: bytes ^= key // 단일 바이트 XOR, 파일 전체에 동일한 키 적용
return utf8_decode(strip_BOM(bytes))단일 바이트 XOR 방식입니다.
모든 위치에 동일한 바이트가 적용됩니다. 롤링 키도, IV도 없으며, 제대로 된 암호화라고 할 수 없습니다.
uid 필드는 의미 있어 보이지만 실제 복호화에는 사용되지 않는 단순한 파일 식별자일 뿐입니다.
취약점: 단일 바이트 XOR이기 때문에, 출력 결과가 유효한 UTF-8 JSON인지 확인하는 것만으로
최대 256번의 시도 안에 키를 무차별 대입(brute-force)할 수 있습니다.
제가 실제로 그렇게 했습니다. 또는 MASTER_KEY = 0xAB라는 것을 알면 직접 계산할 수도 있습니다.
미리 계산된 키 (모두 실제 파일로 검증됨):
파일 키
System 0x2E
Actors 0xC7
MapInfos 0xB6
Skills 0xD9
Map001/010 0x04
Map002/011/020 0x1B
Map003/012/021/030 0x1A
… (자릿수 합에 따른 순차적 구성) …
자릿수 합이 같은 맵들은 키를 공유한다는 점에 유의하십시오 (예: Map001과 Map010은 모두 키 0x04를 사용함). 이 또한 또 다른 취약점입니다.
레이어 2 — 변조 방지 (NW.js 전용)
`if (typeof window._K === 'undefined')`를 통해 첫 번째 XHR 로드 시 한 번 실행됩니다.
Node.js의 fs/path 모듈을 직접 사용하며, 브라우저가 아닌 NW.js(RPG Maker MZ와 함께 제공되는 런타임)에서만 작동합니다.
주입된 코드 내 문자열 난독화: 내부 문자열을 위한 하위 암호화가 존재합니다.
```javascript
f341986 = (s) => {
b = Buffer.from(s, 'base64')
for (let i = 0; i < b.length; i++) b[i] ^= 0x77
return b.toString()
}
```
Base64 디코딩 → 0x77로 XOR 연산. 디코딩된 문자열:
* 'Gx4VMDsyJAFFWRMbGw==' → libGLESv2.dll
* 'Gx4VMjA7WRMbGw==' → libEGL.dll
* 'ExIVAhAVAhAOEgQDHhkQWQMSBAM=' → debugbugyesting.test
파일 무결성 검사 (v6f3bb0): libGLESv2.dll 및 libEGL.dll(게임과 함께 배포된 NW.js 런타임 DLL)에서 원본 바이트를 읽어 다음 사항을 확인합니다:
1. 정확한 파일 크기
2. 특정 오프셋의 32바이트가 하드코딩된 16진수 문자열과 일치하는지 여부
3. 파일의 마지막 16바이트가 모두 0인지 여부
이 검사 중 하나라도 실패하면 `window.close(); process.exit()`가 즉시 실행되어 게임을 종료합니다.
이는 NW.js 런타임을 패치된 버전으로 교체하는 것을 방지합니다.
개발자 도구 차단: 게임 디렉토리에 `debugbugyesting.test`라는 이름의 폴더가 존재하는 경우에만
F12/F8로 개발자 도구를 열 수 있습니다. 그렇지 않으면 아무 일도 일어나지 않습니다.
F10은 모든 `data/*.json` 파일이 올바르게 복호화되는지 확인하기 위해 전체 자가 스캔을 트리거합니다.
요약
| 항목 | 세부 사항 |
| :--- | :--- |
| 도구 | MZDataCrypt 1.6.0 |
| 데이터 암호 | 단일 바이트 XOR (매우 쉽게 역산 가능) |
| 키 소스 | 0xAB XOR (파일명_문자_합계 & 0xFF) |
| 마스터 키 | window._K에 하드코딩된 0xAB |
| 변조 방지 | NW.js DLL 바이트 패턴 검사 (libGLESv2.dll, libEGL.dll) |
| 개발자 도구 조건 | debugbugyesting.test/ 폴더가 존재해야 함 |
| 문자열 난독화 | 내부 파일명에 대해 Base64 + XOR 0x77 적용 |
| 주입 지점 | rmmz_managers.js의 DataManager.onXhrLoad |
===================
Mo : RJ01596306 게임의 최신 버전과 보안 도구인 MZDataCrypt(v1.6.0 vs v1.10.0)의 리버스 엔지니어링 분석 내용입니다.
분석 결과, 동일한 도구를 사용한 RPG Maker MZ 게임들 사이에서 버전 업데이트에 따른 암호화 성능의 대폭적인 향상이 확인되었습니다.
Overview
Aspect | v1.6.0 | v1.10.0 |
|---|---|---|
bid field | 1.6.0 | 1.10.0 |
Master key _K | 171 (0xAB) | 211 (0xD3) |
Filename hash | Sum of char codes | djb2 rolling hash |
Per-byte cipher | Flat single-byte XOR | Rolling stream cipher w/ feedback |
String cipher | base64 + XOR 0x77 | base64 + XOR 0x61 (bitwise disguise) |
Dev folder | debugbugyesting.test | oK5dpQ1prR2ml6xG8ceI0ksH4ndW3i.test |
Anti-debugger | None | setInterval(debugger) every 50ms |
DLL integrity | libGLESv2 + libEGL | Same (identical expected hashes) |
Data Format
모든 `data/*.json` 파일이 다음과 같이 대체됩니다:
{
"uid": "<8 hex chars>", // per-file identifier, NOT used in decryption
"bid": "1.x.x", // tool version
"data": "<base64>" // encrypted payload
}v1.6.0 — 키 유도
단순하고 취약함. 단일 바이트 XOR, 파일의 모든 바이트에 동일한 키가 적용됨.
MASTER_KEY = 0xAB // 171, hardcoded as window._K
function getFileKey(filename): // no extension
t = sum of charCode(c) for each c in filename
return MASTER_KEY XOR (t & 0xFF)
function decrypt(base64_data, filename):
bytes = base64_decode(base64_data)
key = getFileKey(filename)
for i in range(len(bytes)):
bytes[i] ^= key // uniform XOR, trivially reversible
return utf8_decode(strip_BOM(bytes))취약점:
단일 바이트 XOR은 출력이 유효한 UTF-8 JSON인지 확인하여 최대 256번의 시도 내에 무차별 대입 공격이 가능합니다.
문자 코드 합계가 같은 파일 이름들은 키를 공유합니다(예: Map001과 Map010은 둘 다 0x04로 동일).
Precomputed keys (v1.6.0):
System → 0x2E Actors → 0xC7 MapInfos → 0xB6
Skills → 0xD9 States → 0xDF Troops → 0x2C
Weapons → 0x76 Tilesets → 0xE6 Items → 0xA9
Enemies → 0x6D Armors → 0xDF Classes → 0x65
Map001/010 → 0x04 Map002/011/020 → 0x1B
Map003/012/021/030 → 0x1A (sequential by digit sum)v1.10.0 — 키 유도
주요 업그레이드. djb2 해시 및 암호문 피드백이 포함된 롤링 스트림 암호를 사용합니다.
Step 1 — Filename hash (djb2):
t = 0
for each char c in filename (no extension):
t = (t << 5) - t + charCode(c) // djb2: t * 31 + c
t &= 0xFFFFFFFF // keep 32-bitdjb2가 해시를 훨씬 더 잘 분산하여, Map001/Map010이 더 이상 충돌하지 않습니다.
Step 2 — Base file key:
fk = _K XOR (t & 0xFF) // _K = 211 (0xD3)(JS에서 XOR은 의도를 숨기기 위해 (a | b) & ~(a & b)로 작성됩니다)
Step 3 — Rolling stream cipher:
l = fk // initial state
for each byte i:
rotated = ((l << 4) XOR (l >> 2)) & 0xFF
k = ((fk XOR 60) + (i % 256) + rotated) XOR 122 + 19 (mod 256)
decrypted = raw[i] XOR k
raw[i] = decrypted
l = decrypted // ← feedback: next key depends on previous OUTPUTv1.6.0과의 주요 차이점:
이것은 암호문 피드백(ciphertext feedback) 방식의 스트림 암호입니다.
각 바이트의 키는 이전에 복호화된 모든 바이트에 의존합니다. _K를 모르면 파일별로 무차별 대입(brute-force)을 할 수 없는데,
잘못된 키를 사용하면 즉시 데이터가 연쇄적으로 깨지기 때문입니다.
주입 지점
두 버전 모두 한 줄로 압축된(약 7–9KB) js/rmmz_managers.js 내의 DataManager.onXhrLoad를 패치합니다. 함수 전체가 교체됩니다.
변조 방지 — 두 버전 모두 해당
첫 번째 XHR 로드 시 if (typeof window._K === 'undefined')를 통해 한 번 실행됩니다. NW.js 전용 (Node.js의 require('fs') / require('path') 사용).
DLL 무결성 검사: NW.js 런타임 DLL에서 원시 바이트(raw bytes)를 읽어 다음 사항을 확인합니다:
- 정확한 파일 크기
- 특정 오프셋의 32바이트가 하드코딩된 16진수 문자열과 일치하는지 여부
- 파일의 마지막 16바이트가 모두 0인지 여부
// Targets (both versions, identical expected hashes):
libGLESv2.dll size=8192528 offset=10000 hash=D34889CF488B054DE375...
libEGL.dll size=386576 offset=5000 hash=2558000000488B0CCA3B...둘 중 하나의 검사가 실패하면 → 즉시 window.close(); process.exit()를 실행합니다.
개발자 도구 차단: dev 폴더가 존재하는 경우에만 F12/F8로 개발자 도구를 열 수 있습니다.
버전마다 폴더 이름이 변경되었습니다. v1.6.0은 읽기 쉬운 이름을 사용했지만, v1.10.0은 무작위로 생성된 알 수 없는 문자열을 사용합니다.
String obfuscation for internal filenames:
// v1.6.0: base64_decode(s), then XOR each byte with 0x77
// v1.10.0: base64_decode(s), then XOR each byte with 0x61
// (written as (b[i] | 97) & ~(b[i] & 97) to hide the XOR)v1.10.0 전용 — 활성 안티 디버그
v1.10.0의 새로운 기능입니다. dev 폴더가 없으면 50ms마다 디버거 트랩을 발생시킵니다:
setInterval(function() {
(function(){}).constructor('debugger')();
}, 50);단순 문자열 검색을 피하기 위해 리터럴 debugger 키워드 대신 Function.constructor를 사용합니다.
이 인터벌을 먼저 비활성화하지 않으면 개발자 도구를 거의 사용할 수 없게 됩니다.
RJ01569430의 경우, 정품을 구매해 비교해 보니 차이점은 'renpy' 폴더에 있었습니다.
==========================================
Mr
: 그래서 이게 이상한 DRM인가요, 아니면 안에 숨겨진 악성코드가 있는 건가요?
==========================================
Mr
: 또한, 이 소프트웨어 페이지들은 어제 해킹되었으며, RAT 악성코드가 포함되어 있었습니다 (4월 9일-10일).
게이머들이 CPU-Z를 꽤 많이 사용하니 주의하세요. 해킹된 버전은 다음과 같습니다:
CPU-Z 2.19
HWMonitor 1.63
===========================================
Mo : 이 파일에 악성코드가 숨겨져 있는지 확실하지는 않지만,
확인한 RJ01588706 의 경우 libEGL.dll과 libGLESv2.dll 파일 끝에 0바이트가 추가되어 있을 뿐입니다
rmmz_managers.js에는 json 파일 복호화 코드가 포함되어 있고, 그게 전부인 것 같습니다.
이곳에 게시된 또 다른 새 게임인
RJ01464379
도 같은 방식의 난독화가 적용되어 있습니다.
RPG Maker MZ가 아닌 MV 버전이라 일부 차이는 있을 수 있습니다.
=====================================
Mr : 새로운 정보로 게시물을 업데이트했습니다 (Update New info 확인) .
이 RPGM 악성코드에 감염되었는지 확인하는 방법도 추가했으니 확인해 보시기 바랍니다.
추가 정보를 확인하는 대로 업데이트하겠습니다.
========================================
Mr : 새로운 정보입니다. 동일한 멀웨어가 f95 사이트의 RenPy 게임/모드 갤러리를 감염시켰던 스페인 사람으로부터 유입되었습니다. 이전 버전 멀웨어에 대한 'colobancuz'의 분석을 인용하겠습니다:
감염된 파일:
Previous RenPy malware analysis ( F95 ):
이 파일(zaesdl)에는 RPGM 버전의 것과 동일한 C2 URL이 포함되어 있으므로, 동일한 RAT입니다:
===========================
이거 이후로 글은 대부분 AI요약으로 코드에 대한 분석들 + 감염경로 확인법 인데
글 접는법을 몰라서 다 풀어쓰면 너무 글이 길어져서 일단 해석은 보류하고
요약하자면
1 - 게임파일 돚거(해외사이트)할때 꺼넘버로 된것들과 renpy게임 json파일을
압축상태에서 '편집' 으로 json을 열었을때 원래 코드가 아니라 암호화된 코드면 조심하자. (뭔가 들어 있을지도 모른다)
2 - 글쓴이도 이틀전 쯤 부터 의문가지고 찾다가 어제 새벽에 저 첫글을 발견한건데 이걸 어떻게 알려야하지 고민하다가 일단 글써봄
못배운놈이라 글 못써도 이해좀
3 - 위 링크 '새로운 정보' 원문 글 속에 해결 방법은 있는거 같은데 크롬 웹브라우저 번역으로 뭐라적었는지 보려니 너무 힘들다
같이 저거 보고 뭐 어캐할지 의논이 좀 필요할거 같아서 글써봄
==========================
Mr 라고 닉 단 사람이 해답을 어느정도 알고있는거 같은데
나도 지금 이게 뭐가 어떻게 돌아가는지 모르니 댓글로 '해킹당하는거에요?' 이렇게 물으면 답 못해줌
근들갑 떠는거 아니냐 라고 생각할수도 있지만
25년 12월 멀웨어 이슈
가 있었기 때문에 혹시나 해서 글쓴거임
내가 틀린거라면 뭐 근들갑이 맞겠지..
일단 Reeffress 인가 하는놈이 토렌트로 올린거 파일 뭔지 RJ 넘버랑 해서 겜 목록좀 먼저 뽑으러감
