Hướng dẫn defeating javascript obfuscation - đánh bại sự xáo trộn javascript

Câu chuyện về Retterner - một JavaScript deobfuscator nguồn mở mới.

TL;DR

Bây giờ đây là một câu chuyện tất cả về cách

Mã của tôi bị đảo lộn lộn ngược

Và tôi muốn dành một phút

Chỉ ngồi ngay đó

Tôi sẽ nói cho bạn biết làm thế nào tôi đến để xây dựng deobfuscator của tôi - Retterner.


Để làm cho một câu chuyện dài ngắn, tôi đã phát hành một công cụ deobfuscation của JavaScript có tên Restringer, cả dưới dạng mã và là một công cụ trực tuyến.

Để làm cho một câu chuyện ngắn dài - Tôi muốn chia sẻ động lực của mình để tạo ra công cụ, một số quyết định thiết kế và quá trình mà tôi đã thêm các khả năng mới vào nó - vì vậy bạn có thể tham gia vào cuộc vui! Đó là thời gian để đưa niềm vui vào Deobfunsca-- OK điều này có thể cần một số công việc.

Giải quyết, thay thế, lặp lại

Tên là Carter. MAITECARTER.

Một vài năm trước, tôi bắt đầu làm việc trong nhóm nghiên cứu Code Defender tại Perimeterx.

Một phần trong quy trình làm việc của tôi bao gồm điều tra các cuộc tấn công Magecart, hiện gần như đồng nghĩa với các cuộc tấn công chuỗi cung ứng của khách hàng và skimming. Các cuộc tấn công này thường có dạng các tệp JavaScript được tiêm trực tiếp vào các trang web bị xâm phạm hoặc được gửi thông qua một tập lệnh của bên thứ ba bị xâm phạm. Tôi thu thập các tập tin này và phân tích chúng. Tôi sẽ chia sẻ thêm về quá trình điều tra của tôi trong một bài đăng trong tương lai.

Có nhiều cách tiếp cận để phân tích mã, mặc dù về cơ bản chúng ta có thể phân loại chúng là:

  • Tĩnh - xem xét mã mà không cần chạy nó. Tìm các mẫu và theo luồng thực thi mã.
  • Dynamic - Chạy mã trong bất kỳ môi trường nghiên cứu nào phù hợp với bạn, dừng các giá trị và quan sát giữa các giá trị và xếp chồng trong bối cảnh.

Trừ khi tập lệnh thực sự đơn giản để hiểu hoặc bạn đã xử lý mã tương tự, bạn đã sử dụng kết hợp hai cách tiếp cận để có được bức tranh hoàn chỉnh về những gì tập lệnh làm và làm thế nào.

Ví dụ: khi phân tích một tập lệnh như skimmer chống VM được đề cập trong các bài đăng trên blog trước đây của tôi (điểm xa của một cuộc gặp gỡ tĩnh và tự động hóa skimmer deobfuscation), tôi sẽ sử dụng kết hợp đánh giá tĩnh về cấu trúc của mã và chỉ chạy các phần của Mã (trong Chrome DevTools, trên trang

   UDS('laessrrutnwmbnpoyhokgixzdoutrjtqccvcf').substr(0, 11);
8):

  1. Xác định chức năng giải nén / giải mã bằng cách quan sát cấu trúc mã. Làm thế nào điều này được xác định được đề cập trong các blog trước.

    Hàm

       UDS('laessrrutnwmbnpoyhokgixzdoutrjtqccvcf').substr(0, 11);
    9 phù hợp với mô tả:

        function UDS(b) {
            // ...
            return f.join('');
         }

  2. Xác định một thể hiện trong đó

       UDS('laessrrutnwmbnpoyhokgixzdoutrjtqccvcf').substr(0, 11);
    9 được gọi với một đối số chuỗi.

       UDS('laessrrutnwmbnpoyhokgixzdoutrjtqccvcf').substr(0, 11);

    Tôi đã thay thế biến

        var arr = ['log', 'hello', ' ', 'world'];
        console[arr[0]](arr[1] + arr[2] + arr[3]);
    1 bằng giá trị của nó,
        var arr = ['log', 'hello', ' ', 'world'];
        console[arr[0]](arr[1] + arr[2] + arr[3]);
    2, là 11.

  3. Giải nén / giải mã các chuỗi có liên quan động bằng cách chạy càng ít mã càng tốt. Tôi sao chép chức năng
       UDS('laessrrutnwmbnpoyhokgixzdoutrjtqccvcf').substr(0, 11);
    9 và thêm cuộc gọi vào nó sau đó để nhận giá trị:

