_0x 계열 난독화는 JavaScript 난독화에서 가장 자주 보이는 형태 중 하나입니다. 실제 문자열을 배열에 숨기고, 숫자 인덱스를 함수로 감싼 뒤, 배열을 회전시켜 사람이 바로 읽기 어렵게 만듭니다. 하지만 브라우저가 실행해야 하는 문자열 자체는 어딘가에 남아 있으므로 구조를 알면 상당 부분 정리할 수 있습니다.
문자열을 배열로 빼는 것이 핵심입니다
원래 코드에 `fetch('/api/user')`가 있었다면 문자열 배열 난독화는 `const a=['/api/user','token']; fetch(fn(0x0))`처럼 바꿉니다. 여기서 `fn(0x0)`은 배열의 특정 위치를 돌려주는 함수입니다. 코드 전체에 문자열이 직접 보이지 않으니 검색과 독해가 어려워집니다.
첫 번째 해제 목표는 이 참조를 다시 실제 문자열로 치환하는 것입니다. `fn(0x0)`이 항상 같은 값을 돌려준다면 `'/api/user'`로 바꾸는 것만으로도 코드의 의미가 크게 드러납니다.
배열 회전은 인덱스를 일부러 틀어놓는 장치입니다
많은 난독화 코드는 시작 부분에 `while` 루프를 두고 배열의 앞 요소를 뒤로 보내는 식으로 회전시킵니다. 이 과정은 해시처럼 보이는 숫자 계산이 특정 값이 될 때까지 반복됩니다. 목적은 코드에 적힌 인덱스와 실제 문자열 위치를 바로 맞지 않게 만드는 것입니다.
해제 도구는 배열을 원래 실행 시점의 순서로 맞춘 뒤 참조 함수를 평가해야 합니다. 회전을 무시하고 치환하면 `/login`이 들어갈 자리에 `token`이 들어가는 식의 잘못된 결과가 나올 수 있습니다.
- 문자열 배열 함수 찾기
- 배열 회전 루프가 있는지 확인
- 실행 시점 배열 순서 계산
- 함수 호출을 실제 문자열로 치환
- 남은 별칭과 중복 변수를 정리
동적 디코딩이 섞이면 정답 후보를 좁힙니다
일부 난독화는 배열 안에 바로 문자열을 넣지 않고 Base64나 RC4로 한 번 더 감싼 값을 넣습니다. 이 경우 참조 함수가 배열 값을 꺼낸 뒤 디코딩 함수를 거쳐 실제 문자열을 만듭니다. 단순 검색으로는 URL이 보이지 않지만 디코딩 규칙을 찾으면 복원이 가능합니다.
다만 디코딩 키가 런타임 상태에 의존하거나 브라우저 API 결과를 섞으면 정적 해제만으로는 한계가 생깁니다. 이때는 복원된 문자열 후보와 실제 네트워크 요청 결과를 함께 확인해야 합니다.
YAL에서 확인할 수 있는 결과
YAL JS 난독화 해제기는 _0x 문자열 배열, 배열 회전, Base64·RC4 문자열 디코딩 신호를 감지하고 가능한 경우 실제 문자열로 치환합니다. 또한 치환된 문자열 중 URL, API 경로, storage 키를 리포트에 따로 모아 코드 전체를 읽기 전에 핵심 단서를 확인할 수 있게 합니다.
해제 결과가 완전하지 않더라도 `_0x123(0x9a)` 같은 호출이 `/api/test`처럼 바뀌면 사람이 판단할 수 있는 정보량이 크게 늘어납니다. 이것이 실무에서 난독화 해제 도구를 쓰는 핵심 이유입니다.