Phase 13: Tools & Protocols

فراتر از کد بومی: پایتون، JVM، .NET و Bytecode WebAssembly

یک فایل بایت کد یک برنامه و یادداشت های سازنده در مورد آن است، و این درس خوانندگان را ایجاد می کند که نشان می دهد هر کانتینر کدام یادداشت را نگه می دارد.

Type: Build

Languages: Python, TypeScript, Java, C#, WebAssembly text

Prerequisites: Phase 13 · 32 (ELF from scratch), 33 (disassembly and control flow)

Time: ~150 minutes

اهداف یادگیری

  • 16-byte رو بخونيد.pycسرنخ از PEP 552 و رمز را بدون marshalماژول برای فایل هایی که توسط چهار نسخه مترجم نوشته شده است.
  • تجزیه یک فایل کلاس JVM از JVMS فصل 4: مجموعه ثابت، زمینه ها، روش ها، Codeویژگی و جدول هایی کهjavac -gاضافه می کنه
  • کد های JVM را در جدا کنیدLedger.classو اثبات ثبت نام در مقابلjavap -cبر روی چهار ساختاری از همان منبع
  • بخش ماژول WebAssembly را به بخش بخونید، هر بدن عملکرد را رمزگذاری کنید و نتیجه را با wasm-objdump -d. .
  • از دایرکتوری داده های PE به MethodDefردیف ها و CIL هر روش، با کتابخانه خواننده ای که ILSpy استفاده می کند بررسی می شود.
  • اندازه گیری، برای یک تابع در پنج ظرف، کدام نام، نوع و ساختار کنترل هر فرمت نگه دارد و کدام را از دست می دهد.

مشکل

تو ساختيledger.pyو پیدا شدledger.cpython-314.pycدر__pycache__از يه مامور پرسيديد که پرونده چي هست، مامور يه دستگاه تخريب کننده رو اجرا کردUnsupported opcodeو یک خط که میگه# WARNING: Decompyle incompleteمامور نتیجه گرفت که پرونده ها پوشيده شده

پرونده رو پوشيده نکرد دو بايت اولش2b 0eاین عدد CPython 3.14 می نویسد و جدول های کد آپکامپایلر در 3.13 متوقف می شوند. این ابزار قدیمی بود. عامل سر را نمی خواند، بنابراین نمی توانست تفاوت را تشخیص دهد.

درس 33 نشان داد که کد بومی چه چیزی را از دست می دهد: نام پارامتر، انواع و switchکه تبدیل به یک میز پرتاب شد. کانتینر های بایت کد کمتر از دست می دهند و هر کدام چیزی متفاوت از دست می دهند. یک pyc هر نام متغیر را نگه می دارد و هیچ نوعی ندارد. یک فایل کلاس فقط زمانی که شما درخواست کنید نوعها و نام های محلی را حفظ می کند. یک ماژول Wasm ساختار کنترل را حفظ می کند و نام ها را می گذارد مگر اینکه بخش سفارشی آنها را حمل کند. یک مجموعه .NET نام پارامتر را در یک جدول نگه می دارد و نام های محلی را به یک فایل جداگانه می گذارد. شما نمی توانید از هر یک از آنها نتیجه ی یک کمپایلر را قضاوت کنید تا زمانی که بدانید که آن ظرف با چه یادداشت هایی شروع شده است.

این درس چهار خواننده را در کتابخانه استاندارد پایتون ایجاد می کند. هر کدام کانتینر را از اولین بائتهای خود می خوانند، یک تابع را لیست می کنند و در ابزار فرمت خود در وسایل ساخت شده شما بررسی می شوند. بخش آخر می گوید:gradeازledgerدر هر پنج ظرف هم کنار هم

سرپرستی "پی سی" چهار کلمه است

PEP 552 عنوان را به عنوان چهار کلمه 32 بت تعریف می کند. اولین کلمه عدد جادویی است. دومین یک میدان بیت است. بیت 0 مجموعه به معنای فایل مبتنی بر هش است. بیت 1 مجموعه به معنای check_source: مترجم منبع را در واردات هاش می کند و مقایسه می کند. با بیت 0 پاک، کلمات سه و چهار mtime منبع و اندازه منبع هستند. با بیت 0 مجموعه، آنها یک SipHash 64 بیت از منبع را دارند.

شماره جادويي درInclude/internal/pycore_magic_number.h. قاعده آن از 3.11 این است که پایتون 3.n از 2900 + 50n شروع می شود و هر تغییر بایت کد در یک چرخه انتشار یک اضافه می کند. توکن ذخیره شده عدد 16 بیت است که از آن بعد \r\nدر این حالت، پیتون 3.15.0 در سال 2026-10-09 منتشر شد و در ماشین که این وسایل را ساخته است نصب نشده است، بنابراین شماره آن از فایل سرپرستی و نه از یک فایل در اینجا است.

چراغ ها دو فایل pyc در هر مترجم دارند.compile_pyc.pyنوشته بودpy_compile.compile(..., dfile="ledger.py")تا نام فایل ذخیره شده باشدledger.pyنه مسیر ماشین ساخت. اولین 16 بایت فایل 3.14:

output
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

سرنخ مهر زمان رو کلمه به کلمه بخونيد.03 00 00 00یا00 00 00 00اين کلمه پرچميه8d 60 cf 6a0x6acf608d = 1791975565, mtime از ledger.pyوقتی پرونده ساخته شدf1 01 00 00در این فایل ها، 3 و 8 بایت ها شامل است. چهار مترجم در همه چیز در عنوان موافقند، به جز کلمه جادویی:

InterpreterMagic bytesWordFile bytesgrade.co_code bytesgrade instructions
3.11.14a7 0d 0d 0a349510548031
3.12.12cb 0d 0d 0a35319376226
3.13.12f3 0d 0d 0a35719437026
3.14.62b 0e 0d 0a36279659635

