OpenAI 在代码反混淆方面表现惊人

原文信息: 查看原文查看原文

OpenAI is shockingly good at unminifying code

- Frank Fiegel

在互联网上寻找灵感时,我偶然发现了一个看起来很有趣的组件

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" } }) })
    );
},

通常,我会直接阅读压缩后的代码来理解实现(或者寻找 .map 文件来恢复原始代码)。然而,我意识到我从未尝试过让 ChatGPT 为我做这件事… 所以我复制了上述所有代码,并询问 ChatGPT "解释代码"。这是回应:

提供的代码相当复杂,但我将把它分解成更容易理解的格式,解释其不同部分及其功能。

概览

代码使用 JavaScript 定义了几个函数和组件,特别是在 React 应用程序的上下文中。它处理生成一些动态文本内容,可能是 ASCII 艺术或使用预定义字符集的一些视觉效果。此外,它包括用于事件处理、性能计时和 HTML 内容转换的实用函数。

关键组件和函数

  1. 字符集选择:

    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;
    
    • 这部分代码导入了一些常见的数学函数(floorabsmin),并定义了两组字符,存储在 O5 中。
    • 根据当前时间(Date.now() % 3),它选择这些集合中的一个(G7)以供后续使用。
    • 选定字符集的长度存储在 V5 中。
  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 函数根据输入位置(e.xe.y)和当前时间(t.time)生成一个字符。
    • 它使用一些纵横比计算调整位置,并根据位置和时间从 G7 中选择一个字符。
    • YE 函数的目的从代码中不直接清楚,但它似乎是在此计算中使用的自定义函数。
  3. 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(() =&gt; {
        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(() =&gt; {
            let c = "";
            for (let d = 0; d &lt; o; d++) {
                for (let f = 0; f &lt; 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 元素(e.current)的内容,用动态生成的字符填充。
    • 它根据窗口大小计算列数(s)和行数(o),然后用 eT 函数生成的字符填充 div
    • 效果以指定的每秒帧数(JE.fps)运行。

我接着询问“用 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 });
    };

    // 初始大小设置
    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 />;
};

令我惊讶的是,回应不仅足够好,而且非常易于阅读。

这是上述组件的实际效果:

比较输出,~看起来 LLM 回应忽略了一些实现细节~,但它仍然是一个足够好的实现,可以从中学习。绝对是我之前没有考虑过的一个很好的用例!

更新(2024-08-29):最初,我认为 LLM 没有准确复制逻辑,因为输出缺少原始组件中可见的一些字符(例如,░▒▓█)。然而,HN 论坛上的一位用户 指出这可能是复制粘贴错误。

经过进一步调查,我发现原始代码包含的字符与我粘贴到 ChatGPT 中的字符不同。这似乎是一个编码问题,下载脚本后我能够获得正确的字符。在更新代码以使用正确的字符后,输出现在与原始组件完全相同。

我为错误地指责你犯了错误而道歉,GPT-4。

分享于 2024-09-07

访问量 43

预览图片