Hướng dẫn defeating javascript obfuscation - đánh bại sự xáo trộn javascript

Bây giờ, tôi lấy giá trị đó và thay thế cuộc gọi chức năng bằng nó.

Điều đó không quá tệ, phải không? Làm điều này một hoặc hai lần là tốt và bảnh bao, nhưng nó có thể khá tốn thời gian và mệt mỏi khi phải đối mặt với ngày càng nhiều trường hợp của cùng một sự che giấu.

Theo lời của Raymond Hettinger - Hồi phải có một cách tốt hơn! Và tất nhiên là có! Nếu bạn có thể làm điều đó bằng tay, bạn gần như có thể viết một kịch bản để làm điều đó.There must be a better way”! And of course there is! If you can do it manually, you can almost definitely write a script to do it.

Cách tốt hơn (Take 1) - Một loạt các tài liệu tham khảo

Hãy để bắt đầu với một cái gì đó đơn giản và hoạt động theo cách của chúng tôi.

Một trong những loại obfuscation cơ bản hơn được sử dụng tích cực bởi những kẻ tấn công Magecart là phương pháp thay thế mảng. Nó được đặc trưng bởi một mảng được khởi tạo với các chuỗi và các tham chiếu trong suốt mã cho các chỉ số bên trong mảng.

Cái này trông thế nào?

    var arr = ['log', 'hello', ' ', 'world'];
    console[arr[0]](arr[1] + arr[2] + arr[3]);

Tôi sẽ theo ví dụ về Guy Bary, trong phần mềm độc hại phân tích Magecart - từ không đến bài đăng trên blog Hero:

  1. Tải mảng vào bộ nhớ.
  2. Tìm các tài liệu tham khảo mảng bằng Regex.
  3. Nhận giá trị của chúng bằng cách chạy chúng với Eval.
  4. Thay thế các tài liệu tham khảo bằng giá trị thực tế.

   const code = `var arr = ['log', 'hello', ' ', 'world'];
   console[arr[0]](arr[1] + arr[2] + arr[3]);`;

   eval(code.split('\n')[0]); // Load only the array into memory.
   let deob = code;
   const arrName = 'arr';
   const referencesRegex = new RegExp(`${arrName}\\[\\d+\\]`);
   let match = deob.match(referencesRegex);
   while (match) {
   const original = match[0];
   const replacementValue = JSON.stringify(eval(original));
   deob = deob.replace(original, replacementValue);
   match = deob.match(referencesRegex);
   }
   console.log(deob);

Mặc dù điều này hoạt động, nó đòi hỏi sự can thiệp thủ công - cung cấp tên của mảng. Tất nhiên, chúng tôi có thể và cũng nên lưu trữ kết quả hoặc sử dụng thay thế để cải thiện hiệu suất, nhưng điều đó sẽ làm mất đi sự đơn giản của ví dụ.

Lưu ý rằng tôi ban đầu chỉ chạy dòng đầu tiên của mã, sử dụng

    var arr = ['log', 'hello', ' ', 'world'];
    console[arr[0]](arr[1] + arr[2] + arr[3]);