read_pyc_header()درcode/main.pyاین زمینه ها را باز می گرداند و نام نسخه را از کلمه می گیرد. importlib.util.MAGIC_NUMBERبه همان چهار بایت برای مترجم در حال اجرا می دهد. python3 code/main.py compareمنبع رو با هر python3.11بهpython3.15در PATHبرای هر یک از آنها سرنخ را چاپ می کند که خواننده ما در کنار حقایق که مترجم در مورد خود گزارش می دهد می بیند.

مارشال کدهاي موضوعي را به صورت فيلدهاي

بايت 16 از هر دستگاهe3. اینا0x80 | ord('c'): کد نوع مارشال برای یک شی کد با FLAG_REFمجموعه ای که به خواننده میگه برای بعد از این موضوع را به یاد داشته باشهrاسناد مارشال می گوید که فرمت به طور عمدی مستند نشده است و که اشیاء کد بین نسخه ها سازگار نیستند. هر دو بیانیه درست است و فرمت هنوز به اندازه کافی کوچک است تا برای خواندن باشد. Python/marshal.cکد های نوع را ذکر می کند: Nهيچکسiیک int 32 بیتsبائتهایzوZرشته های کوتاه ASCII، (و)دوتاcیک شی رمز، rکنتینر ها جای مرجع خود را قبل از بچه هایشان ذخیره می کنند، بنابراین خواننده همین کار را می کند.

w_complex_object()برای 3.11 تا 3.15 این استargcount،posonlyargcount،kwonlyargcount،stacksize،flagsبه عنوان 32 بایت اینچ، پس code،consts،names،localsplusnames،localspluskinds،filename،name،qualnameپس به عنوان اجسامfirstlineno، پسlinetableوexceptiontable. کد ماژول کدی شی فایل 3.14 را از بایت 17 به دست رمزگذاری کنید:

output
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 ...

بائت های کد دو تا برای هر دستور هستند: 80 00.RESUME 0،52 06.LOAD_CONST 6،74 00.STORE_NAME 0و17 00.MAKE_FUNCTION. فهرست 3.14 در فکسچر ها همان پنج بیانیه در سطح ماژول را چاپ می کند.codeبعد ثابت ها، که چهار مورد کد را نگه می دارند، و بعد از آنهاnames.('LABELS', 'percent_change', 'grade', 'total', 'grade_label'). .

Unmarshallerدرcode/main.pyاین طرح و قدیمی تر که 3.9 و 3.10 استفاده می کند، جایی که nlocals،varnames،freevarsوcellvarsاین زمینه ها جدا شده اند.CodeObjectبا اسم هاي صفاتي که موجود واقعي دارهvarnamesفیلتر هاlocalsplusnamesتوسطCO_FAST_LOCALیه کمیlocalspluskinds. برایgradeدر هر يک از چهار پرونده بازمي گردد('score', 'bucket')،firstlineno10 تا و یک خالیlinetable. آزمونtest_unmarshaller_agrees_with_the_running_interpreterهر رشته رو با marshal.loads()سه پرونده ديگه توسط کد ما قابل خواندن و توسط کد ما قابل خواندن نیستندmarshalتحت اين مترجم

چهار مترجم، چهار باکت کد برای درجه

در این مستندات یک هشدار در هر نسخه وجود دارد: بایت کد یک جزئیات پیاده سازی CPython است و هیچ تضمینی وجود ندارد که ثابت بماند.python3.1N -m dis ledger.pyبرای هر مترجم ضبط شد.

در 3.11gradeشروعش اينطوره:

output
 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_OPدر آفس 6 و دستورالعمل بعدی در 10 قرار دارد. دو بایت بین آنها یک ورودی ذخیره شده از PEP 659 است. مترجم تخصصی سازنده. COMPARE_OPدر 16 دنبالش 22 هست پس دو تا از اونها هستshow_caches=True3.12 جایگزین می شودLOAD_CONSTو اضافهRETURN_VALUEباRETURN_CONST، نام پرش رو به POP_JUMP_IF_FALSEو درمانگرهاgradeاز 80 تا 62 بايت. 3.13 چاپ برچسب ها L1بهL4به جای تعویض و دادن CLI -Cبرای مخزن و-Oبرای تعویضات 3.14 حذف می شود RETURN_CONSTدوباره اضافه می کنهLOAD_SMALL_INTوLOAD_FAST_BORROW, و ورودی هاNOT_TAKENبعد از هر پرش مشروط:

output
 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 -Oلیست نشان می دهد 13 CACHEنوشته ها داخلgrade: بعد از پنجBINARY_OP، یکی پس از دیگریCOMPARE_OP، یکی پس از دیگریPOP_JUMP_IF_FALSE. دستگاه سازنده نشان ميده که حافظه هاي پيشگيري براي چي هستن بعد از 200 تماسdis.dis(grade, adaptive=True)چاپRESUME_CHECKوCOMPARE_OP_INTجایی که لیست ساده داشتRESUMEوCOMPARE_OPوtotalنمایش هاFOR_ITER_LIST،BINARY_OP_ADD_INTوJUMP_BACKWARD_NO_JIT. شکل های تخصصی در همانco_codeبائتهاي پي سي هرگز آنها را در خود ندارد چون تخصص در حافظه اتفاق می افتد.

به همین دلیل است که decompiler ها دارای سقف نسخه هستند. uncompyle6 3.9.3 می گوید که بایت کد را از پایتون 1.0 تا 3.8 پذیرفته است. decompyle3 3.9.3 3.7 و 3.8 را می خواند. xdis 6.3.0, جداکننده نسخه های متقاطع پشت هر دو، تا 3.13 می خواند. pycdc دارای جدول های opcode تا python_3_13.cppدر فایل ساختش و در یک کد چاپ نامعلومUnsupported opcodeو نشان دهنده محصول نامکمل است. PyLingual ادعا می کند که هر نسخه منتشر شده از 3.6 و استفاده مترجم از 3.8 تا 3.15 برای بازسازی و بررسی محصول خود را. برای یک فایل 3.14، سه مورد اول رد یا شکست می یابد و تنها دو مورد آخر اعمال می شود. قبل از انتخاب یک ابزار کلمه جادویی را بخوانید.

