├── README.md ├── examples ├── example.bin ├── example.bin.i64 ├── example2.bin └── example2.bin.i64 ├── img └── example.png └── src ├── CMakeLists.txt └── instrlen.cpp /README.md: -------------------------------------------------------------------------------- 1 | # Instrlen Plugin 2 | 3 | The Instrlen plugin is a tool for IDA Pro that allows for setting the length of an instruction to a custom value. This can be useful when the code is obfuscated or there are jumps after the instruction prefixes. 4 | 5 | 6 | ## Installation 7 | 8 | To install the Instrlen plugin, follow these steps: 9 | 10 | 1. Copy the `instrlen.(dll|dylib)` file to the IDA plugins folder. 11 | 2. Launch IDA Pro. 12 | 3. Go to the instruction you want to change the length of. 13 | 4. Navigate to the `Edit/Plugins` menu. 14 | 5. Choose `Change Instruction Length` from the list of plugins and enter the desired length. 15 | 16 | ## Building the Plugin 17 | 18 | To build the Instrlen plugin, you will need to use the [ida-cmake](https://github.com/allthingsida/ida-cmake) build system. 19 | 20 | Please refer to the ida-cmake documentation for instructions on how to set up and use the build system. 21 | 22 | Once you have set up the ida-cmake build system, you can build the Instrlen plugin by running the following commands: 23 | 24 | ``` 25 | git clone https://github.com/milankovo/instrlen 26 | cmake -B build64 -DEA64=YES -S src/ 27 | cmake --build build64 --config Release 28 | ``` 29 | 30 | Note: The Instrlen plugin is compatible with IDA Pro 9.0. While it has been tested with this version, it might be possible to use it with previous versions of IDA Pro. 31 | 32 | ## Demonstration 33 | 34 | ![plugin in action](img/example.png) -------------------------------------------------------------------------------- /examples/example.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milankovo/instrlen/f0957b674d74695f37bae5908257c9678415a945/examples/example.bin -------------------------------------------------------------------------------- /examples/example.bin.i64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milankovo/instrlen/f0957b674d74695f37bae5908257c9678415a945/examples/example.bin.i64 -------------------------------------------------------------------------------- /examples/example2.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milankovo/instrlen/f0957b674d74695f37bae5908257c9678415a945/examples/example2.bin -------------------------------------------------------------------------------- /examples/example2.bin.i64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milankovo/instrlen/f0957b674d74695f37bae5908257c9678415a945/examples/example2.bin.i64 -------------------------------------------------------------------------------- /img/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milankovo/instrlen/f0957b674d74695f37bae5908257c9678415a945/img/example.png -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12 FATAL_ERROR) 2 | 3 | project(instrlen) 4 | 5 | # Plugin 1 6 | set(CMAKE_CXX_STANDARD 17) 7 | 8 | include($ENV{IDASDK}/ida-cmake/common.cmake) 9 | 10 | set(PLUGIN_NAME instrlen) 11 | set(PLUGIN_SOURCES instrlen.cpp) 12 | set(PLUGIN_RUN_ARGS "-t -z10000") # Debug messages for the debugger 13 | generate() 14 | disable_ida_warnings(instrlen) 15 | -------------------------------------------------------------------------------- /src/instrlen.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Change the length of current instruction 3 | * 4 | * 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | const char *const cref_to_str(uchar cr) 16 | { 17 | switch (cr) 18 | { 19 | case fl_U: 20 | return "fl_U"; 21 | case fl_CF: 22 | return "fl_CF"; 23 | case fl_CN: 24 | return "fl_CN"; 25 | case fl_JF: 26 | return "fl_JF"; 27 | case fl_JN: 28 | return "fl_JN"; 29 | case fl_USobsolete: 30 | return "fl_USobsolete"; 31 | case fl_F: 32 | return "fl_F"; 33 | default: 34 | return "unknown"; 35 | } 36 | } 37 | 38 | struct instruction_resizer_t : public plugmod_t, public post_event_visitor_t 39 | { 40 | netnode new_sizes = netnode("$ instruction new sizes", 0, true); 41 | netnode original_sizes = netnode("$ instruction original sizes", 0, true); 42 | 43 | instruction_resizer_t() 44 | { 45 | register_post_event_visitor(HT_IDP, this, this); 46 | 47 | // dump all new sizes 48 | } 49 | 50 | void dump_sizes() 51 | { 52 | qstring name; 53 | 54 | msg("new sizes:\n"); 55 | new_sizes.get_name(&name); 56 | msg("new sizes name: %s\n", name.c_str()); 57 | for (ea_t ea = new_sizes.altfirst(); ea != BADADDR; ea = new_sizes.altnext(ea)) 58 | { 59 | msg("\tnew size at %llx is %llx\n", ea, new_sizes.altval_ea(ea)); 60 | } 61 | // dump all original sizes 62 | msg("original sizes:\n"); 63 | original_sizes.get_name(&name); 64 | msg("original sizes name: %s\n", name.c_str()); 65 | for (ea_t ea = original_sizes.altfirst(); ea != BADADDR; ea = original_sizes.altnext(ea)) 66 | { 67 | msg("\toriginal size at %llx is %llx\n", ea, original_sizes.altval_ea(ea)); 68 | } 69 | } 70 | 71 | ~instruction_resizer_t() 72 | { 73 | unregister_post_event_visitor(HT_IDP, this); 74 | } 75 | 76 | virtual bool idaapi run(size_t arg) override; 77 | 78 | virtual ssize_t idaapi handle_post_event(ssize_t code, int notification_code, va_list va) override; 79 | }; 80 | 81 | ssize_t idaapi instruction_resizer_t::handle_post_event(ssize_t retcode, int notification_code, va_list va) 82 | { 83 | switch (notification_code) 84 | { 85 | case processor_t::ev_ana_insn: 86 | ///< Analyze one instruction and fill 'out' structure. 87 | ///< This function shouldn't change the database, flags or anything else. 88 | ///< All these actions should be performed only by emu_insn() function. 89 | ///< \insn_t{ea} contains address of instruction to analyze. 90 | ///< \param out (::insn_t *) 91 | ///< \return length of the instruction in bytes, 0 if instruction can't be decoded. 92 | ///< \retval 0 if instruction can't be decoded.: 93 | { 94 | insn_t *insn = va_arg(va, insn_t *); 95 | if (insn && (retcode > 0)) 96 | { 97 | auto new_size = new_sizes.altval_ea(insn->ea); 98 | if (!new_size) 99 | break; 100 | 101 | msg("shortening instruction at %llx to %llu\n", insn->ea, new_size); 102 | original_sizes.altset_ea(insn->ea, insn->size); 103 | insn->size = new_size; 104 | 105 | for (int i = 0; i < 8; ++i) 106 | insn->ops[i].offb = 0; 107 | return insn->size; 108 | } 109 | } 110 | break; 111 | 112 | case processor_t::ev_emu_insn: 113 | { 114 | 115 | ///< Emulate instruction, create cross-references, plan to analyze 116 | ///< subsequent instructions, modify flags etc. Upon entrance to this function, 117 | ///< subsequent instructions, modify flags etc. Upon entrance to this function, 118 | ///< all information about the instruction is in 'insn' structure. 119 | ///< \param insn (const ::insn_t *) 120 | ///< \retval 1 ok 121 | ///< \retval -1 the kernel will delete the instruction 122 | 123 | insn_t *insn = va_arg(va, insn_t *); 124 | 125 | if (insn && (retcode == 1)) 126 | { 127 | 128 | auto new_size = new_sizes.altval_ea(insn->ea); 129 | if (!new_size) 130 | break; 131 | 132 | auto orig_size = original_sizes.altval_ea(insn->ea); 133 | if (orig_size == 0) 134 | { 135 | msg("no orig size of instr at %llx\n", insn->ea); 136 | break; 137 | } 138 | 139 | // ida most likely created a flow from the original instruction to the new one 140 | // we need to remove it 141 | 142 | bool del_flow = false; 143 | // print all crefs from current instruction 144 | auto ea_original_flow = insn->ea + orig_size; 145 | auto ea_new_flow = insn->ea + new_size; 146 | if (1) 147 | { 148 | msg("crefs from %llx\n", insn->ea); 149 | auto i = 0; 150 | /*for (auto ea = get_first_cref_from(insn->ea); ea != BADADDR; ea = get_next_cref_from(insn->ea, ea)) 151 | { 152 | msg("%d. cref from %llx to %llx\n", i++, insn->ea, ea); 153 | }*/ 154 | xrefblk_t xb; 155 | for (bool ok = xb.first_from(insn->ea, XREF_ALL); ok; ok = xb.next_from()) 156 | { 157 | // xb.to - contains the referenced address 158 | msg("%d. cref from %llx to %llx type: %d = %s user: %d iscode: %d\n", i++, xb.from, xb.to, xb.type, cref_to_str(xb.type), xb.user, xb.iscode); 159 | if (xb.type == fl_F && xb.to == ea_new_flow) 160 | { 161 | del_flow = true; 162 | } 163 | } 164 | } 165 | 166 | // msg("fixing crefs instruction at %x to %d -> %d\n", insn->ea, new_size, orig_size); 167 | 168 | // TODO: check whethere this flow exists 169 | if (del_flow) 170 | { 171 | del_cref(insn->ea, ea_new_flow, false); 172 | 173 | // add flow to the original "next" instruction 174 | auto ok3 = add_cref(insn->ea, ea_original_flow, fl_JN); 175 | if (!ok3) 176 | msg("failed to add fl_F cref from %llx to %llx\n", insn->ea, ea_original_flow); 177 | } 178 | 179 | if (0) 180 | { 181 | 182 | if (!is_head(get_flags(ea_original_flow))) 183 | { 184 | ea_t ea_1 = get_item_head(ea_original_flow); 185 | 186 | msg("[!] %llx's destination is inside instruction at %llx at %llx. Fixing...\n", insn->ea, ea_1, ea_original_flow); 187 | // new_sizes.altset_ea(ea_1, ea_original_flow - ea_1); 188 | // plan_ea(ea_1); 189 | } 190 | } 191 | } 192 | } 193 | break; 194 | } 195 | return retcode; 196 | } 197 | 198 | //-------------------------------------------------------------------------- 199 | static plugmod_t *idaapi init() 200 | { 201 | return new instruction_resizer_t(); 202 | } 203 | 204 | //-------------------------------------------------------------------------- 205 | static const char comment[] = "Change the callee address"; 206 | static const char help[] = 207 | "This plugin allows the user to change the length of the current instruction\n"; 208 | 209 | //-------------------------------------------------------------------------- 210 | static const char *const form = 211 | "HELP\n" 212 | "%s\n" 213 | "ENDHELP\n" 214 | "Enter the new length\n" 215 | "\n" 216 | " <~N~ew length:L::40:::>\n" 217 | "\n" 218 | "\n"; 219 | 220 | bool idaapi instruction_resizer_t::run(size_t) 221 | { 222 | ea_t ea = get_screen_ea(); // get current address 223 | if (!is_code(get_flags(ea))) 224 | return false; // not an instruction 225 | // get the callee address from the database 226 | // ea_t callee = node2ea(n.altval_ea(ea) - 1); 227 | 228 | ea_t size = new_sizes.altval_ea(ea); 229 | 230 | if (size == 0) 231 | size = get_item_size(ea); 232 | 233 | char buf[MAXSTR]; 234 | qsnprintf(buf, sizeof(buf), form, help); 235 | if (ask_form(buf, &size) > 0) 236 | { 237 | if (size == BADADDR) 238 | { 239 | // msg("removing new size for instr at %llx", ea); 240 | new_sizes.altdel_ea(ea); 241 | original_sizes.altdel_ea(ea); 242 | } 243 | else 244 | { 245 | msg("setting the new size of instruction at %llx to %llx\n", ea, size); 246 | new_sizes.altset_ea(ea, size); 247 | // original_sizes.altdel_ea(ea); 248 | } 249 | plan_ea(ea); // reanalyze the current instruction 250 | } 251 | else 252 | { 253 | // msg("ask form failed\n"); 254 | } 255 | 256 | return true; 257 | } 258 | 259 | //-------------------------------------------------------------------------- 260 | static const char wanted_name[] = "Change instruction length"; 261 | static const char wanted_hotkey[] = "Alt-F12"; 262 | 263 | //-------------------------------------------------------------------------- 264 | // 265 | // PLUGIN DESCRIPTION BLOCK 266 | // 267 | //-------------------------------------------------------------------------- 268 | plugin_t PLUGIN = 269 | { 270 | IDP_INTERFACE_VERSION, 271 | PLUGIN_MULTI, // plugin flags 272 | init, // initialize 273 | 274 | nullptr, // terminate. this pointer may be nullptr. 275 | nullptr, // invoke plugin 276 | 277 | comment, // long comment about the plugin 278 | // it could appear in the status line 279 | // or as a hint 280 | 281 | help, // multiline help about the plugin 282 | 283 | wanted_name, // the preferred short name of the plugin 284 | wanted_hotkey // the preferred hotkey to run the plugin 285 | }; 286 | --------------------------------------------------------------------------------