4, để tránh bất kỳ tác dụng phụ nào có thể phát sinh do chạy toàn bộ mã, cụ thể là thông điệp ‘Hello World được in trong khi thực hiện. Mặc dù tôi quản lý để tránh điều đó ở đây, tập lệnh nguồn có thể đã đặt mảng khá nhiều ở bất cứ đâu, hoặc không hoàn toàn các dòng mới (như thường thấy với mã được tiêm). Tác dụng phụ cũng có thể tồi tệ hơn nhiều so với chỉ là một thông điệp lành tính.

Tăng độ phức tạp

Hãy để trộn nó lên một chút. Hãy xem xét một sự phát triển của kỹ thuật obfuscation này, phương pháp thay thế mảng tăng cường. Nó có các đặc điểm tương tự như đối tác chưa được cung cấp của nó, nhưng với một biểu thức chức năng được gọi thêm ngay lập tức (hoặc iife) với một tham chiếu đến mảng là một trong những đối số của nó, thay đổi (tăng) các giá trị trong mảng theo một cách nào đó.

Cái này trông thế nào?

    var arr = ['world', ' ', 'hello', 'log'];
    (function(a, b) {
      a = a.reverse();
    })(arr, 7);
    console[arr[0]](arr[1] + arr[2] + arr[3]);

Sau khi

    var arr = ['log', 'hello', ' ', 'world'];
    console[arr[0]](arr[1] + arr[2] + arr[3]);
5 được khai báo, iife đảo ngược thứ tự của mình để mỗi chỉ mục bây giờ trỏ đến giá trị chính xác. Nếu chúng tôi cố gắng chạy tập lệnh deobfuscation trên đó, chúng tôi sẽ nhận được mã bị hỏng này:

    console["world"](" " + "hello" + "log");

Giải pháp dễ dàng là chạy tập lệnh là để có được ngữ cảnh vừa phải, và sau đó tìm kiếm và thay thế. Để làm điều đó, chúng tôi loại bỏ sự phân tách dòng từ

    var arr = ['log', 'hello', ' ', 'world'];
    console[arr[0]](arr[1] + arr[2] + arr[3]);
6 và bị bỏ lại với
    var arr = ['log', 'hello', ' ', 'world'];
    console[arr[0]](arr[1] + arr[2] + arr[3]);
7.

Bẫy mà kỳ nghỉ xuân (thực hiện)

Blog Guy sườn liên quan đến một loại obfuscation khác nhau mặc dù - thay thế chức năng mảng tăng cường (đã bỏ qua phiên bản chưa được điều khiển). Loại obfuscation này cũng chứa một mảng có chuỗi và iife tăng cường nó, nhưng thay vì được tham chiếu trực tiếp trong suốt mã, một cuộc gọi hàm được sử dụng với các tham số chuỗi/số. Hàm này lấy giá trị từ mảng tương ứng với các tham số được cung cấp.

Cái này trông thế nào?

    var arr = ['d29ybGQ=', 'IA==', 'aGVsbG8=', 'bG9n'];
    (function(a, b) {a = a.reverse();})(arr, 7);
    function dec(a, b) {
      debugger;
      return atob(arr[b - 57]);
    }
    console[dec('vxs', 57)](dec('ap3', 58) + dec('3j;', 59) + dec('aaa', 60));

Ví dụ đồ chơi này cho thấy giá trị gia tăng của hàm và lý do tại sao nó được ưu tiên hơn các thay thế mảng đơn giản hơn: bạn có thể làm bất cứ điều gì bạn muốn bên trong hàm, cụ thể là khoảng cách giá trị tham số từ chỉ mục mảng thực tế để tránh tìm kiếm đơn giản và thay thế chiến thuật và chiến thuật và Đặt bẫy cho các nhà điều tra bắt giữ và chọc vào đôi mắt tò mò. Cái bẫy mà tôi đã bao gồm thực thi treo (nhưng không phá vỡ nó) nếu các devtool được mở hoặc nếu nó chạy bên trong IDE với khả năng gỡ lỗi. Do kịch bản rất ngắn chỉ có 4 cuộc gọi đến chức năng

    var arr = ['log', 'hello', ' ', 'world'];
    console[arr[0]](arr[1] + arr[2] + arr[3]);
