Javascript 코드를 통해 Activation 체크를 하는 경우가 종종있습니다.
공부용도로 그런 경우, 크래킹이 어떻게 이루어지는지에 대한 예시를 적어봅니다.
아래예시는 premium 유저에 대한 권한을 활성화하는 함수입니다.
상세 서술하자면, LICENSE_ACTIONS
라는 객체에서 ACTIVATE
함수를 트리거하는 것입니다.
a.handle(y.LICENSE_ACTIONS, async (e, { type: t, ...r }) => {
switch (t) {
/* 생략 */
case "ACTIVATE": {
const { key: e } = r;
return (async ({ key: e }) => {
r = process.platform, // Windows, Mac, Linux
a = await c();
return m(`${E}/activating`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({
id: "195a9b99-119385-c018b1jt692f",
key: e,
platform: r,
}),
})
.then((e) => e.json())
.then((t) => {
const { status: r, error: n } = t;
return n
? g.FAILED
: N.find((e=>e===r))
? h.set(f.hasValidLicense, !0),
h.set(f.licenseKey, e),
(S.hasValidLicense = !0),
w.emit("reload", { status: "ACTIVATION_COMPLETE" }),
g.VALID)
: (N(r), r);
여기서 자세하게 볼 내용은 아래와 같이 async
구문에서 성공적으로 통신했을 시에 resolve 하는 then
구문입니다.
.then((e) => e.json())
.then((t) => {
const { status: r, error: n } = t;
return n
? g.FAILED
: N.find((e=>e===r))
? h.set(f.hasValidLicense, !0),
h.set(f.licenseKey, e),
(S.hasValidLicense = !0),
w.emit("re-load", { status: "ACTIVATION_COMPLETE" }),
g.VALID)
: (N(r), r);
대강 유추를 해보면 license key 를 저쪽 URL 에 던졌을 때, n 이 에러 여부라고 가정하면 key 에 문제가 있으면 n 이 true
가 될 것 입니다.
그렇게 되면 g.FAILED
라는 실패 신호같은 것이 return 이 되겠죠.
그러면 여기서 알 수 있는 것은 g.FAILED
에 반대되는 것이 g.VALID
라고 했을 때, g.FAILED
를 g.VALID
로 교체하면 인가되지않은 권한에 대한 활성화가 가능할 수도 있겠다는 것입니다.
그러나 삼항연산자 else 문을 보면, 다른 함수들도 활성화에 관여하기 때문에 단순 g.VALID
리턴만으로는 활성화가 가능하지 않겠다는 생각을 해야합니다.
그러면 아예 삼항연산자 모든 경우에 대해 아래와 같은 최종 구문을 넣으면 ACTIVATION 이 될 것이라고 판단할 수 있습니다.
h.set(f.hasValidLicense, !0),
h.set(f.licenseKey, e),
(S.hasValidLicense = !0),
w.emit("re-load", { status: "ACTIVATION_COMPLETE" }),
g.VALID)
그러나 이것만으로는 별다른 변화를 이끌어내지 못했다면, LICENSE_ACTIONS.ACTIVATE
를 호출하는 시작부분을 살펴보아야합니다.
(await i.ipcRen.invoke(v.ipcEvents.LICENSE_ACTIONS, {
type: "VALIDATE",
key: n,
}))
? (await i.ipcRen.invoke(
v.ipcEvents.LICENSE_ACTIONS,
{ type: "ACTIVATE", key: n }
)) !== h.Tm.VALID
? (s(!1), void o(!0))
: void e()
: (s(!1),void o(!0));o(!0)
);
해당 부분이 ACTIVATE
에 대한 유일한 트리거 포인트라고 잡아봅시다.
이 부분도 똑같이 삼항연산자로 이루어져있는데 자세히 보면, VALIDATE
로직이 먼저 통과 되어야 ACTIVATE
로 넘어가는 것을 볼 수 있습니다.
그렇다면, VALIDATE
로직이 통과되지 않아도 ACTIVATE
가 실행되도록 하면 크랙이 완료될 것입니다.
(await i.ipcRen.invoke(v.ipcEvents.LICENSE_ACTIONS, {
type: "VALIDATE",
key: n,
}))
? (await i.ipcRen.invoke(
v.ipcEvents.LICENSE_ACTIONS,
{ type: "ACTIVATE", key: n }
)) !== h.Tm.VALID
? (s(!1), void o(!0))
: void e()
: (await i.ipcRen.invoke(
v.ipcEvents.LICENSE_ACTIONS,
{ type: "ACTIVATE", key: n }
)) !== h.Tm.VALID
? (s(!1), void o(!0))
: void e()
이런 크랙 방식은 CTF 를 비롯한 Electron 기반의 앱 분석시 큰 도움이 될 것이라고 생각합니다.
'<보안 study> > 리버싱' 카테고리의 다른 글
MFC 프레임워크 기반 앱 시작지점 (0) | 2023.01.06 |
---|---|
공유메모리 매핑 (0) | 2023.01.04 |
자바 1.8.0 다운 (0) | 2022.09.07 |
[iOS] iOS 어플 IDA로 원격 디버깅 하기 (1) | 2022.05.20 |
frida-tools 설치 시, SSL 오류 해결 (Ubuntu20.04 기준) (2) | 2022.05.16 |