پرونده کلاس ها یک فرهنگ لغت خود را حفظ می کند

JVMS Java SE 25، بخش 4.1 طرح را به عنوان یک ساختار می دهد: magic،minor_version،major_version،constant_pool_count, استخر ثابت ,access_flags،this_class،super_class، رابط ها ، زمینه ها ، روش ها ، ویژگی ها0xCAFEBABEیک ماشین مجازی جاوا SE 25 نسخه های اصلی 45 تا 69 را پذیرفته و یک نسخه کوچک از 65535 یک فایل پیش نمایش ویژگی را نشان می دهد.Ledger-g.class.ca fe ba be 00 00 00 41 00 5e: اصلی 65 جاوا 21 است و0x5e= 94 شمارش ثابت است.Ledger-jdk25.class, ساخته شده توسط javac 25.0.2 از همان منبع شروع می شودca fe ba be 00 00 00 45: 69 درجه

در استخر ثابت، فایل کلاس فرهنگ لغت خود را نگه می دارد.#13در-gساخت یکMethodrefکه به کلاس اشاره داره#8و نام و نوع#14. دنبال کردن شاخص ها میادLedger.grade:(I)I، که دقیقاً همینطورهjavap -vبعد از اون//. وصف کننده(I)Iیک استintپارامتر و یکintنتیجه([I)Iدرtotalمیگه پارامتر یک استintهر روش، میدان و کلاس که کد لمس می کند چنین ورودی دارد و تایید کننده در بخش 4.10 یک فایل را که دستورالعمل آن با آنها مخالف است رد می کند.ClassFileکلاس درcode/main.pyتمام 17 تاگ ثابت را از جدول 4.4 می خواند و یک را درمان می کند LongیاDoubleبه عنوان دو نقطه. این در یک برچسب ناشناخته و باایت ها پس از ویژگی آخر افزایش می یابد.

ویژگی ها به کلاس، هر زمینه، هر روش و CodeJVMS می گوید که ماشین مجازی باید ویژگی هایی را که نمی داند نادیده بگیرد، به همین دلیل می توان بدون تغییر یک جسم روش جدول های دیبگ را اضافه یا رها کرد. راهنمای javac می گوید -gتمام اطلاعات دیبگینگ را از جمله متغیرهای محلی تولید می کند، که پیش فرض فقط شماره خط و فایل منبع را تولید می کند و که -g:noneسه تا اندازه را می سازند که:

BuildBytesConstant pool countLineNumberTableLocalVariableTableSourceFilegrade code bytes
javac -g1832947 methods6 methodsyes50
javac1581827 methodsnoneyes50
javac -g:none140479nonenoneno50

50 بايت کد از gradeدر هر سه پرونده همسان هستند و تست آن را بررسی می کند.-gاضافه شده برای gradeیک استLocalVariableTableخط: شروع 0، طول 50، سوراخ 0، نام score، وصف کنندهIبدونش یک decompiler قسمت 0 را می بیندintو باید اسم ای اختراع کنهStackMapTableدر هر ساخت باقی می ماند چون تایید کننده به آن نیاز دارد و BootstrapMethodsویژگی باقی می ماند چونmainرشته ها رو به هم متصل مي کنهinvokedynamic. .

کدنویچ میز ها با دست

فصل 6 JVMS هر دستورالعمل را به عنوان یک بایت آپکد و آپراند که نام های آپکد را می دهد تعریف می کند. آفس های شاخه ارزش های امضا شده 16 بیت نسبت به آدرس آپکد شاخه هستند که از x86-64 متفاوت است، جایی که جابجایی نسبت به دستورالعمل بعدی است. tableswitchوlookupswitchبا صفر تا سه بايت پديده شروع ميکنن تا 32 بيت اپرندها از آدرس 4 شروع بشه

بائتهاي gradeاز pc 4 در Ledger-g.class:

output
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 = 40

36 بايت، يک دستور و کلswitch (score / 10)با پرونده مشترکش برای 9 و 10. ساخت بومی همان switchدر درس 33 نیاز به چک محدودیت ها داشتlea، یکmovslq، یکadd، یک پرش غیرمستقیم و پنج نقل مکان در بخش دیگری در اینجا اهداف در دستورالعمل هستند و همینطور این واقعیت که آنها به هم تعلق دارند

disassemble_jvm()تمام 202 کد تعریف شده را با عرض اپراند خود پوشش می دهد، از قانون پر کردن پیروی می کند و راه را به کار می کند javap.compare_with_javap()بازرسي زنداني هاjavap -c -pمتن، از جمله متن چند خطtableswitchتمام چهار ساختار با 108 دستور توافق دارند. آزمونtest_branch_offsets_are_relative_to_the_branch_instructionچک هاifneدر pc 1 فرود در 6if_icmpgeدر 13 فرود در 33 وgotoدر 30 به عقب در 10 فرود مياد

رادار2 6.2.4 که از منبع برای این درس ها ساخته شده است، همان فایل را می خواند.rabin2 -Iگزارش هاarch java،lang java 21،linenum trueوlsyms true, که خواندن نسخه و دو جدول دیبگ است. pd 14 @ sym.Ledger.gradeچاپشtableswitchبا تيرها به پنج هدفشUnable to parse code attribute: 2در این فایل، که یک محدودیت ابزار نام خود و یک دلیل برای حفظjavapبه عنوان کلام. decompileers برای این فرمت در مقایسه با پایتون بالغ هستند. CFR 0.152 به مجوز MIT صادر شده پروکیون 0.6.0 و Vineflower 1.12.0 Apache 2.0 هستند و Vineflower یک شکنی Fernflower با پشتیبانی از جاوا 21 است. خود "فرن فلوور" در داخل مخزن جامعه "انتلج" زندگی می کنه JADX 1.5.6 فرمت Android DEX را پوشش می دهد و JD-GUI آخرین نسخه اش در سال 2019 بود. هاراند، سوتو والرو، مونپرروس و باودری هشت مورد را در سال 2020 اندازه گیری کردند. بهترین تولیدات ترکیب درست برای 84 درصد از کلاس ها و تولیدات معادل معنوی برای 78 درصد. متاداکامپلر آنها ۳۷.۶ درصد از کلاس هایی را که هیچ ابزار دیگری نمی توانست با آن ها کار کند، بازیابی کرد. انواع، نام ها و دسته های تایید شده باعث می شوند که فرمت قابل تجزیه باشد. آنها هیچ یک از decompiler کامل نمی کنند.

