Skip to main content

AutoItBinary

Struct AutoItBinary 

Source
pub struct AutoItBinary { /* private fields */ }
Expand description

High-level view of an AutoIt payload.

Produced by AutoItBinary::try_parse, this type owns every recovered view: the recognized container facts and analysis observations, the parsed AU3 records, any recovered scripts and non-script artifacts, and raw strings found in the payload bytes. Each view is exposed through an accessor and kept additive, so partial recovery from a malformed sample still yields everything decoded before the failure.

Implementations§

Source§

impl AutoItBinary

Source

pub fn try_parse(data: &[u8]) -> Result<Self, Error>

Attempts to parse an AutoIt payload from bytes.

Recognizes the container (PE, .a3x, or a carved record stream) and its encoding, then parses the AU3 record stream when one is located: decrypting and decompressing record payloads, recovering scripts (including EA06 detokenization), preserving non-script artifacts, and extracting raw strings. Record parsing is tolerant — records recovered before a later malformed record remain available via Self::records, with the failure reported in Self::record_diagnostics.

§Errors

Returns Error when the input is not recognized as AutoIt, or when a recognized container is too malformed to begin record parsing. Use Error::recognition_failure to distinguish the cause.

Examples found in repository?
examples/dump.rs (line 32)
7fn main() -> ExitCode {
8    let mut args = env::args_os();
9    let _program = args.next();
10    let mut path = None;
11    for arg in args {
12        if path.is_none() {
13            path = Some(arg);
14        } else {
15            eprintln!("usage: dump <autoit-exe-or-a3x>");
16            return ExitCode::FAILURE;
17        }
18    }
19    let Some(path) = path else {
20        eprintln!("usage: dump <autoit-exe-or-a3x>");
21        return ExitCode::FAILURE;
22    };
23
24    let data = match fs::read(&path) {
25        Ok(data) => data,
26        Err(err) => {
27            eprintln!("failed to read input: {err}");
28            return ExitCode::FAILURE;
29        }
30    };
31
32    match AutoItBinary::try_parse(&data) {
33        Ok(binary) => {
34            print_text(&binary);
35            ExitCode::SUCCESS
36        }
37        Err(err) if err.recognition_failure() == Some(RecognitionFailure::NotRecognized) => {
38            eprintln!("not recognized as AutoIt");
39            ExitCode::FAILURE
40        }
41        Err(err) => {
42            eprintln!("failed to parse AutoIt payload: {err}");
43            ExitCode::FAILURE
44        }
45    }
46}
Source

pub const fn input_kind(&self) -> InputKind

Returns the recognized input container kind.

§Returns

The InputKind determined during analysis (InputKind::Pe, InputKind::A3x, or InputKind::RawStream).

Examples found in repository?
examples/dump.rs (line 61)
59fn print_overview(binary: &AutoItBinary) {
60    section("overview");
61    field("input kind", format!("{:?}", binary.input_kind()));
62    field("encoding", opt_debug(binary.encoding()));
63    field("records", binary.records().len().to_string());
64    field("scripts", binary.scripts().len().to_string());
65    field("artifacts", binary.artifacts().len().to_string());
66    field("strings", binary.strings().len().to_string());
67    field(
68        "record diagnostics",
69        binary.record_diagnostics().len().to_string(),
70    );
71}
Source

pub const fn encoding(&self) -> Option<Encoding>

Returns the recognized AutoIt payload encoding, if known.

§Returns

Some with the recognized Encoding, or None when no encoding could be determined from the container.

Examples found in repository?
examples/dump.rs (line 62)
59fn print_overview(binary: &AutoItBinary) {
60    section("overview");
61    field("input kind", format!("{:?}", binary.input_kind()));
62    field("encoding", opt_debug(binary.encoding()));
63    field("records", binary.records().len().to_string());
64    field("scripts", binary.scripts().len().to_string());
65    field("artifacts", binary.artifacts().len().to_string());
66    field("strings", binary.strings().len().to_string());
67    field(
68        "record diagnostics",
69        binary.record_diagnostics().len().to_string(),
70    );
71}
Source

