Ngoài mã bản địa: Python, JVM, .NET và WebAssembly Bytecode
Type: Build
Languages: Python, TypeScript, Java, C#, WebAssembly text
Prerequisites: Phase 13 · 32 (ELF from scratch), 33 (disassembly and control flow)
Time: ~150 minutes
Mục tiêu học tập
- Đọc 16byte
.pyctiêu đề từ PEP 552 và không tham khảo đối tượng mã mà không cómarshalmodule, cho các tệp được viết bởi bốn phiên bản phiên dịch. - Phân tích một tệp lớp JVM từ JVMS Chương 4: nhóm liên tục, trường, phương pháp, các
Codethuộc tính, và các bảng màjavac -gthêm vào. - Phong cách các mã opcode JVM trong
Ledger.classvà chứng minh danh sách chống lạijavap -ctrên bốn cột của cùng một nguồn. - Đọc một phần mô-đun WebAssembly theo phần, giải mã từng cơ quan chức năng, và chứng minh kết quả chống lại
wasm-objdump -d- Tôi không biết. - Đi bộ các siêu dữ liệu ECMA-335 của một bộ sưu tập .NET từ thư mục dữ liệu PE đến
MethodDefhàng và CIL của mỗi phương pháp, kiểm tra với thư viện đọc mà ILSpy sử dụng. - Đo, cho một chức năng trong năm thùng, tên, loại và cấu trúc điều khiển của mỗi định dạng giữ và mất.
Vấn đề
Anh xây dựngledger.pyvà tìm thấyledger.cpython-314.pyctrong __pycache__Ông hỏi một nhân viên có chứa gì trong hồ sơ, nhân viên đã chạy một máy phân tích.Unsupported opcodevà một dòng nói# WARNING: Decompyle incompleteCảnh sát kết luận rằng hồ sơ đã bị mờ.
Dữ liệu không bị mờ.2b 0e, từ 3627. Đó là số CPython 3.14 viết, và các bảng mã opcode của decompiler dừng ở 3.13. Công cụ đã lỗi thời.
Bài học 33 cho thấy mã bản địa mất đi những gì: tên tham số, loại và các switchNó trở thành một bàn nhảy. Các container bytecode mất ít hơn, và mỗi container mất một thứ gì đó khác nhau. Một pyc giữ tất cả các tên biến và không có loại. Một tập tin lớp lưu trữ các loại và lưu trữ tên địa phương chỉ khi bạn hỏi. Một mô-đun Wasm giữ cấu trúc điều khiển và bỏ tên trừ khi một phần tùy chỉnh mang chúng. Một tập hợp .NET giữ tên tham số trong một bảng và để lại tên địa phương cho một tệp riêng biệt. Bạn không thể đánh giá kết quả của một máy phân tích trên bất kỳ chúng cho đến khi bạn biết những ghi chú mà container bắt đầu với.
Bài học này xây dựng bốn bộ đọc trong thư viện tiêu chuẩn Python. Mỗi một bộ đọc container từ byte đầu tiên của nó, liệt kê một chức năng, và được kiểm tra so với công cụ của định dạng riêng trên các thiết bị bạn đã xây dựng. Phần cuối cùng đặt gradetừ ledgerbên cạnh nhau trong tất cả năm thùng.
PYC Header là bốn từ
PEP 552 định nghĩa tiêu đề là bốn từ 32 bit. thứ nhất là số phép thuật. thứ hai là một bit trường. Bit 0 set có nghĩa là tập tin dựa trên hash. Bit 1 set có nghĩa là check_source: người giải thích hash nguồn khi nhập và so sánh. với bit 0 rõ ràng, từ 3 và 4 là mtime nguồn và kích thước nguồn. với bit 0 set, chúng chứa một SipHash 64-bit của nguồn.
Số ma thuật sống trong Include/internal/pycore_magic_number.hQuy tắc của nó từ 3.11 là Python 3.n bắt đầu ở 2900 + 50n, và mỗi thay đổi bytecode trong một chu kỳ phát hành thêm một.\r\nPython 3.15.0 được phát hành vào năm 2026-10-09 và không được cài đặt trên máy tính đã xây dựng các thiết bị này, vì vậy số của nó đến từ tệp tiêu đề và không từ tệp ở đây.
Các thiết bị có chứa hai tập tin pyc cho mỗi phiên dịch. compile_pyc.pyviết chúng với py_compile.compile(..., dfile="ledger.py")để tên tập tin được lưu trữ là ledger.py16 byte đầu tiên của các tập tin 3.14:
checked-hash 2b 0e 0d 0a 03 00 00 00 c7 16 c7 fc c5 1d 81 01
timestamp 2b 0e 0d 0a 00 00 00 00 8d 60 cf 6a f1 01 00 00Đọc tiêu đề dấu thời gian từ từ. 03 00 00 00hoặc 00 00 00 00là từ cờ.8d 60 cf 6alà 0x6acf608d = 1791975565, mtime của ledger.pykhi tập tin được xây dựng. f1 01 00 00là 497, kích thước nguồn. tệp hash được kiểm tra có cờ 3 và tám byte hash thay vào đó. bốn người giải thích đồng ý về mọi thứ trong tiêu đề ngoại trừ từ phép thuật:
| Interpreter | Magic bytes | Word | File bytes | grade.co_code bytes | grade instructions |
|---|---|---|---|---|---|
| 3.11.14 | a7 0d 0d 0a | 3495 | 1054 | 80 | 31 |
| 3.12.12 | cb 0d 0d 0a | 3531 | 937 | 62 | 26 |
| 3.13.12 | f3 0d 0d 0a | 3571 | 943 | 70 | 26 |
| 3.14.6 | 2b 0e 0d 0a | 3627 | 965 | 96 | 35 |
read_pyc_header()trong code/main.pytrả lại các trường này và đặt tên cho phiên bản từ từ. importlib.util.MAGIC_NUMBERcho cùng bốn byte cho người dịch đang chạy. python3 code/main.py comparebiên soạn nguồn với mỗi python3.11đếnpython3.15được tìm thấy trên PATHĐối với mỗi người nó in tiêu đề người đọc của chúng tôi thấy bên cạnh các sự thật mà người diễn giải báo cáo về chính mình.
Marshal viết Code Object Field theo Field
Byte 16 của mỗi thiết bị là e3- Đó là 0x80 | ord('c'): mã kiểu marshal cho một đối tượng mã với FLAG_REFset, nói với người đọc để nhớ đối tượng cho sau rCác tài liệu Marshall nói rằng định dạng không được ghi chép một cách cố ý và các đối tượng mã không tương thích giữa các phiên bản. Cả hai tuyên bố đều đúng, và định dạng vẫn đủ nhỏ để đọc. Python/marshal.cliệt kê các mã loại: NKhông có ai cả.imột int 32 bit, sbyte, zvà ZDòng ASCII ngắn, (và )Túp, cmột đối tượng mã, rContainer đặt chỗ tham chiếu trước con cái của họ, để người đọc cũng làm như vậy.
w_complex_object()viết một object code theo thứ tự cố định. cho 3.11 đến 3.15 nó là argcount- posonlyargcount- kwonlyargcount- stacksize- flagsnhư 32 bit inch, sau đó code- consts- names- localsplusnames- localspluskinds- filename- name- qualnamenhư các đối tượng, thì firstlineno, sau đó linetablevà exceptiontable. Khóa mã mã module đối tượng của tập tin 3.14 bằng tay từ byte 17:
00 00 00 00 argcount 0
00 00 00 00 posonlyargcount 0
00 00 00 00 kwonlyargcount 0
01 00 00 00 stacksize 1
00 00 00 00 flags 0
f3 22 00 00 00 type 's' with FLAG_REF, 34 bytes of co_code follow
80 00 52 06 74 00 52 01 17 00 74 01 ...Các byte mã là hai cho mỗi lệnh: 80 00là RESUME 0- 52 06là LOAD_CONST 6- 74 00là STORE_NAME 0, và17 00là MAKE_FUNCTION. Dấu hiệu 3.14 trong các thiết bị in cùng năm tuyên bố cấp mô-đun.codeđến các định vị, giữ bốn vật có mã đốn, và sau đó chúng names('LABELS', 'percent_change', 'grade', 'total', 'grade_label')- Tôi không biết.
Unmarshallertrong code/main.pyđọc bố cục này và cũ hơn mà 3.9 và 3.10 sử dụng, nơi nlocals- varnames- freevars, vàcellvarslà các cánh đồng riêng biệt.CodeObjectvới các tên thuộc tính giống nhau đối tượng thực có. varnamesbộ lọc localsplusnamesbởi CO_FAST_LOCALMột chút localspluskinds- Vì gradetrong mỗi 4 tập tin nó trả lại ('score', 'bucket')- firstlineno10, và một không trống linetable- Thử nghiệmtest_unmarshaller_agrees_with_the_running_interpreterso sánh mọi lĩnh vực với marshal.loads()3 tập tin khác có thể đọc được bằng mã của chúng tôi và không thể đọc được bởi chúng tôi.marshaldưới sự giải thích của người diễn giải này.
Bốn người diễn giải, bốn Bytecode cho lớp
Các tài liệu này có một cảnh báo trên mỗi phiên bản: bytecode là một chi tiết thực hiện của CPython và không có đảm bảo rằng nó sẽ vẫn giống nhau.python3.1N -m dis ledger.pyđược bắt giữ cho mỗi người phiên dịch.
Trong 3.11 gradebắt đầu như thế này:
11 2 LOAD_FAST 0 (score)
4 LOAD_CONST 1 (10)
6 BINARY_OP 2 (//)
10 STORE_FAST 1 (bucket)
12 12 LOAD_FAST 1 (bucket)
14 LOAD_CONST 2 (9)
16 COMPARE_OP 5 (>=)
22 POP_JUMP_FORWARD_IF_FALSE 2 (to 28)BINARY_OPnằm ở ofset 6 và hướng dẫn tiếp theo ở 10. Hai byte giữa chúng là một mục cache trong đường từ PEP 659, phiên dịch viên thích ứng chuyên ngành. COMPARE_OP16 được tiếp theo bởi 22, vì vậy nó sở hữu hai mục cache.show_caches=True3.12 thay thế LOAD_CONST+RETURN_VALUEvới RETURN_CONST, đổi tên nhảy thành POP_JUMP_IF_FALSE, và giảm bớt gradetừ 80 đến 62 byte. 3.13 in nhãn L1đếnL4thay vì bù đắp và cho CLI -Ccho kho lưu trữ và -Ocho các khoản bù đắp. 3.14 loại bỏ RETURN_CONSTmột lần nữa, thêm LOAD_SMALL_INTvà LOAD_FAST_BORROW, và các mục nhập NOT_TAKENsau mỗi nhảy có điều kiện:
12 20 LOAD_FAST_BORROW 1 (bucket)
22 LOAD_SMALL_INT 9
24 COMPARE_OP 188 (bool(>=))
26 CACHE 0 (counter: 0)
28 POP_JUMP_IF_FALSE 3 (to L1)
30 CACHE 0 (counter: 0)
32 NOT_TAKEN-C -Odanh sách cho thấy 13CACHEĐăng nhập bên tronggrade: 5 sauBINARY_OP, một sau mỗi ngườiCOMPARE_OP, một sau mỗi ngườiPOP_JUMP_IF_FALSE- Cỗ máy điều chỉnh cho thấy bộ nhớ cache là gì.dis.dis(grade, adaptive=True)dấu vân tayRESUME_CHECKvàCOMPARE_OP_INTnơi danh sách đơn giản đã cóRESUMEvàCOMPARE_OP, vàtotalcho thấyFOR_ITER_LIST-BINARY_OP_ADD_INT, vàJUMP_BACKWARD_NO_JITCác hình thức chuyên môn sống trong cùng mộtco_codemột pyc không bao giờ chứa chúng, bởi vì chuyên môn hóa xảy ra trong bộ nhớ.
Đây là lý do tại sao các máy giải trình có giới hạn phiên bản. uncompyle6 3.9.3 nói rằng nó chấp nhận bytecode từ Python 1.0 đến 3.8. decompyle3 3.9.3 đọc 3.7 và 3.8. xdis 6.3.0, trình giải trình phiên bản chéo đằng sau cả hai, đọc lên đến 3.13. pycdc có các bảng opcode lên đến python_3_13.cpptrong file build của nó, và trên một bản in opcode không rõ Unsupported opcodePyLingual tuyên bố mọi phiên bản được phát hành kể từ 3.6 và sử dụng các phiên dịch viên từ 3.8 đến 3.15 để biên soạn lại và kiểm tra đầu ra của riêng nó. Đối với một tệp 3.14, ba tệp đầu tiên từ chối hoặc thất bại, và chỉ hai tệp cuối cùng áp dụng.
Các tập tin lớp giữ một từ điển riêng
JVMS Java SE 25, phần 4.1, đưa ra bố cục như một cấu trúc: magic- minor_version- major_version- constant_pool_count, hồ bơi liên tục,access_flags- this_class- super_class, giao diện, trường, phương pháp, thuộc tính.0xCAFEBABEMột máy ảo Java SE 25 chấp nhận các phiên bản lớn 45 đến 69, và một phiên bản nhỏ của 65535 đánh dấu một tệp tính năng xem trước.Ledger-g.classlà ca fe ba be 00 00 00 41 00 5e: chính 65 là Java 21, và 0x5e= 94 là số lượng hồ bơi liên tục. Ledger-jdk25.class, được xây dựng bởi javac 25.0.2 từ cùng một nguồn, bắt đầu ca fe ba be 00 00 00 45Tầm 69
Các hồ sơ lớp lưu trữ từ điển của riêng mình.#13trong -gxây dựng là một Methodref- Đúng vậy.#8và tên và loại #14Theo các chỉ số cho raLedger.grade:(I)I, đó chính xác là gì javap -vdấu vân tay sau //- Tác giả(I)Ilà một inttham số và một intKết quả.([I)Itotalnói tham số là một intarray. Mỗi phương pháp, trường và lớp mà mã chạm có một mục như vậy, và người xác minh trong phần 4.10 từ chối một tệp có hướng dẫn không đồng ý với chúng.ClassFilelớp trong code/main.pyđọc tất cả 17 thẻ liên tục từ bảng 4.4 và xử lý Longhoặc DoubleNó tăng lên trên một thẻ không rõ và trên byte sau thuộc tính cuối cùng.
Các thuộc tính đính kèm với lớp, mỗi trường, mỗi phương pháp, và CodeJVMS nói rằng máy ảo phải bỏ qua các thuộc tính mà nó không biết, đó là lý do tại sao các bảng gỡ lỗi có thể được thêm hoặc bỏ ra mà không thay đổi một bộ phận phương pháp.-gtạo ra tất cả thông tin gỡ lỗi bao gồm các biến địa phương, rằng mặc định tạo ra số đường và tệp nguồn chỉ, và rằng -g:none3 tạo ra một số lượng:
| Build | Bytes | Constant pool count | LineNumberTable | LocalVariableTable | SourceFile | grade code bytes |
|---|---|---|---|---|---|---|
javac -g | 1832 | 94 | 7 methods | 6 methods | yes | 50 |
javac | 1581 | 82 | 7 methods | none | yes | 50 |
javac -g:none | 1404 | 79 | none | none | no | 50 |
50 byte mã của gradelà giống nhau trong cả ba tập tin, và kiểm tra kiểm tra nó.-gthêm cho gradelà một LocalVariableTablehàng: bắt đầu 0, chiều dài 50, khe 0, tên score, mô tả IKhông có nó, máy giải trình sẽ thấy khe 0 kiểuintVà phải phát minh ra một cái tên.StackMapTableở trong mọi cấu trúc bởi vì người xác minh cần nó, và BootstrapMethodsthuộc tính vẫn còn vì mainkết nối dây thông qua invokedynamic- Tôi không biết.
Tự động giải mã bàn
Chương 6 của JVMS định nghĩa mỗi hướng dẫn là một byte opcode và các operand mà opcode đặt tên. Branch offsets là các giá trị ký 16 bit liên quan đến địa chỉ của branch opcode, khác với x86-64, nơi sự di chuyển tương quan với hướng dẫn tiếp theo. tableswitchvà lookupswitchbắt đầu với 0 đến 3 byte để các operand 32 bit của chúng bắt đầu từ địa chỉ là nhiều của bốn.
Các byte của gradetừ máy tính 4 trong Ledger-g.class- Có thể là:
aa tableswitch at pc 4
00 00 00 padding to pc 8
00 00 00 2c default: 4 + 44 = 48
00 00 00 06 low 6
00 00 00 0a high 10
00 00 00 2a case 6: 4 + 42 = 46
00 00 00 28 case 7: 4 + 40 = 44
00 00 00 26 case 8: 4 + 38 = 42
00 00 00 24 case 9: 4 + 36 = 40
00 00 00 24 case 10: 4 + 36 = 4036 byte, một hướng dẫn, và toàn bộ switch (score / 10)với trường hợp chung của nó cho 9 và 10.switchtrong bài học 33 cần kiểm tra giới hạn, một lea, một movslq, một addĐây là mục tiêu trong hướng dẫn, và cũng là thực tế là họ thuộc về nhau.
disassemble_jvm()bao gồm tất cả 202 mã opcode được xác định với chiều rộng của chúng, tuân thủ quy tắc đệm, và làm cho các operands theo cách javapcompare_with_javap()Phân tích những kẻ bị bắt javap -c -pvăn bản, bao gồm cả nhiều dòng tableswitchCác bộ phận này được kiểm tra và kiểm tra các hướng dẫn của các bộ phận này.test_branch_offsets_are_relative_to_the_branch_instructionkiểm traifnetại pc 1 hạ cánh trên 6, if_icmpgeở 13 khi hạ cánh trên 33, và gotoở 30 hạ cánh trở lại trên 10.
radar2 6.2.4, được xây dựng từ nguồn cho các bài học này, đọc cùng một tập tin. rabin2 -Ibáo cáo arch java- lang java 21- linenum true, vàlsyms true, là việc đọc của phiên bản và hai bảng debug. pd 14 @ sym.Ledger.gradein tableswitchNó cũng có dấu ấn.Unable to parse code attribute: 2trong tập tin này, đó là một hạn chế của công cụ tự đặt tên và một lý do để giữ javapnhư lời tiên tri. Các trình phân tích cho định dạng này đã trưởng thành khi so sánh với Python. CFR 0.152 được MIT cấp phép. Procyon 0.6.0 và Vineflower 1.12.0 là Apache-2.0, và Vineflower là một chiếc cành Fernflower với hỗ trợ Java 21. Fernflower sống trong kho lưu trữ của cộng đồng IntelliJ. JADX 1.5.6 bao gồm định dạng Android DEX, và JD-GUI đã được phát hành lần cuối vào năm 2019. Harrand, Soto-Valero, Monperrus và Baudry đo tám trong số đó vào năm 2020. Kết quả kết quả đúng cách tổng hợp tốt nhất cho 84 phần trăm các lớp và tương đương về ngữ nghĩa cho 78 phần trăm. Meta-decompiler của họ đã phục hồi 37,6% các lớp mà không có công cụ nào xử lý. Các loại, tên và các đống xác minh làm cho định dạng có thể được giải thể. Chúng không làm cho bất kỳ decompiler nào hoàn thành.
Một mô-đun đống là một danh sách các phần
Các đặc điểm cốt lõi xác định một mô-đun như tiền đề 00 61 73 6d 01 00 00 00, dây \0asmvà phiên bản 1, sau đó là các phần.u32ID 1 đến 13 là các phần được biết đến, mỗi lần tối đa và theo thứ tự. ID 0 là một phần tùy chỉnh với một tên, được phép ở bất cứ nơi nào. Tất cả các số nguyên là LEB128, vì vậy e4 00là 100 và 80 08là 1024.wasm-objdump -hledger.wasmliệt kê bảy phần, và WasmModuletìm thấy cùng bảy với cùng một tải trọng bù đắp:
| Section | Payload | Count | What it holds for ledger |
|---|---|---|---|
| Type | 10 to 26 | 3 | (i32) -> nil, (i32, i32) -> i32, (i32) -> i32 |
| Import | 28 to 39 | 1 | env.log with type 0 |
| Function | 41 to 46 | 4 | type index for each defined function |
| Memory | 48 to 51 | 1 | one page, no maximum |
| Export | 53 to 110 | 5 | memory and the four function names |
| Code | 113 to 271 | 4 | bodies of 23, 63, 44, and 23 bytes |
| Data | 273 to 290 | 1 | F\0D\0C\0B\0A\0 at offset 1024 |
Sự phân chia giữa các phần chức năng và mã tồn tại để trình duyệt có thể bắt đầu biên soạn trước khi toàn bộ tập tin đến. Mỗi mục mã là một kích thước, một vector của các tuyên bố địa phương và một biểu thức.gradephương tiện địa phương là 01 7f: một nhóm một i32- Đó là bucket, với loại hình của nó và không có tên của nó.
Các quy trình kiểm soát được cấu trúc bởi xây dựng.block- loop, hoặcifCác nhãn được chỉ mục theo độ sâu tổ. br_tablethực hiện một nhánh gián tiếp thông qua một operand chỉ mục danh sách nhãn.gradetrong file offset 0xa3- Có thể là:
0e br_table
0b 11 labels follow
04 04 04 04 04 04 buckets 0 to 5: label 4, the outermost block, returns 0
03 02 01 00 buckets 6, 7, 8, 9: labels 3, 2, 1, 0
00 bucket 10: label 0, shared with 9
04 default label 4Mỗi nhãn là độ sâu. Label 0 là nội tâm nhất block, và mã sau nó endlà i32.const 4tiếp theo là br 4- 6blockhướng dẫn trước bàn, mười một nhãn, và năm brchỉ dẫn sau khi nó là toàn bộ switch, và một người xác nhận từ chối bất kỳ nhánh nào rời khỏi cấu trúc. disassemble_wasm()theo dõi độ sâu để các đoạn liệt kê như wasm-objdump, bao gồm mỗi mã op-code một-byte từ unreachableđếni64.extend32_s, và phù hợp với những người bị bắt giữ wasm-objdump -dtrên 77 trong số 77 hướng dẫn cho cả hai tòa nhà.
Tên là thứ duy nhất mà một mô-đun đơn giản rơi xuống.wasm-objdumpdấu ấn vẫn còn<grade>vì phần xuất bản bản đồ chuỗi đó để hàm chỉ số 2, và người đọc làm tương tự. Tên địa phương không tồn tại ở đâu trong tệp 290-byte. wat2wasm --debug-namesthêm một phần tùy chỉnh có tên namevới các phần phụ cho tên module, tên chức năng, và tên địa phương.{0: 'score', 1: 'bucket'}cho hàm 2 và {0: 'values', 1: 'count', 2: 'sum', 3: 'i'}cho hàm 3. wasm-stripDòng lưu trữ các công cụ-thông ước mô tả hai nhà cung cấp hơn. DWARF có thể được nhúng như .debug_*Các phần tùy chỉnh.sourceMappingURLphần tùy chỉnh có thể chỉ vào một bản đồ nguồn có số cột là thay đổi byte vào bộ nhị phân.
Công cụ được di chuyển trong năm 2026.wasm-decompile, và Homebrew xây dựng trên máy này có chính xác 11 công cụ còn lại. Wasm-tools 1.261.0 cung cấpprint- objdump, vàdemangle. Binaryen version_133 cung cấp wasm-dis. radar2 6.2.4 cũng đọc mô-đun.rabin2 -Inói arch wasmNó là biểu tượng củagradebắt đầu từ 0x8e, nơi các phương tiện địa phương bắt đầu, trong khi wasm-objdumpnhãn chức năng tại 0x8b, nơi mà trường kích thước của nó bắt đầu.get_local- set_local, vàbrtable 11 4 4 ...Với số lượng nhãn được in. Các byte là giống nhau trong cả ba công cụ và đánh vần khác nhau, vì vậy một so sánh phải đặt tên công cụ mà nó đã được chụp. Nghiên cứu cho bài học này không tìm thấy mô-đun bộ xử lý WebAssembly trong thư mục bộ xử lý của Ghidra, và danh sách đó không được đọc đầy đủ. Các bên thứ ba ghidra-wasm plugin v2.4.0 nhắm mục tiêu Ghidra 12.0. Bản thân thông số kỹ thuật là một tài liệu sống. Trang W3C cho wasm-core-2là một dự thảo khuyến nghị ứng cử viên ngày 13 tháng 10 năm 2026 có bản tóm tắt mô tả phiên bản 3.0, và dự thảo của biên tập viên có cùng ngày.
ECMA-335 Metadata là cơ sở dữ liệu của chương trình
Một tập hợp .NET là một tập tin PE. LedgerNet.dllbắt đầu với MZ, có ba phần được đặt tên .text- .rsrc, và.reloc, và tuyên bố máy tính 0x14cvì trình biên dịch đã phát ra mã trung lập nền tảng. thư mục dữ liệu 14 của tiêu đề tùy chọn, CLR Runtime Header, chỉ ra RVA 0x2008ECMA-335 II.25.3.3 xác định 72 byte ở đó:
48 00 00 00 Cb 72
02 00 05 00 MajorRuntimeVersion 2, MinorRuntimeVersion 5
78 21 00 00 MetaData RVA 0x2178
84 07 00 00 MetaData size 1924
01 00 00 00 Flags: COMIMAGE_FLAGS_ILONLY
05 00 00 06 EntryPointToken 0x06000005, MethodDef row 5, which is MainCác metadata gốc tại RVA 0x2178bắt đầu bằng chữ ký 42 53 4a 42, có đoạn BSJB, sau đó là phiên bản 1.1 và chuỗi v4.0.303195 bộ tiêu đề dòng chảy theo:#~trong sự bù đắp0x6c, sau đó #Strings- #US- #GUID, và#BlobII.24.2 nói mỗi loại dòng chảy xảy ra tối đa một lần.#~dòng bắt đầu với một HeapSizesByte, 0 ở đây, vì vậy mỗi chuỗi, GUID, và chỉ số blob là hai byte rộng, và một 64-bit Validmặt nạ đặt tên cho các bảng hiện tại.
| Table | Id | Rows | Row size | What the rows say |
|---|---|---|---|---|
| Module | 0x00 | 1 | 10 | the module name and its GUID |
| TypeRef | 0x01 | 21 | 6 | System.Object, System.Console, 19 more |
| TypeDef | 0x02 | 4 | 14 | <Module>, Ledger, and two compiler helpers |
| Field | 0x04 | 2 | 6 | Labels and a static array initialiser |
| MethodDef | 0x06 | 6 | 14 | the five methods and .cctor |
| Param | 0x08 | 5 | 6 | before, after, score, values, score |
| MemberRef | 0x0a | 16 | 6 | 12 attribute constructors and the 4 methods that Main calls |
| CustomAttribute | 0x0c | 12 | 6 | attributes the compiler and the SDK add |
| StandAloneSig | 0x11 | 3 | 2 | the three local variable signatures |
| Assembly, AssemblyRef, ClassLayout, FieldRva, NestedClass | 1, 2, 1, 1, 1 | identity, references, layout |
Một kích thước hàng không được lưu trữ ở bất cứ đâu. II.22 cho mỗi cột một loại, và II.24.2.6 cho quy tắc kích thước.HeapSizes. Một chỉ số bảng đơn giản là hai byte trong khi bảng đó có ít hơn 65536 hàng. Một chỉ số mã hóa có thể chỉ ra vào n bảng với b thẻ bit là hai byte trong khi mỗi bảng đó có ít hơn 2 ^ 16-b) hàng. CliAssemblytính toán các kích thước 14 hàng từ các quy tắc này và số lượng hàng. Hệ thống.Tình phản xạ.Thả dữ liệu mét trong các thiết bị báo cáo cùng một kích thước và các trục trục trục cùng nhau, đó là cách kiểm tra chứng minh toán học.
III.1.9 xác định biểu tượng: byte cao là bảng, ba byte thấp là hàng dựa trên 1. 0x06000002là MethodDefhàng 2. Đường đó có RVA 0x2060, tên Grade, và một vết signature 00 01 08 08: thông tin gọi mặc định, một tham số, trả lại int32, tham số int32- Nó là ParamListđiểm ở hàng 3 của Param, tên của ông là score- Bảng Param là lý do tại sao ILSpy có thể inGrade(int score)từ một bộ phận phát hành bị tước đi.0x2060Cơ thể phương pháp bắt đầu với một tiêu đề mỡ:
13 30 flags 0x13: fat, init locals; size 3 dwords
02 00 MaxStack 2
2d 00 00 00 CodeSize 45
01 00 00 11 LocalVarSigTok 0x11000001StandAloneSighàng 1 giữ 07 01 08LOCAL_SIG, một người địa phương, int32. Local có một loại và không có tên. ECMA-335 giữ tên địa phương trong định dạng chuyển đổi lỗi của Partition V, và .NET SDK viết chúng vào một PDB.DebugTypeKhông có, vì vậy không có bảng và không có tệp mang tên bucket- PercentChangecó một tiêu đề nhỏ thay vào đó: byte đầu tiên của nó 3alà 0x0e << 2 | 2, một cơ thể 14 byte mà không có người dân địa phương và một đống mặc định của 8.
45 byte mã của Gradegiải mã với bảng opcode Partition III. switchlà opcode 0x45tiếp theo là u32đếm và nhiều như vậy int32Mục tiêu liên quan đến hướng dẫn sau bảng:
IL_0000: 02 ldarg.0
IL_0001: 1f 0a ldc.i4.s 10
IL_0003: 5b div
IL_0004: 0a stloc.0
IL_0005: 06 ldloc.0
IL_0006: 1c ldc.i4.6
IL_0007: 59 sub
IL_0008: 45 05 00 00 00 08 00 00 00 06 00 00 00 04 00 00 00 02 00 00 00 02 00 00 00
switch (IL_0029, IL_0027, IL_0025, IL_0023, IL_0023)
IL_0021: 2b 08 br.s IL_002b
IL_0023: 1a 2a ldc.i4.4, retBộ biên dịch đã trừ 6 trước khi switch, vậy là trường hợp 6 là mục tiêu đầu tiên.disassemble_cil()render token qua các bảng: ldsfld Labels- call Grade- call System.Console::WriteLine, vàldstr ' 'từ #USLời tiên tri cho tất cả những điều này làMetaDump, một chương trình C# ngắn trong các thiết bị được xây dựng trên System.Reflection.MetadataNó in số lượng hàng, kích thước và trục xuất của mỗi bảng, RVA của mỗi phương pháp, chữ ký, tên tham số, và byte IL, và 226 trường của System.Reflection.Emit.OpCodes- compare_with_metadump()đồng ý với tất cả 246 kiểm tra. bảng runtime có 218 mã opcode cộng với tám .prefixbảng bài học có 219 mục vì nó bao gồm:no.từ III.2.2, mà bảng Emit chưa bao giờ có.
Công cụ cho định dạng: ILSpy 11.0 (MIT, phát hành năm 2026-08-12) chạy trên macOS thông qua phiên bản máy tính để bàn Avalonia của nó. ilspycmd11.1.0.9782 in IL với -ilvà chọn một phương pháp theo token với -m 0x06000002. dnSpyEx 6.6.0 tiếp tục dnSpy chỉ trên Windows.ildasmcác tàu như các gói NuGet thay vì bên trong SDK. radar2 6.2.4 cũng đọc các metadata: rabin2 -Inói arch cilvà class PE32, vàisdanh sách Ledger.Grade(int:score):intNó không có bộ giải thể CIL trong xây dựng này. Thiết bị này được xây dựng với .NET SDK 8.0.205 bởi vì đó là SDK được cài đặt trên máy xây dựng. .NET 10 đã hiện hành từ năm 2025-11-11 và viết các cấu trúc ECMA-335 tương tự.
Những gì mỗi thùng chứa giữ
Đặtgradetừ tất cả 5 thùng chứa trong một bàn.python3 code/main.py keeps, chạy bộ đọc trên các thiết bị:
| Container | Body bytes | Function name | Parameter names | Parameter and return types | Local names | Line numbers | Control flow |
|---|---|---|---|---|---|---|---|
| ELF object, clang -O0 | 118 | grade in .symtab | none | none | none | none, no .debug_* sections | 5 jumps and 1 indirect jump through a relocation table |
| CPython 3.14 pyc | 96 | co_qualname | score | none | bucket | co_linetable, first line 10 | 4 compare and POP_JUMP_IF_FALSE pairs |
JVM class, javac -g | 50 | constant pool | score in LocalVariableTable | (I)I | none beyond the parameter | LineNumberTable, 6 rows | 1 tableswitch |
| WebAssembly | 63 | Export section | only with a name section | (i32) -> i32 | only with a name section | none | 6 nested blocks, 1 br_table, 4 br |
| .NET assembly | 45 | MethodDef | score in Param | static int32(int32) | none, types only in StandAloneSig | none without a PDB | 1 switch, 1 br.s |
Bảng giải thích kết quả phân tích mà bạn sẽ thấy. Bài học 34 so sánh int total(const int *, int)với int total(long param_1, uint param_2)Ghidra suy luận từ mã bản địa, bởi vì mã bản địa không giữ bất kỳ cột nào trong số này. Một pyc giữ mọi tên và không có loại, và dòng điều khiển của nó là nhảy có hình dạng thay đổi mỗi phát hành, do đó trình giải trình Python phá vỡ mỗi phiên bản và phục hồi tên miễn phí.-glà trên, và thể hiện switchMột mô-đun Wasm giữ cấu trúc bằng cách xây dựng, vì vậy wasm2watlà một chuyến đi vòng không mất và mất mát duy nhất là tên. Một tập hợp .NET giữ được mô tả đầy đủ nhất, bởi vì các bảng siêu dữ liệu là quy định, và vẫn cần một PDB cho tên của một địa phương.
Bảng cho bạn biết máy giải biên dịch phải làm việc gì, và các đầu ra của nó sao chép một sự kiện được lưu trữ và kết luận một. Một tên tham số trong đầu ra ILSpy là một bản sao. Một tên địa phương trong cùng đầu ra là một kết luận trừ khi có một PDB. Bài học 35 ghi lại sự khác biệt đó như thẩm quyền của mỗi tuyên bố.
Hãy xây dựng nó
Chạy demo:
cd phases/13-tools-and-protocols/43-bytecode-and-managed-formats
python3 code/main.py
npx tsx code/main.tsKết quả là một báo cáo JSON. cho mỗi pyc nó cho cả hai tiêu đề, tệp và gradekích thước, số lệnh và tên opcode từ danh sách được chụp, và liệu người giải thích đang chạy có thể đọc nó hay không. Đối với mỗi lớp xây dựng nó cho phiên bản, kích thước, số lượng bảng gỡ lỗi, và các javapĐối với mỗi cấu trúc Wasm nó cho các phần, loại, chức năng, nội dung phần tên, và cácwasm-objdumpĐối với bộ sưu tập, nó cung cấp tiêu đề CLI, dòng, số lượng hàng bảng, phương pháp và hệ thống.Tình ảnh.PATH- Tôi không biết.
Đọc code/main.pytheo thứ tự này:
Readergiữ trình chiếu và chiều rộng số nguyên.uleb()vàsleb()là những bộ đọc LEB128 mà Wasm và .NET cần.read_pyc_header()-Unmarshaller, vàPycFileđọc một máy tính.parse_dis_text()đọc những người bị bắt giữdisdanh sách để so sánh phiên bản chéo không cần bốn người giải thích tại thời điểm thử nghiệm.ClassFileđọc tập tin lớp vàdisassemble_jvm()giải mã mã của nó.parse_javap_code()vàcompare_with_javap()là kiểm tra của thần tiên.WasmModuleđọc các phần vàdisassemble_wasm()giải mã một biểu hiện với độ sâu của nó.compare_with_wasm_objdump()là kiểm tra của thần tiên.CliAssemblyđi bộ PE, tiêu đề CLI, metadata root, stream và bảng.TABLE_SCHEMASvàCODED_INDEXESlà II.22 và II.24.2.6 như dữ liệu.disassemble_cil()giải mã một cơ thể vàcompare_with_metadump()kiểm tra nó.NativeObjectđọc đủ ELF để liệt kê các biểu tượng hàm và lấy bài học 33 liệt kê như là văn bản của nó.what_each_keeps()vàfigure_data()xây dựng sự so sánh.
Có thêm 5 lệnh nữa.python3 code/main.py pyc FILE- class FILE- wasm FILE, vàdll FILEin cấu trúc và danh sách của mỗi chức năng trong một tệp bạn cung cấp cho họ. python3 code/main.py comparebiên soạn ledger.pyvới mọi người diễn giải trên PATHvà in những gì mỗi người viết.
Sử dụng nó
Sử dụng các độc giả theo thứ tự này trên một thùng bạn đã xây dựng.
- Đọc các byte đầu tiên và đặt tên định dạng.
0d 0alà một pyc,ca fe ba bemột hồ sơ lớp học,00 61 73 6dmột mô-đun,4d 5avới thư mục dữ liệu 14 đặt một bộ sưu tập. - Đọc tiêu đề và ghi lại phiên bản sản xuất nó tên cho một pyc là từ phép thuật, cho một tệp lớp phiên bản chính, cho một tập hợp các chuỗi phiên bản runtime và metadata.
- Hãy kiểm tra mọi trình giải biên dịch mà bạn dự định chạy với phiên bản đó trước khi bạn chạy nó.
- Đánh bật trình đọc và công cụ của định dạng trên cùng một tệp và so sánh chúng hàng với hàng. Một sự không phù hợp là một lỗi trong trình đọc hoặc một thay đổi trong công cụ, và cả hai phải được hiểu trước khi danh sách là bằng chứng.
- Sẵn sàng để xem các tính năng bạn quan tâm, ghi dấu từng sự kiện như là được lưu giữ hoặc bị mất và đặt tên cho bảng, thuộc tính hoặc phần mà nó xuất phát từ.
- Chỉ sau đó đọc sản xuất decompiler, đánh dấu mỗi tên và nhập vào nó như một bản sao của một sự thật được giữ hoặc như một suy luận.
Giữ các tệp văn bản Oracle với các container. Khi phiên bản phiên bản phiên bản, JDK, SDK hoặc wabt thay đổi, xây dựng lại với build_fixtures.shvà bắt lại, bởi vì một danh sách thay đổi là một vật cố định thay đổi.
Phòng thí nghiệm tương tác
Phòng thí nghiệm A: đọc tiêu đề, sau đó là đối tượng mã
Từ codethư mục, mở Python:
cd phases/13-tools-and-protocols/43-bytecode-and-managed-formats/code
python3 -qĐi chạy:
from main import *
pyc = load_pyc("ledger.cpython-311.pyc")
pyc.header.to_dict()
pyc.module.names
pyc.functions()["grade"].varnames
len(pyc.functions()["grade"].code)Hãy viết ra từ phép thuật trước khi gọi.ledger.cpython-314.pycvà so sánh hai thứ này.code- Vậy thử.import marshal; marshal.loads(pyc.data[16:])trên tập tin 3.11 dưới phiên dịch của bạn và đọc lỗi.
Phòng thí nghiệm B: chuỗi hồ bơi liên tục
cls = load_class("Ledger-g.class")
cls.constant(13)
cls.constant(14)
cls.describe_constant(13)
[row["text"] for row in jvm_listing(cls, "grade")]Theo dõi#13đến#8và #14, sau đó đến Utf8Đăng ký, bằng tay trước khi bạn gọi describe_constant- Vậy thì tải lênLedger-gnone.classvà gọicls.method("grade").attribute_names()Cả hai.Codearray là giống nhau, các thuộc tính không.
Phòng thí nghiệm C: độ sâu và nhãn
module = load_wasm("ledger.wasm")
[(row["depth"], row["text"]) for row in wasm_listing(module, "grade")]
module.function_name(2)
load_wasm("ledger-names.wasm").names["locals"][2]Tìm ra br_tableĐường và đọc độ sâu của nó.endhàng sau nó và phù hợp với mỗi số nhãn trong bảng.ledger.watvà tìm ra block $sixnhãn đó là 3 tên.
Phòng thí nghiệm D: từ token đến byte
asm = load_assembly("LedgerNet.dll")
asm.method("Grade")
body = asm.method_body(asm.method("Grade")["RVA"])
body.fat, body.max_stack, body.code_size, hex(body.local_signature_token)
asm.token_text(0x11000001)
[row["text"] for row in cil_listing(asm, "GradeLabel")]Xét tính toán file offset của RVA 0x2060từ .texttiêu đề phần bằng tay và so sánh với asm.rva_to_offset(0x2060)Vậy hãy nhìn lênasm.row(0x08, 3)và tìm ra tên ở đâu scoresống.
Phòng thí nghiệm thực hành
Thêm một ngôn ngữ nguồn thứ năm vào so sánh, hoặc một sự kiện thứ sáu vào bảng.
Yêu cầu:
- Viết một hàm với một vòng lặp và một biến địa phương trong một ngôn ngữ mà bạn có trình biên dịch, ví dụ Kotlin đến một tệp lớp hoặc Rust đến một mô-đun Wasm.
build_fixtures.sh- Tôi không biết. - Hãy khiến người đọc phù hợp đồng ý với lời tiên tri trong mọi hướng dẫn.
- Tăng
what_each_keeps()với container mới, và với một thực tế khác cho mỗi container: liệu cơ thể mang một bảng ngoại lệ hay khôngtrykhu vực. - Thêm container mới vào
figure_data()và tái tạo.site/figures-re-43.js- Tôi không biết. - Viết một bài kiểm tra thất bại khi một người đọc báo cáo một sự kiện như được giữ mà không đặt tên bảng, thuộc tính hoặc phần chứa nó.
Thành công là nghĩa củapython3 code/main.py keepscho thấy hàng mới với bằng chứng cho mỗi tế bào, và mọi so sánh Oracle vẫn đồng ý.
Bài học: Cây tạo vật
Bài học này bao gồm:outputs/skill-bytecode-and-managed-formats.mdSử dụng nó để biến một container bytecode thành một báo cáo tiêu đề, một danh sách được kiểm tra của một chức năng và một bảng dữ liệu đánh dấu từng tên, loại và cấu trúc như được lưu giữ hoặc bị mất. Nó đòi hỏi công cụ của định dạng như một lời tiên tri, kiểm tra phiên bản trước bất kỳ decompiler nào, và một tuyên bố về nơi mà mỗi dữ liệu bị mất sẽ sống.
Hãy kiểm tra
Chạy demo và bộ xác định:
cd phases/13-tools-and-protocols/43-bytecode-and-managed-formats
python3 code/main.py
npx tsx code/main.ts
python3 -m unittest discover -s code/tests -vViệc kiểm tra phải chứng minh:
- Bốn tiêu đề pyc mang các từ phép thuật 3495, 3531, 3571, và 3627, cờ 3, và một hash 8-byte, và các tệp dấu thời gian mang cờ 0, mtime 1791975565, và kích thước 497
- mỗi từ ma thuật cho 3.11 và sau đó nằm trong phạm vi
2900 + 50nquy tắc cho phép giải phóng - Unmarshaller trả lại cùng tên, liên tục, mã byte, tên biến và bảng hàng như
marshal.loads()trên tệp người diễn giải đang chạy đã viết - Tất cả bốn tập tin đều giữ tên mô-đun,
('score', 'bucket')chograde, dòng đầu tiên 10 vàledger.pynhư tên tệp grade.co_codelà 80, 62, 70 và 96 byte trong bốn phiên bản và các danh sách được chụp có 31, 26, 26 và 35 hướng dẫn- danh sách 3.11 đã
POP_JUMP_FORWARD_IF_FALSE, 3.12 và 3.13 cóRETURN_CONST, và 3.14 đãLOAD_SMALL_INT-LOAD_FAST_BORROW, vàNOT_TAKEN - 3.14
-C -Odanh sách hiển thị 13 mục cache tronggrade, năm người sau đóBINARY_OP, và các chương trình liệt kê thích nghiCOMPARE_OP_INT - một tiêu đề mà không có
0d 0a, một tiêu đề bị cắt, và một cơ thể không phải là một đối tượng code nângFormatError - một danh sách hướng dẫn chỉ tồn tại cho pyc được viết bởi người giải thích đang chạy và phù hợp với các hình ảnh được chụp
disđầu ra - các tập tin lớp có 65 hoặc 69 chính, kích thước 1581, 1832, 1404, và 1581, và số lượng nhóm liên tục là 82, 94, 79, và 82
- liên tục
#13quyết địnhMethod Ledger.grade:(I)Ivà#47đếnString F - người giải thể JVM đồng ý với
javap -c -ptrên 108 trong số 108 hướng dẫn trong cả bốn chế độ tableswitchở pc 4 là 36 byte với low 6, high 10, mặc định 48, và mục tiêu 46, 44, 42, 40, 40- ba mức debug thay đổi số tính năng và để lại 50 byte mã của
gradegiống nhau ifne-if_icmpge, vàgotomục tiêu tương đối với mã opcode chi nhánh- một phép thuật sai, trượt bayt, và một mã op không xác định tăng
FormatError - 7 phần Wasm có sự bù đắp tải trọng
wasm-objdump -hdấu ấn, và tên xây dựng thêm mộtnamephần tùy chỉnh ở số 293 đến 425 - các loại, các loại
env.lognhập khẩu, năm xuất khẩu, và phân đoạn dữ liệu tại 1024 decode như đã viết trongledger.wat - Bộ giải thể Wasm đồng ý với
wasm-objdump -dtrên 77 trong số 77 hướng dẫn trong cả hai chế độ gradecó 6 khối tổ, mộtbr_tableVới nhãn4 4 4 4 4 4 3 2 1 0 0và mặc định 4 ở độ sâu 6, và mộti32địa phương- Tên chức năng xuất phát từ xuất khẩu mà không có phần tên và tên địa phương chỉ có nó
- LEB128
e4 00là 100,7flà -1,80 08là 1024, và các loại khối được hiển thị nhưwasm-objdump - một đoạn văn sai, phiên bản 2, và một mã opcode không rõ tăng
FormatError - bộ phận là PE32 với máy
0x14c, CLI header tại RVA0x2008, thời gian chạy 2,5,ILONLY, và điểm nhập cảnh0x06000005 - phiên bản siêu dữ liệu là
v4.0.30319, năm dòng chảy tồn tại, và 14 bảng có số lượng hàng và kích thước hàng báo cáo Oracle - người đọc .NET đồng ý với System.Reflection.Metadata trên 246 trong 246 kiểm tra
Gradecó tham sốscore, chữ kýstatic int32(int32), một cái đầu béo vớiMaxStack2 và 45 byte mã, và mộtint32địa phương không có tên- CIL
switchmục tiêu,br.s-ldsfld-call, vàldstrOperand giải quyết thông qua các bảng và đống - bảng mã opcode CIL bao gồm tất cả mã opcode thời gian chạy và chỉ thêm
no. - bảng giữ báo cáo tên tham số trong bốn container và không có trong mã bản địa, và các loại trong ba
- Mỗi dòng Oracle trong demo đọc n của n
- hình ảnh tương tác chứa cùng một dữ liệu
figure_data()sản xuất
Các chế độ sản xuất thất bại
| Failure | What the weak tool reports | What the analysis must prove |
|---|---|---|
| Decompiler older than the interpreter | Unsupported opcode, output marked incomplete, or a claim of obfuscation | The magic word, the release it names, and the tool's documented ceiling |
| pyc read with the wrong interpreter | marshal.loads raises or returns garbage | Our unmarshaller reads names, constants, and sizes from any 3.9 to 3.15 file, and instructions come from the matching dis |
| Specialised opcodes mistaken for the file's content | COMPARE_OP_INT in a listing | A pyc holds the unspecialised form, specialisation happens in memory after warm-up |
| Local names reported for a default javac build | var1, var2 presented as source names | LocalVariableTable is present only with -g, the 50 code bytes are identical either way |
| Branch target off by the instruction length | A JVM listing whose jumps land mid-instruction | Offsets relative to the branch opcode, checked against javap |
| Switch padding skipped | A tableswitch with a wrong default and shifted cases | Zero to three padding bytes to a 4-aligned pc, 36 bytes for five cases |
| Wasm names assumed stripped | "no symbols" | Exports name every exported function, locals need the name section, wasm-strip removes only that section |
| Three tools, three spellings | get_local versus local.get, a function at 0x8b versus 0x8e | Same bytes, and the listing that the comparison was captured with |
| Table row size guessed | Every row after the first table misread | Row sizes from HeapSizes, row counts, and coded index widths, equal to the oracle's |
| Local names expected in a release assembly | V_0 presented as the source name | Locals have types in StandAloneSig and names only in a PDB that DebugType none did not write |
| Reader guesses an unknown opcode or type code | A plausible mnemonic | FormatError with the offset, and the oracle comparison stops there |
| Oracle captured from another build | Tests pass on stale text | build_fixtures.sh names every tool version and writes versions.txt and sha256.txt |
Quy tắc hoạt động
Hãy đọc tiêu đề trước và đặt tên người sản xuất nó chỉ ra. Hãy đọc container với một người đọc từ chối những gì họ không biết, và kiểm tra danh sách của nó so với công cụ của định dạng trên cùng một tệp. Sau đó nói, cho mỗi tên và loại trong đầu ra của máy phân tích, cho dù container đã lưu trữ nó hoặc công cụ suy luận nó.
Đọc thêm
- PEP 552: Deterministic pycs
- CPython
Include/internal/pycore_magic_number.h - Python
marshalmodule documentation - CPython
Python/marshal.c - Python 3.14
dismodule documentation - PEP 659: Specializing Adaptive Interpreter
- PEP 626: Precise line numbers for debugging and other tools
- PEP 790: Python 3.15 Release Schedule
- uncompyle6 on PyPI- decompyle3 on PyPI- xdis on PyPI- pycdc on GitHub- PyLingual on GitHub
- The Java Virtual Machine Specification, Java SE 25, Chapter 4: The class File Format
- The Java Virtual Machine Specification, Java SE 25, Chapter 6: The Java Virtual Machine Instruction Set
- javac manual, Java SE 25và javap manual
- CFR- Procyon- Vineflower- Fernflower in intellij-community- JADX
- Harrand, Soto-Valero, Monperrus, Baudry, "Java Decompiler Diversity and its Application to Meta-decompilation" (JSS 2020)
- ECMA-335, Common Language Infrastructure, 6th edition (June 2012)
- PE Format, Microsoft Learn
- System.Reflection.Metadata.Ecma335.TableIndex
- ILSpy on GitHub- ilspycmd on NuGet- dnSpyEx on GitHub
- WebAssembly Core Specification: Binary Format, Modulesvà Values
- WebAssembly Core Specification: Custom Sections (name section)
- WebAssembly tool-conventions: Debugging
- wabt 1.0.42 release notes- wasm-tools- Binaryen- ghidra-wasm-plugin
- radare2 on GitHub, phiên bản 6.2.4 được xây dựng từ nguồn cho bài học này
This free lesson is part of the AI Engineering from Scratch curriculum. Read the full explanation, run the lesson code, and verify the result in the interactive reader or from the repository source.
Browse the complete course catalog or open this lesson on GitHub.