8, nó không thực sự làm nhiều hơn là khó chịu, nhưng hãy tưởng tượng một kịch bản với hàng trăm cuộc gọi - nó đã trở nên khá cũ khá nhanh. Tất nhiên bạn có thể chống lại cái bẫy đơn giản này theo một số cách như vô hiệu hóa các điểm dừng, loại bỏ dòng đó theo cách thủ công, v.v.

Dưới đây là một vài ví dụ về các điều kiện mà bẫy có thể gây ra và phá vỡ thực thi:

  • Các devtools được mở.
  • Kịch bản được làm đẹp.
  • Có các chỉ số của môi trường NodeJS.
  • Có các chỉ số của một môi trường VM.

Một thủ thuật hữu ích khác để ném điều tra về kịch bản bị xáo trộn này là bằng cách cung cấp chức năng

    var arr = ['log', 'hello', ' ', 'world'];
    console[arr[0]](arr[1] + arr[2] + arr[3]);
8 bằng một đối số vứt bỏ, được sử dụng tất cả (như đối số
   const code = `var arr = ['log', 'hello', ' ', 'world'];
   console[arr[0]](arr[1] + arr[2] + arr[3]);`;

   eval(code.split('\n')[0]); // Load only the array into memory.
   let deob = code;
   const arrName = 'arr';
   const referencesRegex = new RegExp(`${arrName}\\[\\d+\\]`);
   let match = deob.match(referencesRegex);
   while (match) {
   const original = match[0];
   const replacementValue = JSON.stringify(eval(original));
   deob = deob.replace(original, replacementValue);
   match = deob.match(referencesRegex);
   }
   console.log(deob);
0 trong ví dụ). Tất cả các cuộc gọi chứa khác nhau và một tham số đầu tiên ngẫu nhiên, giúp làm phức tạp các mẫu tìm kiếm và thay thế. Thêm sự xúc phạm vào thương tích, bạn thậm chí có thể quá tải các cuộc gọi chức năng với các tham số không cần thiết bổ sung để thay đổi hoàn toàn chữ ký cuộc gọi.

Vì những người thích chơi với mã trong khi đọc có thể nhận thấy, ví dụ này vẫn có thể bị phá hủy bằng kỹ thuật tìm kiếm và thay thế đơn giản của chúng tôi, như vậy:

    const code = `var arr = ['d29ybGQ=', 'IA==', 'aGVsbG8=', 'bG9n'];
    (function(a, b) {a = a.reverse();})(arr, 7);
    function dec(a, b) {
      debugger;
      return atob(arr[b - 57]);
    }
    console[dec('vxs', 57)](dec('ap3', 58) + dec('3j;', 59) + dec('aaa', 60));`;
    
    eval(code);
    let deob = code;
    const funcName = 'dec';
    const referencesRegex = new RegExp(`${funcName}\\('[^)]+?\\)`);
    let match = deob.match(referencesRegex);
    while (match) {
    	const original = match[0];
    	const replacementValue = JSON.stringify(eval(original));
    	deob = deob.replace(original, replacementValue);
    	match = deob.match(referencesRegex);
    }
    console.log(deob);

Vì vậy, chỉ để làm phức tạp các vấn đề (vì tại sao không?), Tôi đã di chuyển hàm

    var arr = ['log', 'hello', ' ', 'world'];
    console[arr[0]](arr[1] + arr[2] + arr[3]);
8 sang cuối mã và ghi đè lên chức năng mảng và
    var arr = ['log', 'hello', ' ', 'world'];
    console[arr[0]](arr[1] + arr[2] + arr[3]);
