OpenAIはコード復元(Unminifying)で驚くべき性能を示す
(glama.ai)- インターネットを見て回っていると、興味深いASCIIアートのコンポーネントを見つけた。見た目は素晴らしかったが、実装方法を理解するのは難しかった
- ソースコードを確認したが、コードは圧縮(Minified)されていた
圧縮されたコードの例
- コードの一部は次のとおり:
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 };
ChatGPTにコードの説明を依頼
- ChatGPTに「コードを説明してほしい」と依頼した
- ChatGPTの回答:
提供されたコードはかなり複雑ですが、各部分とその機能をわかりやすく説明します。
概要
- JavaScriptを使って複数の関数とコンポーネントを定義している
- Reactアプリケーションで動的なテキストコンテンツを生成している
- ASCIIアート、または事前定義された文字セットを用いた視覚効果を生成している
主なコンポーネントと関数
- 文字セットの選択:
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;
Mathの関数を取り込み、2つの文字セットを定義している- 現在時刻に応じて文字セットを選択している
- 選択した文字セットの長さを保存している
- 動的文字選択関数:
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コンポーネント (
tT):
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, left: 0, width: "100%", height: "100%" }, children: a.jsx("div", { ref: e, style: { width: "100%", height: "100%", whiteSpace: "pre", overflow: "hidden" } }) });
};
- このReactコンポーネントは、定期的に
div要素の内容を動的に生成された文字で更新する - ウィンドウサイズに応じて列数と行数を計算し、
divを文字で埋める
TypeScriptに変換されたコード
- ChatGPTにTypeScriptへ変換したコードを依頼した
- ChatGPTの回答:
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(null); const [size, setSize] = useState({ height: null, width: null }); useEffect(() => { const handleResize = () => { setSize({ height: window.innerHeight, width: window.innerWidth }); }; 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); return () => { clearInterval(intervalId); }; }, [size]); return ( ); }; export default () => { return ; };
GN⁺のまとめ
- この記事は、圧縮されたコードを理解し復元する方法を説明している
- ChatGPTを使って複雑なコードを説明し、TypeScriptへ変換する過程を示している
- コードの復元と理解を助けるためにAIツールを活用する方法を提示している
- 類似機能を持つプロジェクトとして
PrettierとUglifyJSがある
1件のコメント
Hacker Newsの意見
HumanifyJSの作者がLLMベースのツールを開発した。このツールはASTレベルでLLMを使い、unminification後もコードが動作することを保証する
JSのminificationは機械的で比較的単純だ。手作業で行うには面倒だが、変換自体は限定的なので、多少メモを取りながら識別子を追えば読むことはできる
一般的なunminificationやobfuscation解除は、依然として未解決の問題だ。過去に意図的に難読化したプログラムを書いたことがあるが、ChatGPTは表面的なレベルですら理解できなかった
一部の開発者は難読化のためにminificationを使うので、不満を持つかもしれない。ColdFusionの以前のバージョンで提供されていたコード"暗号化"ツールは弱いアルゴリズムだったにもかかわらず、安全だと思って使っていた人たちがいた
タイトルではOpenAIよりもChatGPTやgpt-4を使うほうが適切かもしれない
これは人間を上回る知的性能の一例だ。LLMはunminificationを理解しており、この特定のテーマでは人間より優れている
LLMはテキスト変換に優れている。これが彼らの中核的な強みだ
約1年前、GPTを使ってマルウェアの難読化解除を試みたが、自分でコードを分析するほうがよかった。最近もう一度試したところ、ある程度は理解できていた
LLMを使って複雑なコードをより読みやすくするのは非常に解放感がある。ときどき詳細を見落とすことはあるが、それは自分で確認できる
unminifiedコードがminifiedコードと一致しなければ、有用性は大きく低下する。多くの場合、コードを実行した後では差分を確認できない
Webサイトの計算アルゴリズムを知りたいとき、minifiedされたJSをChatGPTに入力すれば簡単に把握できた。LLMが完全なSaaSアプリをクローンできる未来も想像できる