1use idakit::prelude::*;
15
16fn main() -> Result<(), Box<dyn std::error::Error>> {
17 let db = std::env::args()
18 .nth(1)
19 .expect("usage: edits <path/to/database.i64>");
20
21 Ida::run(move |ida| -> Result<(), Error> {
22 ida.call(move |idb| -> Result<(), Error> {
23 idb.open(&db).call()?;
24
25 let entry = idb
26 .functions()
27 .next()
28 .expect("the database has a function")
29 .address();
30
31 name_and_comment(idb, entry)?;
32 function_types(idb, entry)?;
33 signature_surgery(idb, entry)?;
34 defining_types(idb)?;
35 editing_members(idb)?;
36 building_types(idb, entry);
37 classifier_and_errors(idb, entry);
38
39 idb.close(false);
40 println!("\nEDITS OK");
41 Ok(())
42 })?
43 })??;
44
45 Ok(())
46}
47
48fn name_and_comment(idb: &mut Database, ea: Address) -> Result<(), Error> {
51 println!("== at_mut: rename + comment + patch (one read-capable cursor) ==");
52
53 let original_name = idb.at(ea).name();
55 println!(" {ea:#x} name before: {original_name:?}");
56
57 {
58 let mut loc = idb.at_mut(ea);
60 loc.rename("idakit_edit_demo")?;
61 loc.set_comment("renamed by the edits example", false)?;
62 loc.set_comment("shown at every xref", true)?;
63 println!(" name after: {:?}", loc.name());
65 println!(" regular cmt: {:?}", loc.comment());
66 println!(" repeatable cmt: {:?}", loc.repeatable_comment());
67 }
68
69 let original_bytes = idb.at(ea).bytes(4);
71 let flipped: Vec<u8> = original_bytes.iter().map(|b| !b).collect();
72 {
73 let mut loc = idb.at_mut(ea);
74 loc.patch(&flipped)?;
75 println!(
76 " patched 4 bytes, read-back matches: {}",
77 loc.bytes(4) == flipped
78 );
79 loc.patch(&original_bytes)?;
80 }
81
82 if let Some(name) = original_name {
83 idb.at_mut(ea).rename(name)?;
84 }
85 Ok(())
86}
87
88fn function_types(idb: &mut Database, ea: Address) -> Result<(), Error> {
92 println!("\n== function_mut: prototypes ==");
93 println!(" prototype before: {:?}", idb.function(ea).prototype());
94
95 if let Some(mut f) = idb.function_mut(ea) {
98 f.set_type("int edits_probe(int a, int b)")?;
99 }
100 println!(" prototype after: {:?}", idb.function(ea).prototype());
101
102 idb.with_function_mut(ea, |f| f.set_type("void edits_probe(void)"))
105 .transpose()?;
106 println!(" prototype now: {:?}", idb.function(ea).prototype());
107
108 let nowhere = Address::new_const(0xffff_ffff_f000);
110 println!(
111 " function_mut(unmapped) is_some: {}",
112 idb.function_mut(nowhere).is_some()
113 );
114
115 idb.at_mut(ea).clear_type()?;
121 println!(
122 " prototype after clear: {:?}",
123 idb.function(ea).prototype()
124 );
125 Ok(())
126}
127
128fn signature_surgery(idb: &mut Database, ea: Address) -> Result<(), Error> {
131 println!("\n== function_mut: signature surgery ==");
132
133 if let Some(mut f) = idb.function_mut(ea) {
134 f.set_type("int surgery_probe(int a, int b)")?;
135 }
136 println!(" seeded: {:?}", idb.function(ea).prototype());
137
138 if let Some(mut f) = idb.function_mut(ea) {
140 f.set_return_type(expr::char_().pointer())?; f.set_arg_type(0, expr::decl("unsigned int"))?; f.rename_arg(1, "count")?; f.prepend_this(expr::void().pointer())?; f.set_calling_convention(CallingConvention::Cdecl)?;
145 }
146 println!(" after surgery: {:?}", idb.function(ea).prototype());
147
148 if let Some(mut f) = idb.function_mut(ea)
150 && let Err(Error::TypeWrite { source }) = f.set_arg_type(99, expr::int32())
151 {
152 println!(" set_arg_type(99, ..) -> TypeWrite: {source}");
153 }
154 Ok(())
155}
156
157fn defining_types(idb: &mut Database) -> Result<(), Error> {
159 println!("\n== types_mut: define ==");
160
161 idb.types_mut()
162 .define("struct edit_demo_t { int id; char *name; };")?;
163 let present = idb.named_types().any(|t| t.name() == "edit_demo_t");
164 println!(" defined edit_demo_t, present in named types: {present}");
165
166 match idb
168 .types_mut()
169 .define("typedef struct edit_demo_t edit_demo_alias;")
170 {
171 Ok(()) => println!(" typedef alias to it: ok"),
172 Err(e) => println!(" typedef alias: {e}"),
173 }
174
175 match idb.types_mut().define("struct broken { not valid") {
177 Err(Error::TypeDefineFailed { reason, .. }) => {
178 println!(" malformed define -> TypeDefineFailed: {reason}");
179 }
180 other => println!(" malformed define -> unexpected {other:?}"),
181 }
182 Ok(())
183}
184
185fn editing_members(idb: &mut Database) -> Result<(), Error> {
190 use idakit::types::expr;
191
192 println!("\n== types_mut: edit members ==");
193
194 idb.types_mut()
195 .define("struct edit_member_demo { int a; int b; };")?;
196
197 idb.types_mut()
198 .edit("edit_member_demo")
199 .add_member("c", expr::int32())?;
200 idb.types_mut()
201 .edit("edit_member_demo")
202 .member("a")
203 .set_type(expr::char_())?;
204 idb.types_mut()
205 .edit("edit_member_demo")
206 .member("b")
207 .rename("beta")?;
208
209 let names: Vec<String> = idb
210 .type_named("edit_member_demo")?
211 .members()
212 .unwrap_or_default()
213 .iter()
214 .map(|m| m.name.clone())
215 .collect();
216 println!(" after add + retype + rename, members: {names:?}");
217
218 match idb
220 .types_mut()
221 .edit("edit_member_demo")
222 .member("c")
223 .rename("a")
224 {
225 Err(Error::TypeWrite {
226 source: TypeWriteError::Rejected { code, .. },
227 }) => println!(" duplicate rename -> Rejected({code})"),
228 other => println!(" duplicate rename -> unexpected {other:?}"),
229 }
230
231 idb.types_mut()
233 .define("enum edit_enum_demo { DEMO_A = 1, DEMO_B = 2 };")?;
234 idb.types_mut()
235 .edit("edit_enum_demo")
236 .add_constant("DEMO_C", 3)?;
237 idb.types_mut()
238 .edit("edit_enum_demo")
239 .constant("DEMO_A")
240 .set_value(10)?;
241 let constants: Vec<(String, u64)> = match idb.type_named("edit_enum_demo")?.shape() {
242 TypeShape::Enum { members, .. } => {
243 members.iter().map(|m| (m.name.clone(), m.value)).collect()
244 }
245 _ => Vec::new(),
246 };
247 println!(" after add + revalue, constants: {constants:?}");
248 Ok(())
249}
250
251fn building_types(idb: &mut Database, ea: Address) {
256 println!("\n== TypeExpr builder: compose + apply ==");
257
258 let uint_array = expr::uint32().array(4); let ptr_to_named = expr::named("edit_demo_t").pointer(); let const_ptr = expr::int32().const_().pointer(); println!(" uint32().array(4) -> {uint_array:<14} ({uint_array:?})");
263 println!(" named(\"edit_demo_t\").pointer() -> {ptr_to_named}");
264 println!(" int32().const_().pointer() -> {const_ptr}");
265
266 println!(
268 " (edit_demo_t *).deref() == named: {}",
269 ptr_to_named.clone().deref() == expr::named("edit_demo_t")
270 );
271
272 report(
275 "set_type(uint32().array(4))",
276 idb.at_mut(ea).set_type(uint_array),
277 );
278 report(
279 "set_type(named(..).pointer())",
280 idb.at_mut(ea).set_type(ptr_to_named),
281 );
282
283 report(
286 "set_type(named(\"no_such\").pointer())",
287 idb.at_mut(ea)
288 .set_type(expr::named("no_such_zzz").pointer()),
289 );
290
291 let proto = expr::function(expr::int32())
293 .arg(expr::int32())
294 .named_arg("flags", expr::uint32())
295 .variadic()
296 .build();
297 println!(" function(int32).arg(int32).named_arg(\"flags\", uint32).variadic -> {proto}");
298 report(
299 "function_mut(entry).set_type(built prototype)",
300 idb.function_mut(ea)
301 .map_or(Ok(()), |mut f| f.set_type(proto)),
302 );
303}
304
305fn classifier_and_errors(idb: &mut Database, ea: Address) {
309 println!("\n== the &str classifier + type errors ==");
310
311 println!(" \"edit_demo_t\" -> {:?}", TypeExpr::from("edit_demo_t"));
313 println!(
314 " \"edit_demo_t *\" -> {:?}",
315 TypeExpr::from("edit_demo_t *")
316 );
317 println!(
318 " \"int\" -> {:?} (a keyword: parsed, not looked up)",
319 TypeExpr::from("int")
320 );
321
322 report(
324 "set_type(\"no_such_type_xyz\")",
325 idb.at_mut(ea).set_type("no_such_type_xyz"),
326 );
327
328 report(
331 "set_type(named(\"int\"))",
332 idb.at_mut(ea).set_type(expr::named("int")),
333 );
334
335 report(
337 "set_type(decl(\"%%% junk %%%\"))",
338 idb.at_mut(ea).set_type(expr::decl("%%% junk %%%")),
339 );
340
341 report("set_type(\"int\")", idb.at_mut(ea).set_type("int"));
344}
345
346fn report(call: &str, r: Result<(), Error>) {
348 match r {
349 Ok(()) => println!(" {call} -> Ok"),
350 Err(Error::TypeWrite {
351 source: TypeWriteError::NoType { name },
352 }) => {
353 println!(" {call} -> NoType {{ name: {name:?} }}");
354 }
355 Err(Error::TypeWrite {
356 source: TypeWriteError::ParseFailed { reason, .. },
357 }) => {
358 println!(" {call} -> ParseFailed: {reason}");
359 }
360 Err(Error::TypeWrite {
361 source: TypeWriteError::ApplyRejected { reason, .. },
362 }) => {
363 println!(" {call} -> ApplyRejected: {reason}");
364 }
365 Err(e) => println!(" {call} -> {e}"),
366 }
367}