یک ماژول وسم یک لیست از بخش ها است

مشخصات اصلی یک ماژول را به عنوان پیشگام تعریف می کند00 61 73 6d 01 00 00 00، رشته\0asmو نسخه 1، و بعد از آن بخش ها. هر بخش یک بایت ID است، یک u32اندازه در LEB128 و محتوای آن. IDs 1 تا 13 بخش های شناخته شده هستند، هر یک در حد حداکثر یک بار و به ترتیب. ID 0 یک بخش سفارشی با نام است، اجازه می دهد در هر نقطه. همه اعداد کامل LEB128 هستند، بنابراین e4 00100 و 80 081024مستwasm-objdump -hدرledger.wasmهفت بخش را فهرست می کند وWasmModuleهفت مورد را با همان تعویض بار مفید پیدا می کند:

SectionPayloadCountWhat it holds for ledger
Type10 to 263(i32) -> nil, (i32, i32) -> i32, (i32) -> i32
Import28 to 391env.log with type 0
Function41 to 464type index for each defined function
Memory48 to 511one page, no maximum
Export53 to 1105memory and the four function names
Code113 to 2714bodies of 23, 63, 44, and 23 bytes
Data273 to 2901F\0D\0C\0B\0A\0 at offset 1024

تقسیم بین بخش های تابع و کد وجود دارد تا مرورگر بتواند قبل از ورود کل فایل شروع به جمع آوری کند. هر ورودی کد یک اندازه، یک ویکتور اعلامیه های محلی و یک عبارت است. برای gradeوکتور محلی01 7f: یک گروه از یک نفرi32. ایناbucket، با نوعش و بدون اسمش

جریان کنترل توسط ساخت ساختار می شود. مشخصات می گوید شاخه ها فقط می توانند بسته شدن را هدف قرار دهند.block،loop، یاifبرچسب ها با توجه به عمق انبوه بندی انندکس می شوند.br_tableیک شاخه غیر مستقیم را از طریق یک اپراند انجام می دهد که یک لیست برچسب را شاخص می کند.gradeدر فایل آفسه0xa3:

output
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 4

هر برچسب عمق است. برچسب 0 درونی ترین است.block، و کد بعد از آنend.i32.const 4بعدشbr 4از بلوک نتیجه خارج شد.blockدستورالعمل های پیش روی میز، ۱۱ برچسب و ۵ برچسبbrبعد از اينکه تمامش رو هم بگيريمswitch، و یک اعتبار دهنده هر شاخه ای را که ساختار را ترک می کند رد می کند.disassemble_wasm()عمق رو ردیابی می کنه پس قطعات فهرست مثلwasm-objdump, هر کد آپ بايتي از unreachableبهi64.extend32_sو با دستگير ها مطابقت دارهwasm-objdump -dدر 77 از 77 دستورات برای هر دو ساختمون

اسم ها تنها چیزی هستند که یک ماژول ساده می گذارد.wasm-objdumpهنوز چاپ شده<grade>به این دلیل که بخش صادرات نقشه هایی را که رشته به شاخص عملکرد 2 می کند، و خواننده همان کار را انجام می دهد. نام های محلی در هیچ جایی در فایل 290 بایت وجود ندارد. wat2wasm --debug-namesبخش سفارشی را با نام اضافه می کند nameبا زیر بخش هایی برای نام ماژول، نام عملکرد و نام های محلی. فایل به 425 بایت می رسد. خواننده پیدا می کند {0: 'score', 1: 'bucket'}برای تابع 2 و {0: 'values', 1: 'count', 2: 'sum', 3: 'i'}برای تابع 3wasm-stripدر ورق ذخیره سازی کنوانسیون ابزار دو حامل دیگر را توصیف می کند. DWARF می تواند به عنوان.debug_*بخش های سفارشی.sourceMappingURLبخش سفارشی می تواند به یک نقشه منبع اشاره کند که شماره ستون آن باایت های تعویض شده به دوگانه است.

ابزارها در طول سال 2026 حرکت کردند.wasm-decompile، و ساخت Homebrew روی این ماشین دقیقاً ۱۱ ابزار باقی مانده را دارد.print،objdumpوdemangle. نسخه_133 بينيارين ارائه ميدهwasm-disرادار2 6.2.4 ماژول رو هم ميخوادrabin2 -Iمیگهarch wasm. نمادش برايgradeشروع می شه0x8e، جایی که متری محلی شروع می شود، در حالی که wasm-objdumpبرچسب ها تابع در 0x8bدر فهرستش از يادداشت هاي قدیمی تر استفاده ميکنهget_local،set_localوbrtable 11 4 4 ...با چاپ شماره برچسب بائتهای در هر سه ابزار یکسان هستند و املاکی متفاوت است، بنابراین یک مقایسه باید نام ابزار را که با آن ضبط شده است نام دهد. تحقیقات برای این درس هیچ ماژول پردازنده WebAssembly در دایرکتوری پردازنده های Ghidra پیدا نکردند و این لیست به طور کامل خوانده نشد. افزونه Ghidra-wasm v2.4.0 طرف سوم هدف Ghidra 12.0 است. مشخصات خودش یک سند زنده است. صفحه W3C برای wasm-core-2یک طرح توصیه کاندید از 13 اکتبر 2026 است که خلاصه آن نسخه 3.0 را توصیف می کند و طرح ویرایشگر نیز همان تاریخ را دارد.