pub const fn container(&self) -> &ContainerInfo

Returns summarized container-level facts.

§Returns

A reference to the ContainerInfo built from the recorded observations.

Examples found in repository?
examples/dump.rs (line 74)
73fn print_container(binary: &AutoItBinary) {
74    let container = binary.container();
75    section("container");
76    field("input kind", format!("{:?}", container.input_kind()));
77    field("encoding", opt_debug(container.encoding()));
78    field(
79        "autoit signature offset",
80        container
81            .autoit_signature_offset()
82            .map_or_else(|| "none".to_string(), |offset| format!("{offset}")),
83    );
84    field(
85        "version marker",
86        container.version_marker().map_or_else(
87            || "none".to_string(),
88            |marker| format!("{:?} @ offset {}", marker.encoding, marker.offset),
89        ),
90    );
91    field(
92        "payload stream offsets",
93        if container.payload_stream_offsets().is_empty() {
94            "none".to_string()
95        } else {
96            format!("{:?}", container.payload_stream_offsets())
97        },
98    );
99
100    match container.pe_script_resource() {
101        None => field("pe script resource", "none".to_string()),
102        Some(resource) => {
103            println!("  pe script resource:");
104            subfield(
105                "type",
106                match resource.type_name {
107                    Some(name) => format!("{} ({name})", resource.type_id),
108                    None => resource.type_id.to_string(),
109                },
110            );
111            subfield("name", escape_inline(resource.name));
112            subfield(
113                "language id",
114                resource
115                    .language_id
116                    .map_or_else(|| "none".to_string(), |id| id.to_string()),
117            );
118            subfield("rva", format!("{:#x}", resource.rva));
119            subfield("file offset", resource.offset.to_string());
120            subfield("size", format!("{} bytes", resource.size));
121        }
122    }
123
124    if container.packed_markers().is_empty() {
125        field("packed markers", "none".to_string());
126    } else {
127        println!("  packed markers:");
128        for marker in container.packed_markers() {
129            println!("    {} @ offset {}", marker.name, marker.offset);
130        }
131    }
132}
Source

pub const fn observations(&self) -> &ObservationLog

Returns the analysis observations.

§Returns

A reference to the ObservationLog holding the concrete observations recorded during analysis.

Examples found in repository?
examples/dump.rs (line 135)
134fn print_observations(binary: &AutoItBinary) {
135    let observations = binary.observations().entries();
136    section(&format!("observations ({})", observations.len()));
137    for observation in observations {
138        println!("  {observation:?}");
139    }
140}
Source

pub fn records(&self) -> &[Record]

Returns parsed AU3 records.

Records recovered before any later malformed record remain present here; the failure is reported separately via Self::record_diagnostics.

§Returns

A slice of the parsed Record values, empty when no record stream was located.

Examples found in repository?
examples/dump.rs (line 52)
48fn print_text(binary: &AutoItBinary) {
49    print_overview(binary);
50    print_container(binary);
51    print_observations(binary);
52    print_records(binary.records());
53    print_record_diagnostics(binary);
54    print_scripts(binary.scripts());
55    print_artifacts(binary.artifacts());
56    print_strings(binary.strings());
57}
58
59fn print_overview(binary: &AutoItBinary) {
60    section("overview");
61    field("input kind", format!("{:?}", binary.input_kind()));
62    field("encoding", opt_debug(binary.encoding()));
63    field("records", binary.records().len().to_string());
64    field("scripts", binary.scripts().len().to_string());
65    field("artifacts", binary.artifacts().len().to_string());
66    field("strings", binary.strings().len().to_string());
67    field(
68        "record diagnostics",
69        binary.record_diagnostics().len().to_string(),
70    );
71}
Source

pub fn record_diagnostics(&self) -> &[RecordParseDiagnostic]

Returns diagnostics from tolerant AU3 record parsing.

§Returns

A slice of RecordParseDiagnostic entries describing records that failed to parse; empty when every record parsed cleanly.