8 sau khi thực thi:

    var arr = ['d29ybGQ=', 'IA==', 'aGVsbG8=', 'bG9n'];
    (function(a, b) {a = a.reverse();})(arr, 7);
    console[dec('vxs', 57)](dec('ap3', 58) + dec('3j;', 59) + dec('aaa', 60));
    arr.length = 0;
    dec = () => {};
    function dec(a, b) {
    	debugger;
    	return atob(arr[b - 57]);
    }

Tôi có thể viết một tìm kiếm phức tạp hơn và thay thế tập lệnh chỉ trích xuất mảng và chức năng và sau đó tìm ra các chuỗi, nhưng đó không thể mở rộng. Thế bây giờ thì thế nào?

Cách tốt hơn (Take 2) - Tổng quát hóa giải pháp: Ra khỏi tập lệnh

Hãy để tôi nhảy thẳng vào đầu sâu của hồ bơi bằng cách đưa bạn đi qua việc xây dựng một deobfuscator cho loại hình che giấu này. Nó sẽ là một giải pháp cơ bản và tôi sẽ đề cập đến những thiếu sót của việc thực hiện cụ thể vào cuối phần này, vì vậy hãy viết ra bất cứ điều gì bạn nghĩ bạn sẽ làm khác đi và cho tôi biết nếu tôi bỏ lỡ bất cứ điều gì .

Chỉ cần một lời nhắc nhở nhanh chóng - Tôi sẽ sử dụng gói FLAST để làm phẳng AST để giúp dễ dàng tìm thấy các cấu trúc mã đang đề cập đến nhau và thay thế chúng bằng các chuỗi deobfuscate.

Hãy để bắt đầu với mảng. Trong kịch bản ngắn này, chúng tôi đã phá hoại, chỉ có một mảng, vì vậy, nó đơn giản, nhưng hãy để giả vờ rằng có thể có nhiều hơn một. Làm thế nào chúng ta tìm thấy mảng cụ thể mà chúng ta đang tìm kiếm? Trước tiên, chúng tôi mô tả nó và sau đó viết nó bằng mã.

Chúng tôi đang tìm kiếm:

  • Một trình khai báo biến được khởi tạo với một biểu thức mảng.
  • Biểu thức mảng không trống và chỉ chứa các chuỗi (nghĩa đen).
  • Mảng được tham chiếu trong một hàm (nghĩa là không phải phạm vi toàn cầu).

Sử dụng FLAST, chúng tôi có thể xem xét các tài liệu tham khảo định danh và phạm vi của chúng:

    const relevantArrays = ast.filter(n =>
      n.type === 'VariableDeclarator' &&
      n?.init?.type === 'ArrayExpression' &&
      n.init.elements.length &&   // Is not empty.
      // Contains only literals.
      !n.init.elements.filter(e => e.type !== 'Literal').length &&
      // Used in another scope other than global.
      n.id?.references?.filter(r => r.scope.scopeId > 0).length);

Điều này là quá chung chung. Chúng ta có thể kiểm tra mức độ này mô tả cấu trúc mà chúng ta đang tìm kiếm bằng cách nối thêm mã bị lỗi vào phiên bản không bị che giấu của một tập lệnh lớn (như tập lệnh jQuery tùy ý này) và đếm số mảng khớp với mô tả.

Trong trường hợp này, ba mảng phù hợp:

   UDS('laessrrutnwmbnpoyhokgixzdoutrjtqccvcf').substr(0, 11);
0

Vì vậy, chúng tôi là chung chung, nhưng không quá chung chung. Chúng ta có thể sử dụng mối quan hệ giữa mảng và hàm

    var arr = ['log', 'hello', ' ', 'world'];
    console[arr[0]](arr[1] + arr[2] + arr[3]);
8 để trau dồi đúng. Những gì chúng tôi đang tìm kiếm là:

  • Một khai báo chức năng với chính xác một tham chiếu đến mảng có liên quan.
  • Có ít nhất là nhiều tài liệu tham khảo cho nó như có các mục trong mảng.