ECMA-335 متاداتا پایگاه داده برنامه است

یک مجموعه .NET یک فایل PE است.LedgerNet.dllبا شروع می شهMZ، سه بخش به اسمش داده شده.text،.rsrcو.reloc، و اعلام میکنه0x14cبه این دلیل که کامپایلر کد خنثی سیستم عامل را منتشر کرده است. دایرکتوری داده 14 از سرپرست اختیاری، سرپرست زمان اجرا CLR، به RVA اشاره دارد 0x2008ECMA-335 II.25.3.3 72 بایت را در آن تعریف می کند:

output
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 Main

ریشه متادتا در RVA 0x2178با امضا شروع میشه42 53 4a 42، که میگهBSJB، بعد نسخه 1.1 و رشتهv4.0.30319پنج تا از سرپرستان جریان ها:#~در تعویض0x6c، پس#Strings،#US،#GUIDو#BlobII.24.2 می گوید هر نوع جریان حداکثر یک بار رخ می دهد.#~جریان با یک شروع می شودHeapSizesبايت، صفر در اینجا، پس هر رشته، GUID، و شاخص نقطه دو بايت گسترده است، و یک 64 بيتValidماسک که نام جدول های موجود را مشخص می کند. چهارده بیت تنظیم شده است:

TableIdRowsRow sizeWhat the rows say
Module0x00110the module name and its GUID
TypeRef0x01216System.Object, System.Console, 19 more
TypeDef0x02414<Module>, Ledger, and two compiler helpers
Field0x0426Labels and a static array initialiser
MethodDef0x06614the five methods and .cctor
Param0x0856before, after, score, values, score
MemberRef0x0a16612 attribute constructors and the 4 methods that Main calls
CustomAttribute0x0c126attributes the compiler and the SDK add
StandAloneSig0x1132the three local variable signatures
Assembly, AssemblyRef, ClassLayout, FieldRva, NestedClass1, 2, 1, 1, 1identity, references, layout

اندازه یک ردیف در هیچ جایی ذخیره نمی شود. II.22 به هر ستون نوعی می دهد و II.24.2.6 به قانون اندازه گیری می دهد. یک شاخص توده دو یا چهار بایت به HeapSizesیک شاخص جدول ساده دو بایت است در حالی که این جدول کمتر از 65536 ردیف دارد. یک شاخص کدگذاری شده که می تواند به جدول های n با بایت های برچسب اشاره کند دو بایت است در حالی که هر یک از این جدول ها کمتر از 2^(16-b) ردیف دارد. CliAssemblyاندازه 14 ردیف را از این قوانین محاسبه می کند و ردیف ها شمارش می شوند. سیستم.عكس.پرداد متادایی در وسایل گزارش می دهد اندازه های مشابه و تعویضات جدول مشابه است، که این روش آزمایش ثابت حسابداری است.

III.1.9 نشان دهنده را تعریف می کند: بائت بالا جدول است، سه بائت پایین ردیف مبتنی بر 1 است. 0x06000002.MethodDefخط 2 . این خط RVA دارد0x2060، اسمGrade، و یه خال امضا00 01 08 08: کنوانسیون تماس پیش فرض، یک پارامتر، بازگشت int32، پارامترint32. اونParamListنقاط در ردیف 3 از Param، که اسمشscore. ميز پارام براي اينه که اين جاسوسها ميتونن چاپ کننGrade(int score)از ساخت و ساز آزاد شده0x2060بدن روش با یک سر سر چربی شروع می شود:

output
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 0x11000001

StandAloneSigخط 1 نگه دارد07 01 08.LOCAL_SIG، یه محلیint32. محلی دارای نوع و نام نیست. ECMA-335 نام های محلی را در قالب تغییر تغییر اشکال بخش V نگه می دارد و SDK .NET آنها را به یک PDB می نویسد. این ابزار با DebugTypeهيچکدوم، پس هيچ ميز و پرونده اي با اين اسم نيستbucket.PercentChangeدر عوض يه سرگير کوچولو داره: اولين بايتش3a.0x0e << 2 | 2، يه جسم 14 بايت بدون هيچ محلي و يک دسته پيش فرض از 8

45 بايت کد از Gradeبا جدول کد Opcode بخش III رمزگذاری شود. switchکد آپکود0x45بعدش یکu32شمارش و اون همهint32اهداف مربوط به دستورالعمل بعد از جدول:

output
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, ret

کمپایلر 6 قبل از switch، پس پرونده 6 هدف اولهdisassemble_cil()نشان دهنده توکن ها در جدول ها: ldsfld Labels،call Grade،call System.Console::WriteLineوldstr ' 'از#US.کليک .کليک براي همه اين هاMetaDump، یک برنامه کوتاه C# در چراغ های ساخته شدهSystem.Reflection.Metadataاین تعداد صف ها، اندازه و تعویض هر جدول، RVA هر روش، امضا، نام پارامتر ها و بائتهای IL و 226 میدان از System.Reflection.Emit.OpCodes.compare_with_metadump()با تمام 246 چک موافقم جدول زمان اجرا 218 کد اضافه هشتprefixجدول درس 219 مطلب دارد چون شاملno.از III.2.2 که جدول emit هرگز نداشت.

ابزار برای فرمت: ILSpy 11.0 (MIT، منتشر شده 2026-08-12) از طریق ساخت ورودی Avalonia در macOS اجرا می شود. ilspycmd11.1.0.9782 چاپ IL با -ilو روش رو به صورت نماد با -m 0x06000002. dnSpyEx 6.6.0 فقط در ویندوز ادامه داره dnSpyildasmسفینه ها به عنوان بسته های NuGet به جای داخل SDK. radare2 6.2.4 میتا داده ها را هم می خواند: rabin2 -Iمیگهarch cilوclass PE32وisلیست هاLedger.Grade(int:score):intاین ابزار با .NET SDK 8.0.205 ساخته شده است زیرا این SDK در ماشین ساخت نصب شده است. .NET 10 از سال 2025-11-11 به طور فعلی بوده و همان ساختار ECMA-335 را می نویسد.