Examples found in repository?
examples/dump.rs (line 69)
59fn print_overview(binary: &AutoItBinary) {
60    section("overview");
61    field("input kind", format!("{:?}", binary.input_kind()));
62    field("encoding", opt_debug(binary.encoding()));
63    field("records", binary.records().len().to_string());
64    field("scripts", binary.scripts().len().to_string());
65    field("artifacts", binary.artifacts().len().to_string());
66    field("strings", binary.strings().len().to_string());
67    field(
68        "record diagnostics",
69        binary.record_diagnostics().len().to_string(),
70    );
71}
72
73fn print_container(binary: &AutoItBinary) {
74    let container = binary.container();
75    section("container");
76    field("input kind", format!("{:?}", container.input_kind()));
77    field("encoding", opt_debug(container.encoding()));
78    field(
79        "autoit signature offset",
80        container
81            .autoit_signature_offset()
82            .map_or_else(|| "none".to_string(), |offset| format!("{offset}")),
83    );
84    field(
85        "version marker",
86        container.version_marker().map_or_else(
87            || "none".to_string(),
88            |marker| format!("{:?} @ offset {}", marker.encoding, marker.offset),
89        ),
90    );
91    field(
92        "payload stream offsets",
93        if container.payload_stream_offsets().is_empty() {
94            "none".to_string()
95        } else {
96            format!("{:?}", container.payload_stream_offsets())
97        },
98    );
99
100    match container.pe_script_resource() {
101        None => field("pe script resource", "none".to_string()),
102        Some(resource) => {
103            println!("  pe script resource:");
104            subfield(
105                "type",
106                match resource.type_name {
107                    Some(name) => format!("{} ({name})", resource.type_id),
108                    None => resource.type_id.to_string(),
109                },
110            );
111            subfield("name", escape_inline(resource.name));
112            subfield(
113                "language id",
114                resource
115                    .language_id
116                    .map_or_else(|| "none".to_string(), |id| id.to_string()),
117            );
118            subfield("rva", format!("{:#x}", resource.rva));
119            subfield("file offset", resource.offset.to_string());
120            subfield("size", format!("{} bytes", resource.size));
121        }
122    }
123
124    if container.packed_markers().is_empty() {
125        field("packed markers", "none".to_string());
126    } else {
127        println!("  packed markers:");
128        for marker in container.packed_markers() {
129            println!("    {} @ offset {}", marker.name, marker.offset);
130        }
131    }
132}
133
134fn print_observations(binary: &AutoItBinary) {
135    let observations = binary.observations().entries();
136    section(&format!("observations ({})", observations.len()));
137    for observation in observations {
138        println!("  {observation:?}");
139    }
140}
141
142fn print_records(records: &[Record]) {
143    section(&format!("records ({})", records.len()));
144    for record in records {
145        println!("  #{} @ offset {}", record.index(), record.offset());
146        subfield("subtype", escape_inline(record.subtype()));
147        subfield("name", escape_inline(record.name()));
148        let profile = record.profile();
149        subfield(
150            "profile",
151            format!(
152                "encoding={:?} encryption={:?} compression={:?}",
153                profile.encoding, profile.encryption, profile.compression
154            ),
155        );
156        subfield(
157            "compressed",
158            format!(
159                "{} (compressed_size={}, uncompressed_size={})",
160                record.compressed(),
161                record.compressed_size(),
162                record.uncompressed_size()
163            ),
164        );
165        subfield(
166            "checksum",
167            format!(
168                "{:#010x} ({})",
169                record.checksum(),
170                if record.checksum_valid() {
171                    "valid"
172                } else {
173                    "invalid"
174                }
175            ),
176        );
177        subfield(
178            "timestamps",
179            format!(
180                "created={} last_write={}",
181                record.creation_time(),
182                record.last_write_time()
183            ),
184        );
185        subfield(
186            "data sizes",
187            format!(
188                "encrypted={} decrypted={} decompressed={} payload={}",
189                record.encrypted_data().len(),
190                record.decrypted_data().len(),
191                record
192                    .decompressed_data()
193                    .map_or_else(|| "none".to_string(), |data| data.len().to_string()),
194                record.payload_data().len()
195            ),
196        );
197        subfield(
198            "decompression",
199            format!("{:?}", record.decompression_status()),
200        );
201    }
202}
203
204fn print_record_diagnostics(binary: &AutoItBinary) {
205    let diagnostics = binary.record_diagnostics();
206    if diagnostics.is_empty() {
207        return;
208    }
209    section(&format!("record diagnostics ({})", diagnostics.len()));
210    for diagnostic in diagnostics {
211        println!(
212            "  record #{} @ offset {}: {:?}",
213            diagnostic.record_index, diagnostic.offset, diagnostic.reason
214        );
215    }
216}
Source

