Skip to main content

MethodBody

Struct MethodBody 

pub struct MethodBody {
    pub size_code: usize,
    pub size_header: usize,
    pub local_var_sig_token: u32,
    pub max_stack: usize,
    pub is_fat: bool,
    pub is_init_local: bool,
    pub is_exception_data: bool,
    pub exception_handlers: Vec<ExceptionHandler>,
}
Expand description

Describes one method that has been compiled to CIL bytecode.

The MethodBody struct represents the parsed body of a .NET method, including header information, code size, stack requirements, local variable signature, and exception handling regions.

§Header Format Detection

The structure automatically detects whether the method uses a tiny or fat header format based on the first byte(s) of the method body data:

  • Tiny Format: Single byte header for methods with ≤63 bytes of code
  • Fat Format: 12-byte header for complex methods with extensive metadata

§Field Organization

§Size Information

§Stack and Variables

§Format and Features

§Memory Layout

Tiny Header (1 byte):
┌─────────────────────────────────────┐
│ Size(6) │ Reserved(2) │ Format(2)   │
└─────────────────────────────────────┘

Fat Header (12 bytes):
┌─────────────────────────────────────┐
│ Flags(12) │ Size(4) │ Format(2)     │  (bytes 0-1)
├─────────────────────────────────────┤
│ MaxStack (16 bits)                  │  (bytes 2-3)
├─────────────────────────────────────┤
│ CodeSize (32 bits)                  │  (bytes 4-7)
├─────────────────────────────────────┤
│ LocalVarSigTok (32 bits)            │  (bytes 8-11)
└─────────────────────────────────────┘

§Thread Safety

MethodBody is fully thread-safe:

  • All fields are immutable after construction
  • No interior mutability or shared state
  • Safe to share across threads without synchronization

Fields§

§size_code: usize

Size of the method code (length of all CIL instructions, excluding the header) in bytes

§size_header: usize

Size of the method header in bytes (1 for tiny format, 12+ for fat format)

§local_var_sig_token: u32

Metadata token for a signature describing the layout of local variables (0 = no locals)

§max_stack: usize

Maximum number of items on the operand stack during method execution

§is_fat: bool

Flag indicating the method header format (false = tiny, true = fat)

§is_init_local: bool

Flag indicating whether to zero-initialize all local variables before method execution

§is_exception_data: bool

Flag indicating whether this method has exception handling data sections

§exception_handlers: Vec<ExceptionHandler>

Collection of exception handlers for this method (empty if no exception handling)

Implementations§

§

impl MethodBody

pub fn from(data: &[u8]) -> Result<MethodBody>

Create a MethodBody object from a sequence of bytes.

Parses a complete .NET method body including header, CIL instructions, and optional exception handling sections. Automatically detects and handles both tiny and fat header formats according to ECMA-335 specifications.

§Arguments
  • data - The byte slice containing the complete method body data, starting with the method header and including all CIL instructions and exception handling sections
§Returns
  • Ok - Successfully parsed method body with all metadata
  • Err - Parsing failed due to invalid format, insufficient data, or corruption
§Errors

This method returns an error in the following cases:

  • Empty Data: The provided byte slice is empty
  • Invalid Format: Header format bits don’t match tiny or fat patterns
  • Insufficient Data: Not enough bytes for the declared header or code size
  • Malformed Sections: Exception handling sections have invalid structure
§Examples
use dotscope::metadata::method::MethodBody;

let method_data = &[0x16, 0x00, 0x2A]; // Simple method: ldarg.0, ret
let body = MethodBody::from(method_data)?;

assert!(!body.is_fat);

pub fn from_lenient(data: &[u8]) -> Result<(MethodBody, usize)>

Create a MethodBody from bytes, filtering out invalid exception handlers.

This is useful for parsing method bodies with malformed exception handlers (e.g., injected by BitMono’s AntiDecompiler) where the handler offsets are garbage but the method header and IL code are valid. Invalid handlers are logged and skipped; only valid handlers are kept.

