用ChatGPT逆向工程压缩后的Js代码,表现惊艳 精华
互联网世界里,每个开发者都有这么一个瞬间:你遇到一个炫酷的小组件,想知道它是怎么实现的,但源码却是最小化的,看起来全是乱码。这时,你会怎么做?今天,Frank Fiegel要和大家分享一个非常时髦和实用的方法——用ChatGPT来逆向工程被压缩的JavaScript代码。
发现炫酷组件
在某个闲暇的夜晚,我无意间浏览到了一个网站,发现了一个非常有趣的组件(https://reactive.network/hackathon)。它展示了一个以ASCII艺术形式呈现的动态动画,着实是令人眼前一亮。
动态效果
出于好奇,我决定深入研究它的实现。但当我打开源码时,发现这些代码被压缩和最小化了,看起来十分费解。
const { floor: ra, abs: KE, min: QE } = Math,
O5 = ["reactive.network REACTIVE.NETWORK", "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/|()1{}[]?-_+~<>i!lI;:,^`'. .:â–‘â–’â–“â–ˆ"],
G7 = Date.now() % 3 ? O5[1] : O5[0],
V5 = G7.length,
JE = { fps: 60 };
function eT(e, t, n, r) {
const i = t.time * 8e-5,
s = QE(t.cols, t.rows),
o = t.metrics.aspect * 0.2,
l = { x: ((4 * (e.x - t.cols / 6.25)) / s) * o, y: (5 * (e.y - t.rows / 4)) / s },
u = ra(KE(YE(l) - i) * V5 + (ra(e.x / 1) % 2) * 2) % V5;
return G7[u];
}
const tT = () => {
const e = j.useRef(null),
[t, n] = j.useState({ height: null, width: null });
return (
j.useEffect(() => {
function r() {
n({ height: window.innerHeight, width: window.innerWidth });
}
if (typeof window < "u") return n({ height: window.innerHeight, width: window.innerWidth }), window.addEventListener("resize", r), () => window.removeEventListener("resize", r);
}, []),
j.useEffect(() => {
const r = e.current;
if (!r) return;
const i = 12,
s = ra(t.width / i) * 1.6,
o = ra(t.height / i),
l = { aspect: s / o },
u = setInterval(() => {
let c = "";
for (let d = 0; d < o; d++) {
for (let f = 0; f < s; f++) c += eT({ x: f, y: d }, { cols: s, rows: o, metrics: l, time: Date.now() });
c += `
`;
}
r.textContent = c;
}, 1e3 / JE.fps);
return () => clearInterval(u);
}, [t]),
a.jsx("div", { style: { position: "absolute", top: 0, left: 0, width: "100%", height: "100%" }, children: a.jsx("div", { ref: e, style: { width: "100%", height: "100%", whiteSpace: "pre", overflow: "hidden" } }) })
);
};
function nT(e) {
return Math.cos(e.x * e.x - e.y * e.y);
}
const { floor: ia, abs: rT, min: iT } = Math,
D5 = ["reactive.network REACTIVE.NETWORK", "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/|()1{}[]?-_+~<>i!lI;:,^`'. .:â–‘â–’â–“â–ˆ"],
X7 = Date.now() % 3 ? D5[1] : D5[0],
F5 = X7.length,
sT = { fps: 60 };
function oT(e, t, n, r) {
const i = t.time * 8e-5,
s = iT(t.cols, t.rows),
o = t.metrics.aspect * 0.2,
l = { x: ((4 * (e.x - t.cols / 6.25)) / s) * o, y: (5 * (e.y - t.rows / 4)) / s },
u = ia(rT(nT(l) - i) * F5 + (ia(e.x / 1) % 2) * 2) % F5;
return X7[u];
}
const aT = () => {
const e = j.useRef(null),
[t, n] = j.useState({ height: null, width: null });
return (
j.useEffect(() => {
function r() {
n({ height: window.innerHeight, width: window.innerWidth });
}
if (typeof window < "u") return n({ height: window.innerHeight, width: window.innerWidth }), window.addEventListener("resize", r), () => window.removeEventListener("resize", r);
}, []),
j.useEffect(() => {
const r = e.current;
if (!r) return;
const i = 12,
s = ia(t.width / i) * 1.6,
o = ia(t.height / i),
l = { aspect: s / o },
u = setInterval(() => {
let c = "";
for (let d = 0; d < o; d++) {
for (let f = 0; f < s; f++) c += oT({ x: f, y: d }, { cols: s, rows: o, metrics: l, time: Date.now() });
c += `
`;
}
r.textContent = c;
}, 1e3 / sT.fps);
return () => clearInterval(u);
}, [t]),
a.jsx("div", { style: { position: "absolute", top: 0, left: 0, width: "100%", height: "100%" }, children: a.jsx("div", { ref: e, style: { width: "100%", height: "100%", whiteSpace: "pre", overflow: "hidden" } }) })
);
},
使用ChatGPT逆向工程
通常情况下,我会耐心地阅读这些乱成一团的代码,尝试理解其实现原理,或者找一个.map文件来还原原本的代码。然而,这次我想到了一个新奇的办法:为什么不让ChatGPT帮我解读这些代码呢?
于是,我将以上代码复制并询问ChatGPT:“能解释一下这段代码吗?”以下是它的回复:
概览
代码定义了几个函数和组件,特别是用于React应用的。它主要处理生成一些动态文本内容,可能是ASCII艺术或者使用预定义字符集的某些视觉效果。此外,它还包括事件处理、性能计时和HTML内容转换的实用工具函数。
核心组件和函数
- 字符集选择:
const { floor: ra, abs: KE, min: QE } = Math;
const O5 = ["reactive.network REACTIVE.NETWORK", "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/|()1{}[]?-_+~<>i!lI;:,^`'. .:â–‘â–’â–“â–ˆ"];
const G7 = Date.now() % 3 ? O5[1] : O5[0];
const V5 = G7.length;
这部分代码引入了一些常用的数学函数,并定义了两个字符集。基于当前的时间戳(Date.now() % 3),选择其中一个字符集使用,并计算其长度。
- 动态字符选择函数:
function eT(e, t, n, r) {
const i = t.time * 8e-5;
const s = QE(t.cols, t.rows);
const o = t.metrics.aspect * 0.2;
const l = { x: ((4 * (e.x - t.cols / 6.25)) / s) * o, y: (5 * (e.y - t.rows / 4)) / s };
const u = ra(KE(YE(l) - i) * V5 + (ra(e.x / 1) % 2) * 2) % V5;
return G7[u];
}
eT函数根据输入位置和当前时间生成字符。调整位置后,基于时间和位置选择一个字符。
- React组件用于显示动态内容:
const tT = () => {
const e = j.useRef(null);
const [t, n] = j.useState({ height: null, width: null });
j.useEffect(() => {
function r() {
n({ height: window.innerHeight, width: window.innerWidth });
}
if (typeof window < "u") {
n({ height: window.innerHeight, width: window.innerWidth });
window.addEventListener("resize", r);
return () => window.removeEventListener("resize", r);
}
}, []);
j.useEffect(() => {
const r = e.current;
if (!r) return;
const i = 12;
const s = ra(t.width / i) * 1.6;
const o = ra(t.height / i);
const l = { aspect: s / o };
const u = setInterval(() => {
let c = "";
for (let d = 0; d < o; d++) {
for (let f = 0; f < s; f++) c += eT({ x: f, y: d }, { cols: s, rows: o, metrics: l, time: Date.now() });
c += `
`;
}
r.textContent = c;
}, 1e3 / JE.fps);
return () => clearInterval(u);
}, [t]);
return a.jsx("div", { style: { position: "absolute", top: 0, lef t: 0, width: "100%", height: "100%" }, children: a.jsx("d iv", { ref: e, style: { width: "100%", height: "100%", whiteSpa ce: "pre", overflow: "hidden" } }) });
};
这是一个React组件,设置一个间隔以定期更新div元素的内容。它根据窗口大小计算列和行数,然后用动态生成的字符填充div。
将代码转换为TypeScript并变得可读
我进一步要求ChatGPT将其中的代码转换为TypeScript,并使其更加可读。以下是它的回复:
import React, { useEffect, useRef, useState } from 'react';
const selectedCharacterSet =
"$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/|()1{}[]?-_+~<>i!lI;:,^`'. .:â–‘â–’â–“â–ˆ";
const characterSetLength = selectedCharacterSet.length;
const calculateCharacter = (
x: number,
y: number,
cols: number,
rows: number,
aspect: number,
time: number,
): string => {
const timeFactor = time * 0.000_08;
const size = Math.min(cols, rows);
const aspectRatio = aspect * 0.2;
const position = {
x: ((4 * (x - cols / 6.25)) / size) * aspectRatio,
y: (5 * (y - rows / 4)) / size,
};
const index =
Math.floor(
Math.abs(
Math.cos(position.x * position.x - position.y * position.y) -
timeFactor,
) *
characterSetLength +
(Math.floor(x) % 2) * 2,
) % characterSetLength;
return selectedCharacterSet[index];
};
type Size = {
height: null | number;
width: null | number;
};
const AsciiArtGenerator: React.FC = () => {
const textRef = useRef<HTMLDivElement>(null);
const [size, setSize] = useState<Size>({ height: null, width: null });
useEffect(() => {
const handleResize = () => {
setSize({ height: window.innerHeight, width: window.innerWidth });
};
// Initial size setting
handleResize();
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
useEffect(() => {
const element = textRef.current;
if (!element || !size.width || !size.height) return undefined;
const cols = Math.floor(size.width / 12) * 1.6;
const rows = Math.floor(size.height / 12);
const aspectRatio = cols / rows;
const intervalId = setInterval(() => {
let content = '';
for (let y = 0; y < rows; y++) {
for (let x = 0; x < cols; x++) {
content += calculateCharacter(
x,
y,
cols,
rows,
aspectRatio,
Date.now(),
);
}
content += '\n';
}
element.textContent = content;
}, 1_000 / 60); // 60 fps
return () => {
clearInterval(intervalId);
};
}, [size]);
return (
<div
ref={textRef}
style={{
height: '100%',
overflow: 'hidden',
whiteSpace: 'pre',
width: '100%',
}}
/>
);
};
export default () => {
return <AsciiArtGenerator />;
};
当我将ChatGPT生成的代码与原始组件进行比较时,我发现输出几乎是完全一致的。虽然最初我认为LLM在复制代码时忽略了一些实现细节,但进一步调查发现,问题很可能是字符编码错误。还原正确字符后,输出是完全一致的。
笔者尝试使用当红辣子鸡Cursor来复现这一过程,看看是否能够完成这一工作。以下是cursor生成的代码,大家看chatGPT和Cursor Claude 3.5谁更胜一筹:
import React, { useRef, useState, useEffect } from 'react';
const ASCII_CHARS = "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/|()1{}[]?-_+~<>i!lI;:,^`'. .:■■□□";
const TEXT = "reactive.network REACTIVE.NETWORK";
const CHAR_SET = Math.random() < 0.67 ? ASCII_CHARS : TEXT;
const CHAR_LENGTH = CHAR_SET.length;
const FPS = 60;
function calculateChar(x, y, cols, rows, aspect, time) {
const scale = Math.min(cols, rows);
const adjustedX = ((4 * (x - cols / 6.25)) / scale) * aspect * 0.2;
const adjustedY = (5 * (y - rows / 4)) / scale;
const value = Math.cos(adjustedX * adjustedX - adjustedY * adjustedY);
const index = Math.floor(Math.abs(value - time * 8e-5) * CHAR_LENGTH + (Math.floor(x) % 2) * 2) % CHAR_LENGTH;
return CHAR_SET[index];
}
const AsciiAnimation = () => {
const containerRef = useRef(null);
const [dimensions, setDimensions] = useState({ height: null, width: null });
useEffect(() => {
const updateDimensions = () => {
setDimensions({ height: window.innerHeight, width: window.innerWidth });
};
if (typeof window !== 'undefined') {
updateDimensions();
window.addEventListener('resize', updateDimensions);
return () => window.removeEventListener('resize', updateDimensions);
}
}, []);
useEffect(() => {
const container = containerRef.current;
if (!container) return;
const cellSize = 12;
const cols = Math.floor(dimensions.width / cellSize) * 1.6;
const rows = Math.floor(dimensions.height / cellSize);
const aspect = cols / rows;
const animationInterval = setInterval(() => {
let frame = '';
for (let y = 0; y < rows; y++) {
for (let x = 0; x < cols; x++) {
frame += calculateChar(x, y, cols, rows, aspect, Date.now());
}
frame += '\n';
}
container.textContent = frame;
}, 1000 / FPS);
return () => clearInterval(animationInterval);
}, [dimensions]);
return (
<div style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%' }}>
<div ref={containerRef} style={{ width: '100%', height: '100%', whiteSpace: 'pre', overflow: 'hidden' }} />
</div>
);
};
export default AsciiAnimation;
最后,不得不承认,LLM正变得越来越强大,它不仅能够进行复杂的代码解释,还能帮你逆向实现一些非常实用的功能。可以预见未来还会有更多用途被挖掘出来,大家拭目以待。
参考:https://glama.ai/blog/2024-08-29-reverse-engineering-minified-code-using-openai
本文转载自 AI工程化,作者: ully