pub fn scripts(&self) -> &[Script]

Returns recovered script records.

§Returns

A slice of recovered Script views; empty when no script records were found.

Examples found in repository?
examples/dump.rs (line 54)
48fn print_text(binary: &AutoItBinary) {
49    print_overview(binary);
50    print_container(binary);
51    print_observations(binary);
52    print_records(binary.records());
53    print_record_diagnostics(binary);
54    print_scripts(binary.scripts());
55    print_artifacts(binary.artifacts());
56    print_strings(binary.strings());
57}
58
59fn print_overview(binary: &AutoItBinary) {
60    section("overview");
61    field("input kind", format!("{:?}", binary.input_kind()));
62    field("encoding", opt_debug(binary.encoding()));
63    field("records", binary.records().len().to_string());
64    field("scripts", binary.scripts().len().to_string());
65    field("artifacts", binary.artifacts().len().to_string());
66    field("strings", binary.strings().len().to_string());
67    field(
68        "record diagnostics",
69        binary.record_diagnostics().len().to_string(),
70    );
71}
Source

pub fn artifacts(&self) -> &[Artifact]

Returns recovered non-script artifact records.

§Returns

A slice of Artifact views for every record that was not recovered as a Script; empty when there are none.

Examples found in repository?
examples/dump.rs (line 55)
48fn print_text(binary: &AutoItBinary) {
49    print_overview(binary);
50    print_container(binary);
51    print_observations(binary);
52    print_records(binary.records());
53    print_record_diagnostics(binary);
54    print_scripts(binary.scripts());
55    print_artifacts(binary.artifacts());
56    print_strings(binary.strings());
57}
58
59fn print_overview(binary: &AutoItBinary) {
60    section("overview");
61    field("input kind", format!("{:?}", binary.input_kind()));
62    field("encoding", opt_debug(binary.encoding()));
63    field("records", binary.records().len().to_string());
64    field("scripts", binary.scripts().len().to_string());
65    field("artifacts", binary.artifacts().len().to_string());
66    field("strings", binary.strings().len().to_string());
67    field(
68        "record diagnostics",
69        binary.record_diagnostics().len().to_string(),
70    );
71}
Source

pub fn strings(&self) -> &[StringFinding]

Returns raw strings found in recovered record payload bytes.

§Returns

A slice of StringFinding values extracted from record payloads; empty when none were found.

Examples found in repository?
examples/dump.rs (line 56)
48fn print_text(binary: &AutoItBinary) {
49    print_overview(binary);
50    print_container(binary);
51    print_observations(binary);
52    print_records(binary.records());
53    print_record_diagnostics(binary);
54    print_scripts(binary.scripts());
55    print_artifacts(binary.artifacts());
56    print_strings(binary.strings());
57}
58
59fn print_overview(binary: &AutoItBinary) {
60    section("overview");
61    field("input kind", format!("{:?}", binary.input_kind()));
62    field("encoding", opt_debug(binary.encoding()));
63    field("records", binary.records().len().to_string());
64    field("scripts", binary.scripts().len().to_string());
65    field("artifacts", binary.artifacts().len().to_string());
66    field("strings", binary.strings().len().to_string());
67    field(
68        "record diagnostics",
69        binary.record_diagnostics().len().to_string(),
70    );
71}

Trait Implementations§

Source§

impl Clone for AutoItBinary

Source§

fn clone(&self) -> AutoItBinary

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for AutoItBinary

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

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> 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<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

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> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
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.