§Arguments
  • data - The byte slice containing the complete method body data
§Returns
  • Ok((MethodBody, usize)) - Successfully parsed method body with only valid exception handlers, and the count of invalid handlers that were filtered out
  • Err - Parsing failed due to invalid header format or insufficient data

pub fn from_raw(data: &[u8]) -> Result<MethodBody>

Parse without any EH validation. Returns all parsed handlers including garbage.

Used for obfuscation detection where we need to see raw handler data to identify injected garbage exception handlers.

§Arguments
  • data - The byte slice containing the complete method body data
§Returns
  • Ok - Successfully parsed method body with all handlers (unvalidated)
  • Err - Parsing failed due to invalid header format or insufficient data

pub fn size(&self) -> usize

Get the total serialized size including header, code, and exception handlers.

This calculates the complete size of the method body as it appears in the assembly file, including:

  • Method header (1 byte for tiny, 12 bytes for fat)
  • IL code bytes
  • 4-byte alignment padding (for fat format with exception handlers)
  • Exception handler sections
§Returns

The total serialized size in bytes.

pub fn write_to<W: Write>(&self, writer: &mut W, il_code: &[u8]) -> Result<u64>

Writes the method body to a writer.

Serializes the method header (tiny or fat format), IL code, and exception handlers directly to the provided writer. This avoids intermediate buffers for efficient streaming output.

§Arguments
  • writer - Any type implementing std::io::Write
  • il_code - The IL bytecode for the method
§Returns

The total number of bytes written (header + code + exception handlers + padding).

§Format Selection

The format is automatically determined based on method characteristics:

  • Tiny Format: Used when code size ≤ 63 bytes, no local variables, and no exceptions
  • Fat Format: Used for all other methods
§Examples
use dotscope::metadata::method::MethodBody;

let body = MethodBody {
    size_code: 2,
    size_header: 1,
    local_var_sig_token: 0,
    max_stack: 8,
    is_fat: false,
    is_init_local: false,
    is_exception_data: false,
    exception_handlers: vec![],
};

let il_code = vec![0x17, 0x2A]; // ldc.i4.1, ret
let mut buffer = Vec::new();
let bytes_written = body.write_to(&mut buffer, &il_code)?;

assert_eq!(bytes_written, 3); // 1-byte header + 2-byte code
§Errors

Returns an error if writing to the writer fails or if the method body parameters are invalid (e.g., code size exceeds limits).

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> AsAny for T
where T: Any,

Source§

fn as_any(&self) -> &(dyn Any + 'static)

Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Source§

fn type_name(&self) -> &'static str

Gets the type name of self
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> Downcast for T
where T: AsAny + ?Sized,

Source§

fn is<T>(&self) -> bool
where T: AsAny,

Returns true if the boxed type is the same as T. Read more
Source§

fn downcast_ref<T>(&self) -> Option<&T>
where T: AsAny,

Forward to the method defined on the type Any.
Source§

fn downcast_mut<T>(&mut self) -> Option<&mut T>
where T: AsAny,

Forward to the method defined on the type Any.
Source§

impl<T> ErasedDestructor for T
where T: 'static,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, A> IntoAst<A> for T
where T: Into<A>, A: Ast,

Source§

fn into_ast(self, _a: &A) -> A

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<F, T> IntoSample<T> for F
where T: FromSample<F>,

Source§

fn into_sample(self) -> T

Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<SS, SP> SupersetOf<SS> for SP
where SS: SubsetOf<SP>,

Source§

fn to_subset(&self) -> Option<SS>

The inverse inclusion map: attempts to construct self from the equivalent element of its superset. Read more
Source§

fn is_in_subset(&self) -> bool

Checks if self is actually part of its subset T (and can be converted to it).
Source§

fn to_subset_unchecked(&self) -> SS

Use with care! Same as self.to_subset but without any property checks. Always succeeds.
Source§

fn from_subset(element: &SS) -> SP

The inclusion map: converts self to the equivalent element of its superset.
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more