آنچه هر ظرف نگه دارد

بذارgradeاز تمام پنج ظرف در یک میز. هر ردیف ازpython3 code/main.py keeps، که خواننده ها رو بر روی چراغ ها می گذرد:

ContainerBody bytesFunction nameParameter namesParameter and return typesLocal namesLine numbersControl flow
ELF object, clang -O0118grade in .symtabnonenonenonenone, no .debug_* sections5 jumps and 1 indirect jump through a relocation table
CPython 3.14 pyc96co_qualnamescorenonebucketco_linetable, first line 104 compare and POP_JUMP_IF_FALSE pairs
JVM class, javac -g50constant poolscore in LocalVariableTable(I)Inone beyond the parameterLineNumberTable, 6 rows1 tableswitch
WebAssembly63Export sectiononly with a name section(i32) -> i32only with a name sectionnone6 nested blocks, 1 br_table, 4 br
.NET assembly45MethodDefscore in Paramstatic int32(int32)none, types only in StandAloneSignone without a PDB1 switch, 1 br.s

جدول نتایج تجزیه کننده را که خواهید دید توضیح می دهد. درس 34 مقایسهint total(const int *, int)باint total(long param_1, uint param_2)که گیدرا از کد بومی نتیجه گرفت، زیرا کد بومی هیچ یک از این ستون ها را حفظ نمی کند. یک pyc هر نام و هیچ نوع را حفظ می کند، و جریان کنترل آن پریدن هایی است که شکل آنها هر نسخه تغییر می کند، بنابراین پیکپین ها در هر نسخه شکسته می شوند و نام ها را به صورت رایگان بازیابی می کنند. یک فایل کلاس انواع و دسته های تأیید شده را حفظ می کند، نام ها را حفظ می کند وقتی-gدر حال اجرا است و بیان می کندswitchیک ماژول "ویسم" ساختار را با ساخت نگه می دارد، بنابراینwasm2watیک مجموعه .NET کامل ترین توصیف را حفظ می کند، زیرا جدول های متادتا قانونی هستند و هنوز هم برای نام یک محلی نیاز به PDB دارند.

جدول به شما می گوید که decompiler با چه چیزی کار می کند و کدام از خروجی ها یک واقعیت ذخیره شده را کپی می کنند و کدام یک را نتیجه می دهند. نام پارامتر در خروجی ILSpy کپی است. نام محلی در همان خروجی یک نتیجه است مگر اینکه PDB وجود داشته باشد. درس 35 این تفاوت را به عنوان مقام هر ادعای ثبت می کند.

آن را بسازید

نمایش نمایش را اجرا کن:

bash
cd phases/13-tools-and-protocols/43-bytecode-and-managed-formats
python3 code/main.py
npx tsx code/main.ts

محصول یک گزارش JSON است. برای هر pyc هر دو عنوان، فایل و را می دهدgradeاندازه ها، تعداد دستورالعمل ها و نام های کد های opcode از لیست گرفته شده، و اینکه آیا مترجم در حال اجرا می تواند آن را بخواند. برای هر کلاس ساخت آن را به نسخه، اندازه، شمارش جدول دیبگ، و javapبرای هر ساخت و ساز Wasm بخش ها، انواع، عملکردها، محتوای بخش نام وwasm-objdumpبرای جمع آوری، این CLI را به عنوان، جریان، شمارش ردیف جدول، روش ها و سیستم می دهد.PATH. .

بخونcode/main.pyدر این ترتیب:

  1. Readerدر برنده و عرض عددی کامل قرار دارد.uleb()وsleb()این خواننده های LEB128 هستند که واسم و .NET نیاز دارند.
  2. read_pyc_header()،UnmarshallerوPycFileيه پي سي رو بخونparse_dis_text()‫از دستگیران میخوادdisلیست ها به طوری که مقایسه بین نسخه ها به چهار مترجم در زمان آزمون نیاز ندارد.
  3. ClassFileپرونده کلاس رو میخواد وdisassemble_jvm()کدش رو رمزنگاري ميکنهparse_javap_code()وcompare_with_javap().آرهکل چک ميکنه
  4. WasmModuleبخش ها رو مي خواد وdisassemble_wasm()یک عبارت را با عمق آن رمزنگاری می کند.compare_with_wasm_objdump()این چکِ آسمانییه
  5. CliAssemblyدر پی، CLI سر، metadata root، جریان ها و جدول ها راه می رود. TABLE_SCHEMASوCODED_INDEXESII.22 و II.24.2.6 به عنوان داده ها هستند. disassemble_cil()یک بدن رو رمزگشایی می کنه وcompare_with_metadump()-آره، چکش کن
  6. NativeObjectبه اندازه کافی ELF را برای فهرست کردن نمادهای تابع می خواند و درس 33 را به عنوان متن خود می گیرد. what_each_keeps()وfigure_data()مقایسه رو بساز

پنج فرمان ديگه وجود دارهpython3 code/main.py pyc FILE،class FILE،wasm FILEوdll FILEساختار و لیست هر تابع را در یک فایل که به آنها می دهید چاپ کنید. python3 code/main.py compareمجموعه هاledger.pyبا هر مترجمي که دارهPATHو آنچه هرکدوم نوشته چاپ مي کنه

ازش استفاده کن