Trong mã, mô tả này trông như thế này:

   UDS('laessrrutnwmbnpoyhokgixzdoutrjtqccvcf').substr(0, 11);
1

Vì vậy, đối với mỗi mảng chúng tôi tìm thấy, chúng tôi có thể tìm kiếm một hàm giải mã phù hợp với mô tả cho đến khi chúng tôi tìm thấy một trận đấu.

Bây giờ, tất cả những gì còn lại là tìm thấy chức năng tăng cường bằng cách tìm kiếm biểu thức cuộc gọi trong đó callee là một biểu thức chức năng (nghĩa là một hàm được khai báo tại chỗ, thay vì định danh hoặc một đối tượng) và mảng của chúng tôi là một trong các đối số của nó. Chúng tôi thậm chí không cần phải tìm kiếm toàn bộ mã cho mô tả đó, chỉ các tham chiếu đến mảng của chúng tôi:

   UDS('laessrrutnwmbnpoyhokgixzdoutrjtqccvcf').substr(0, 11);
2

Đặt tất cả lại với nhau, tôi có ý nghĩa hơn khi đặt từng mô tả vào chức năng riêng của nó và tất cả các chức năng trong một lớp duy nhất và chia sẻ AST được phân tích cú pháp thay vì truyền nó mỗi lần.

   UDS('laessrrutnwmbnpoyhokgixzdoutrjtqccvcf').substr(0, 11);
3

Bạn có thể thay thế mã nội tuyến bằng mã jQuery được tiêm và kiểm tra nó.

Tất cả trong tất cả, đó là một mã hóa phô mai. Giải pháp này vẫn còn thiếu, vì nó không bao gồm tất cả các kịch bản có thể. Ở đây, một danh sách một phần các kịch bản không được bảo hiểm:

  • Có thể có các mảng khác trong mã phù hợp với cùng một mô tả.
  • Có thể có nhiều hơn một chức năng tăng cường.
  • Hàm giải mã có thể sử dụng API trình duyệt (như kiểm tra vị trí.HREF), nơi sẽ hoạt động trong môi trường NodeJS của chúng tôi ra khỏi hộp.
  • Có thể có nhiều hơn một phần bị che giấu trong mã.
  • Mảng có thể được xác định trong một dòng và được điền vào một dòng khác.

Những gì tôi hy vọng tôi đã quản lý để truyền đạt là làm thế nào để sử dụng FLAST để tạo ra mã ngắn gọn và hiệu quả nói trong các cấu trúc và mối quan hệ nút.

Cuộc hành trình cho đến nay

Những gì tôi đã chia sẻ cho đến bây giờ là cách hành trình deobfuscation của tôi bắt đầu. Nó trông như thế nào bây giờ?

Bạn có thể kiểm tra nó cho chính mình!

Tôi đã thu hẹp một số mô tả obfuscation trong máy dò obfuscation của tôi và đối với một số loại che giấu đó, Restringer cung cấp các bộ xử lý tiền cụ thể để loại bỏ bẫy hoặc đơn giản hóa mã trước khi chuyển sang các nỗ lực khử trùng chung hơn.

Ví dụ, Caesar+ obfuscation, mà tôi đã phá vỡ trong DEOBFuscating Caesar+, có một lớp bên ngoài hoạt động như một loại Packer kết hợp chuỗi mã được đóng gói bằng các phần tử HTML. Phương pháp này được sử dụng để ngăn chặn việc chạy mã bên ngoài môi trường trình duyệt. Để tự động hóa việc giải nén trong môi trường NodeJS, môi trường trình duyệt mô phỏng được sử dụng thông qua JSdom.

Logic deobfuscation chính được trình bày trong phương pháp DEOBFuscate chính:

   UDS('laessrrutnwmbnpoyhokgixzdoutrjtqccvcf').substr(0, 11);
4