از خواننده ها در اين ترتيب در يک ظرف ساختي استفاده کنيد

  1. اولین بائتهای اول را بخوانید و نام فرمت را بنویسید. دو بائته بعد0d 0aیک پی سی استca fe ba beیک پرونده کلاس، 00 61 73 6dیک ماژول4d 5aبا فهرست داده 14 مجموعه ای را تنظیم کنید.
  2. برای یک pyc که کلمه جادویی است، برای یک فایل کلاس نسخه اصلی، برای یک مجموعه رشته های نسخه اجرا و متادتا.
  3. قبل از اجرا کردن هر نسخه ای که می خواهید اجرا کنید را با آن نسخه مقایسه کنید. سقف و جایی که ابزار آن را مستند می کند را ثبت کنید.
  4. در یک فایل، خواننده و ابزار فرمت خود را اجرا کنید و آنها را ردیف به ردیف مقایسه کنید. یک عدم مطابقت یک خطا در خواننده یا تغییر در ابزار است و هر یک از آنها باید قبل از اینکه لیست به عنوان مدرک شناخته شود درک شود.
  5. جدول نگهدارنده ها را برای عملکرد مورد علاقه تان پر کنید. هر واقعیت را به عنوان نگه داشته یا گم شده نشان دهید و جدول، ویژگی یا بخش که از آن آمده نام دهید.
  6. فقط بعد از آن محصول تخلیق کننده را بخوانید. هر نام را نشان دهید و آن را به عنوان یک کپی از یک واقعیت ذخیره شده یا به عنوان یک نتیجه گیری تایپ کنید.

فایل های متن Oracle را در کانتینرها نگه دارید. هنگامی که یک مترجم، JDK، SDK یا نسخه wabt تغییر می کند، با build_fixtures.shو دوباره ضبطش کنیم، چون لیست تغییر داده شده یک قسمت تغییر داده شده است.

آزمایشگاه تعاملی

لابراتوار A: سر و بعد کدهای موردی را بخوانید

ازcodeدایرکتوری، پیتون باز:

bash
cd phases/13-tools-and-protocols/43-bytecode-and-managed-formats/code
python3 -q

راه رفتن:

python
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)

قبل از اولین تماس کلمه جادویی رو بنویس و بعدش بارگذاری کنledger.cpython-314.pycو آن دو را مقایسه کنیدcodeبعدش امتحان کنimport marshal; marshal.loads(pyc.data[16:])در فایل 3.11 زیر مترجم خود را و خطا را بخوانید.

آزمایشگاه B: زنجیره ثابت استخر

python
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")]

دنبالش کن#13به#8و#14، بعدش بهUtf8نوشته ها، قبل از تماسdescribe_constantپس بارش رو بردارLedger-gnone.classو تماس بگیریدcls.method("grade").attribute_names()در هر دو موردCodeصفات یکسان هستند، صفات یکسان نیستند.

آزمایشگاه C: عمق و برچسب ها

python
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]

پیدا کنbr_tableو عمق آن را بخوان.endبعد از آن ردیف ها و هر یک را با یک شماره برچسب در جدول مطابقت دهید. سپس باز کنید ledger.watو پیدا کردنblock $sixاون برچسب 3 اسم

لابراتوار D: از توکن به بایت

python
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")]

محاسبه تعویض فایل RVA 0x2060از.textسرگوشن بخش با دست و مقایسه با asm.rva_to_offset(0x2060)پس نگاه کن بالاasm.row(0x08, 3)و پیدا کن اسمش کجاستscoreزندگی می کنه

آزمایشگاه تمرین

یک زبان منبع پنجم را به مقایسه اضافه کنید، یا یک واقعیت ششم را به جدول اضافه کنید.

الزامات:

  • یک تابع با یک حلقه و یک متغیر محلی را در یک زبان که شما سازنده آن را دارید، بنویسید، به عنوان مثال Kotlin به یک فایل کلاس یا Rust به یک ماژول Wasm. محصول و لیست Oracle را با دستور دقیق در build_fixtures.sh. .
  • با هر دستور و دستورات اوکرال موافق باشید. فقط زمانی که دستگاه به آنها نیاز دارد، اوکرال اضافه کنید.
  • طولانيwhat_each_keeps()با ظرف جدید و با یک واقعیت دیگر برای هر ظرف: آیا جسم دارای یک میز استثنا یا tryمنطقه
  • ظرف جدید را به اضافه کنیدfigure_data()و دوباره بازنشسته شدنsite/figures-re-43.js. .
  • یک آزمون را بنویسید که وقتی یک خواننده یک واقعیت را بدون نام دادن جدول، ویژگی یا بخش که آن را در بر دارد گزارش می دهد، شکست می خورد.

موفقیت به معنیpython3 code/main.py keepsکه نشان می دهد خط جدید با شواهد برای هر سلول و هر مقایسه Oracle هنوز هم موافق است.

درس هنرپیشه

این درس شاملoutputs/skill-bytecode-and-managed-formats.mdاز آن برای تبدیل یک کانتینر بایت کد به یک گزارش سر، یک لیست چک شده از یک تابع و یک جدول واقعیت هایی که هر نام، نوع و ساختار را به عنوان ذخیره یا گم شده نشان می دهد استفاده کنید. این نیاز به ابزار خود از فرمت به عنوان یک کلام، بررسی نسخه قبل از هر decompiler و بیانیه ای از جایی که هر واقعیت گم شده زندگی می کند.

بررسی کنید

نمایش و مجموعه تعیین کننده را اجرا کنید:

bash
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 -v

بررسی باید ثابت کند:

  • چهار سرنخ pyc دارای کلمات جادویی 3495, 3531, 3571, و 3627, پرچم های 3, و یک هشت باایت, و فایل های تایم استیمپ پرچم های 0, mtime 1791975565, و اندازه 497
  • هر کلمه جادويي براي 3.11 و بعدش در محدوده ي2900 + 50nقانون آزاد کردن
  • unmarshaller همان نام ها، ثابت ها، بائتهای کد، نام متغیرها و جدول خط را به عنوان marshal.loads()در پرونده که مترجم اجرا می کند نوشته شده است
  • همه چهار پرونده اسم ماژول رو نگه می دارن('score', 'bucket')برایgrade، اولين خط 10 وledger.pyبه عنوان نام فایل
  • grade.co_codeاین هشتاد، ۶۲، ۷۰ و ۹۶ بایت در چهار نسخه است و لیست های ضبط شده دارای دستورالعمل ۳۱، ۲۶، ۲۶ و ۳۵ هستند
  • فهرست 3.11 دارهPOP_JUMP_FORWARD_IF_FALSE، 3.12 و 3.13 دارندRETURN_CONST، و 3.14 دارهLOAD_SMALL_INT،LOAD_FAST_BORROWوNOT_TAKEN
  • ۳.۱۴-C -Oلیست 13 ورودی ذخیره شده را در grade، پنج تا بعد از اونBINARY_OP، و نمایش های لیست سازندهCOMPARE_OP_INT
  • سرنخ بدون0d 0a، يه سر و کله کوتاه شده و يه جسم که كود آبجيت نيست بلندش كنهFormatError
  • یک لیست دستورالعمل فقط برای pyc نوشته شده توسط مترجم در حال اجرا وجود دارد و با ضبط مطابقت دارد disتولید
  • پرونده های کلاس دارای 65 یا 69 اصلی، اندازه 1581, 1832, 1404 و 1581, و تعداد ثابت مجموعه 82, 94, 79, و 82 هستند
  • ثابت#13تصمیم می گیردMethod Ledger.grade:(I)Iو#47بهString F
  • دستگاه جداسازی JVM موافق استjavap -c -pدر 108 از 108 دستورالعمل در هر چهار ساخت
  • tableswitchدر pc 4 36 بایت با 6 پایین، 10 بالا، 48 پیش فرض، و اهداف 46, 44, 42, 40, 40 است
  • سه سطح دیبگ تعداد صفات را تغییر می دهد و 50 بایت کد را از gradeیکسان
  • ifne،if_icmpgeوgotoهدف ها نسبت به کد آپکود شاخه هستند
  • جادو اشتباه، بايت هاي عقب و افزايش نامعريف کد آپFormatError
  • هفت بخش " وسم " دارای تعویض بار مفید هستندwasm-objdump -hچاپ و نام ها اضافه می کند nameبخش سفارشی در 293 تا 425
  • انواعenv.logواردات، پنج صادرات و بخش داده ها در 1024 رمزگذاری شده در ledger.wat
  • دستگاه جدا کردن واسم موافقهwasm-objdump -dدر 77 از 77 دستورالعمل در هر دو ساختمون
  • grade6 تا بلوک هست که به هم میرهbr_tableبا برچسب ها4 4 4 4 4 4 3 2 1 0 0و 4 به طور پیش فرض در عمق 6 و 1 i32محلی
  • نام های عملکردی از صادرات بدون بخش نام و نام های محلی فقط با آن می آیند
  • LEB128 e4 00100 است7f-1 است80 081024 است و نوع بلوک مثل wasm-objdump
  • یک پیش فرض اشتباه، نسخه 2 و افزایش کد غیرقابل شناختFormatError
  • مجموعه PE32 با ماشین0x14c, CLI سر در RVA 0x2008, زمان اجرا 2.5 ,ILONLY، و نقطه ورود0x06000005
  • نسخه متاداتا اینهv4.0.30319، پنج جریان وجود دارد و 14 جدول تعداد صف ها و اندازه صف ها را گزارش می دهد
  • خواننده .NET با سیستم.آزمایش.متاداتا در 246 از 246 چک موافق است
  • Gradeدارای پارامتر استscore، امضاstatic int32(int32)، يه سرپرستي چاق باMaxStack2 و 45 بايت کد و 1 int32محلی بدون نام
  • CIL switchهدف هاbr.s،ldsfld،callوldstrاپراندها از طریق جدول ها و توده ها حل می شوند
  • جدول کد های CIL هر کد های زمان اجرا را پوشش می دهد و فقط اضافه می کند no.
  • جدول نگهدار نام پارامتر را در چهار ظرف و در هیچ یک برای کد بومی گزارش می دهد و در سه نوع
  • هر خط درامو در نماد N از n میخواد
  • این شکل تعاملی همان داده ها را در خود قرار می دهد figure_data()تولید می کند

روش های شکست تولید

FailureWhat the weak tool reportsWhat the analysis must prove
Decompiler older than the interpreterUnsupported opcode, output marked incomplete, or a claim of obfuscationThe magic word, the release it names, and the tool's documented ceiling
pyc read with the wrong interpretermarshal.loads raises or returns garbageOur 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 contentCOMPARE_OP_INT in a listingA pyc holds the unspecialised form, specialisation happens in memory after warm-up
Local names reported for a default javac buildvar1, var2 presented as source namesLocalVariableTable is present only with -g, the 50 code bytes are identical either way
Branch target off by the instruction lengthA JVM listing whose jumps land mid-instructionOffsets relative to the branch opcode, checked against javap
Switch padding skippedA tableswitch with a wrong default and shifted casesZero 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 spellingsget_local versus local.get, a function at 0x8b versus 0x8eSame bytes, and the listing that the comparison was captured with
Table row size guessedEvery row after the first table misreadRow sizes from HeapSizes, row counts, and coded index widths, equal to the oracle's
Local names expected in a release assemblyV_0 presented as the source nameLocals have types in StandAloneSig and names only in a PDB that DebugType none did not write
Reader guesses an unknown opcode or type codeA plausible mnemonicFormatError with the offset, and the oracle comparison stops there
Oracle captured from another buildTests pass on stale textbuild_fixtures.sh names every tool version and writes versions.txt and sha256.txt

قاعده عملیاتی

اول عنوان را بخوانید و نام تولید کننده را که به آن اشاره می کند بخوانید. با یک خواننده که آنچه را که نمی داند را رد می کند، کنتینر را بخوانید و لیست آن را با ابزار فرمت خود در همان فایل بررسی کنید. سپس بگویید، برای هر نام و نوع در محصول یک کپیک، چه کنتینر آن را ذخیره کرده است یا ابزار آن را نتیجه گرفت.

خواندن بیشتر

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.