Điều đầu tiên là xác định loại obfuscation, chạy các tiền xử lý thích hợp và thiết lập bất kỳ bộ xử lý hậu nào có liên quan. Sau đó, các phương pháp deobfuscation chính chạy trong một vòng lặp cho đến khi chúng không còn hiệu quả. Các bộ xử lý sau đó được áp dụng cho mã hiện đang bị khử trùng.

Cuối cùng, mã được chuẩn hóa (ví dụ: biến khung [‘ký hiệu] thành dot.notation) và bị tước bất kỳ mã chết nào nếu tùy chọn đó được chọn.

Các điều khoản an toàn và không an toàn là các thuật ngữ cho biết liệu một phương thức đang sử dụng Eval để giải quyết đầu ra của mã hay không. Sử dụng Eval được coi là không an toàn, ngay cả khi chạy nó trong môi trường hộp cát như VM2.

   UDS('laessrrutnwmbnpoyhokgixzdoutrjtqccvcf').substr(0, 11);
5

Cách thức vòng lặp này được xây dựng, các phương pháp an toàn chạy cho đến khi chúng tự cạn kiệt, và sau đó các phương thức không an toàn chạy một lần. Chu kỳ sau đó được lặp lại. Điều này được thực hiện để tránh chạy các eval quá háo hức trên mã có thể bị khử trùng bằng các phương pháp khác, tạo ra kết quả chính xác hơn.

Phương pháp Runloop có một loạt các phương pháp deobfuscation và tốt, chạy chúng trong một vòng lặp (ngạc nhiên, phải không?). Nó có một ảnh chụp nhanh của mã của mã trước khi chạy và so sánh nó sau mỗi vòng lặp. Nếu ảnh chụp phù hợp với trạng thái hiện tại của mã, điều đó có nghĩa là không có phương thức nào thực hiện bất kỳ thay đổi nào và vòng lặp có thể kết thúc. Một số chu kỳ tối đa cũng được quan sát để tránh một kịch bản vòng vô tận. Ở đây, một phiên bản làm sạch của phương pháp này:

   UDS('laessrrutnwmbnpoyhokgixzdoutrjtqccvcf').substr(0, 11);
6

Mô tả này sẽ hoàn thành mà không có danh sách các phương pháp an toàn và không an toàn hiện có:

   UDS('laessrrutnwmbnpoyhokgixzdoutrjtqccvcf').substr(0, 11);
7

Tôi rất thích đi sâu vào chi tiết trong tương lai về cách tôi thực hiện cơ chế EVAL, cách thức hoạt động của bộ nhớ đệm, những lựa chọn tôi thực hiện về những gì có thể và không thể bị phá hoại tại bất kỳ thời điểm nào, hoặc cách tôi thu thập bối cảnh.

Có rất nhiều tài liệu và ý kiến ​​trong repo.

Chúng ta đi đâu? Bây giờ chúng ta đi đâu?

Dự án này đã ở với tôi kể từ những ngày bắt đầu của tôi tại Perimeterx, và tôi thực sự biết ơn vì đã được trao cơ hội và thời gian để làm việc trong một dự án dài hạn thú vị như vậy.

Retterner đã được mã hóa, tái cấu trúc, viết lại, băm nhỏ, dán lại với nhau, dịch, đổi tên, bị mất, tìm thấy, chịu sự điều tra công khai, hỏi, bị mất một lần nữa và cuối cùng được phê duyệt để được phát hành dưới dạng nguồn mở.Thật là một hành trình!

Resterner là tuyệt vời, nhưng nó khác xa với sự hoàn hảo hoặc đầy đủ.Tôi sẽ giữ nó, và tôi hy vọng nó sẽ hữu ích cho các nhà nghiên cứu và những người đam mê obfuscation khác, tất cả những người đều được chào đón để sử dụng và đóng góp khi họ thấy phù hợp.

Cảm ơn vì đã đọc!Tôi hy vọng tất cả chúng ta lại gặp lại nhau trong Restringer 2: Việc tìm kiếm nhiều sự che giấu hơn và có thể Schwartz sẽ ở bên bạn!