├── .gitignore ├── LICENSE ├── README.md ├── StackStringsDynamicAPILoading.py ├── examples └── StackStringsDynamicAPILoading │ ├── stack_string_example_bunched.c │ ├── stack_string_example_global_var.c │ ├── stack_string_example_local_var.c │ └── stack_string_example_out_of_order.c └── img └── StackStringsDynamicAPILoading_example.gif /.gitignore: -------------------------------------------------------------------------------- 1 | # PyCharm 2 | .idea/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 BitsOfBinary 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ghidra-scripts 2 | This repo will be used to store the Ghidra scripts that I have written. 3 | 4 | ## Installation 5 | Copy the script files into any of `ghidra_scripts` directories (which can be found under `Window->Script Manager->Script Directories`). 6 | 7 | ## StackStringsDynamicAPILoading.py 8 | Python script with the following objectives: 9 | - Find stack strings within a function, relabel them, and retype them as correctly sized character arrays 10 | - Rename and retype dynamically loaded Windows APIs 11 | 12 | This script can be called in two ways: 13 | - Current function (only run within the function that the user is currently in) 14 | - All functions 15 | 16 | ![](./img/StackStringsDynamicAPILoading_example.gif) 17 | 18 | Different stack string examples (written in C) can be found in `examples/StackStringsDynamicAPILoading`. 19 | -------------------------------------------------------------------------------- /StackStringsDynamicAPILoading.py: -------------------------------------------------------------------------------- 1 | # Script to detect Stack strings, and retype the dynamically loaded Windows API where possible 2 | # @author BitsOfBinary 3 | # @category Analysis 4 | 5 | import ghidra.app.script.GhidraScript 6 | from ghidra.app.services import DataTypeManagerService 7 | from ghidra.program.model.util import CodeUnitInsertionException 8 | from ghidra.program.model.symbol import SourceType 9 | from ghidra.program.model.listing import VariableStorage 10 | from ghidra.program.model.lang import Register 11 | from ghidra.program.model.scalar import Scalar 12 | from ghidra.program.model.address import GenericAddress 13 | from ghidra.program.model.data import CategoryPath, ArrayDataType 14 | 15 | 16 | class StackString: 17 | """ 18 | Class to represent a stack string 19 | 20 | Attributes: 21 | val (string): the string value of the stack string 22 | addr (Address): the local address of the stack string 23 | var (Variable): the variable representing the start of the stack string 24 | """ 25 | 26 | def __init__(self): 27 | self.val = "" 28 | self.addr = None 29 | self.var = None 30 | 31 | 32 | class DynamicAPILoadingHandler: 33 | """ 34 | Class used to handle the labelling and retyping of dynamically loaded Windows APIs 35 | 36 | Attributes: 37 | windows_data_type_manager (DataTypeManager): the data type manager specifically for Windows symbols 38 | category_path (CategoryPath): the path to the Windows APIs 39 | """ 40 | 41 | def __init__(self, windows_data_type_manager, category_path=CategoryPath("/winbase.h/functions")): 42 | self.windows_data_type_manager = windows_data_type_manager 43 | self.category_path = category_path 44 | 45 | def global_variable_handler(self, dyn_loaded_addr, stack_str): 46 | """ 47 | Handler to rename + retype global variables 48 | 49 | Args: 50 | dyn_loaded_addr (Address): the address of the global variable 51 | stack_str (StackString): the stack string to use in the renaming/retyping 52 | """ 53 | 54 | print("Renaming global variable %s as %s" % (str(dyn_loaded_addr), stack_str.val)) 55 | createLabel(dyn_loaded_addr, stack_str.val, True) 56 | 57 | try: 58 | dyn_data_type = self.windows_data_type_manager.getDataType( 59 | self.category_path, stack_str.val 60 | ) 61 | 62 | if dyn_data_type: 63 | print("Retyping global variable %s as %s" % (str(dyn_loaded_addr), stack_str.val)) 64 | createData(dyn_loaded_addr, dyn_data_type) 65 | 66 | except CodeUnitInsertionException: 67 | print("Could not retype: %s" % (stack_str.val)) 68 | 69 | def local_variable_handler(self, local_variable, stack_str): 70 | """ 71 | Handler to rename + retype local variables 72 | 73 | Args: 74 | local_variable (Variable): the local variable to retype/rename 75 | stack_str (StackString): the stack string to use in the renaming/retyping 76 | """ 77 | 78 | print("Renaming local variable %s as %s" % (local_variable.getName(), stack_str.val)) 79 | local_variable.setName(stack_str.val, SourceType.USER_DEFINED) 80 | 81 | try: 82 | dyn_data_type = self.windows_data_type_manager.getDataType( 83 | self.category_path, stack_str.val 84 | ) 85 | 86 | if dyn_data_type: 87 | print("Retyping local variable %s as %s" % (local_variable.getName(), stack_str.val)) 88 | local_variable.setDataType(dyn_data_type, SourceType.USER_DEFINED) 89 | 90 | except CodeUnitInsertionException: 91 | print("Could not retype: %s" % (stack_str.val)) 92 | 93 | 94 | class StackStringFunctionHandler: 95 | """ 96 | Class used to handle the parsing of stack strings per function, 97 | and then passing these to DynamicAPILoadingHandler 98 | 99 | Attributes: 100 | MAX_STEPS (int): max number of instructions to go through in a function 101 | MIN_STACK_STRING_LENGTH (int): min length to parse out a stack string 102 | STACK_REGISTERS (list): registers that are used to reference the stack 103 | RETURN_REGISTERS (list): registers that usually contain return values 104 | 105 | dyn_api_handler (DynamicAPILoadingHandler): the handler for renaming/retyping the APIs 106 | current_func (Function): the current function for the handler 107 | end_of_func_addr (Address): the end of the current function 108 | stack_strs (list): StackStrings that have been parsed out 109 | building_stack_str (StackString): temporary StackString for while it is being parsed 110 | ins (Instruction): the current instruction in the function 111 | counter (int): the number of instructions iterated through in the current function 112 | previous_stack_offset (long): the previous stack offset to make sure the stack string is being properly constructed 113 | """ 114 | 115 | MAX_STEPS = 1000 116 | MIN_STACK_STRING_LENGTH = 2 117 | STACK_REGISTERS = ["ESP", "EBP", "RSP", "RBP"] 118 | RETURN_REGISTERS = ["EAX", "RAX"] 119 | 120 | def __init__(self, dyn_api_handler, current_func, end_of_func_addr): 121 | self.dyn_api_handler = dyn_api_handler 122 | self.current_func = current_func 123 | self.end_of_func_addr = end_of_func_addr 124 | 125 | self.current_func.setCustomVariableStorage(True) 126 | 127 | self.stack_strs = [] 128 | self.building_stack_str = StackString() 129 | self.ins = None 130 | self.counter = 0 131 | self.previous_stack_offset = None 132 | 133 | def find_local_variable(self, addr): 134 | """ 135 | Find the local variable at the provided address 136 | 137 | Args: 138 | addr (Address): the address to check if there is a local variable at 139 | """ 140 | for local_variable in self.current_func.getLocalVariables(): 141 | if local_variable.getMinAddress() == addr: 142 | return local_variable 143 | 144 | def init_building_stack_str(self): 145 | """ 146 | Initialise a building stack string 147 | """ 148 | 149 | self.building_stack_str.addr = self.ins.getOperandReferences(0)[0].getToAddress() 150 | 151 | for variable in self.current_func.getLocalVariables(): 152 | 153 | if variable.getStackOffset() == self.building_stack_str.addr.getOffset(): 154 | self.building_stack_str.var = variable 155 | 156 | def stack_char_handler(self, stack_char, stack_offset): 157 | """ 158 | Handler to deal with parsed characters being placed on the stack 159 | 160 | Args: 161 | stack_char (char): a single char parsed from off the stack 162 | stack_offset (int): the offset onto the stack to prevent random character being added to the StackString 163 | """ 164 | # Check the scalar is in a "nice" ASCII range 165 | if stack_char >= 0x2E and stack_char <= 0x7A: 166 | 167 | # If we're building a StackString, make sure we've only incremented one byte on the stack 168 | if self.previous_stack_offset and (stack_offset - self.previous_stack_offset) == 1: 169 | 170 | self.building_stack_str.val += chr(stack_char) 171 | self.previous_stack_offset = stack_offset 172 | 173 | # Otherwise, start building a new StackString, and save off the stack offset 174 | else: 175 | self.building_stack_str = StackString() 176 | self.init_building_stack_str() 177 | 178 | self.building_stack_str.val += chr(stack_char) 179 | self.previous_stack_offset = stack_offset 180 | 181 | # If the scalar is NULL, then it is likely the end of the string 182 | elif stack_char == 0 and len(self.building_stack_str.val) >= self.MIN_STACK_STRING_LENGTH: 183 | 184 | print("\nStack string found:") 185 | print("Value: %s" % (self.building_stack_str.val)) 186 | print("Address: %s" % (str(self.building_stack_str.addr))) 187 | print("Variable: %s\n" % (str(self.building_stack_str.var))) 188 | 189 | # Rename the stack string variable 190 | self.building_stack_str.var.setName(self.building_stack_str.val + "_stack_str", SourceType.USER_DEFINED) 191 | 192 | # Get the data type for "char" 193 | single_char_data_type = getDataTypes("char")[0] 194 | 195 | # Create a proper length character array DataType 196 | data_type = ArrayDataType(single_char_data_type, len(self.building_stack_str.val) + 1, 1) 197 | 198 | # Setup the VariableStorage associated with the character array 199 | stack_offset = self.building_stack_str.var.getStackOffset() 200 | variable_storage = VariableStorage( 201 | self.current_func.getProgram(), 202 | stack_offset, 203 | len(self.building_stack_str.val) + 1, 204 | ) 205 | 206 | # Set the new data type 207 | self.building_stack_str.var.setDataType(data_type, variable_storage, True, SourceType.USER_DEFINED) 208 | 209 | # Add to the stack strings, and clear the building stack string 210 | self.stack_strs.append(self.building_stack_str) 211 | self.building_stack_str = StackString() 212 | self.previous_stack_offset = None 213 | 214 | def call_handler(self, stack_adjustment): 215 | """ 216 | Handler for instructions that are calls that could be used in retyping 217 | 218 | Args: 219 | stack_adjustment (int): the amount to adjust the stack offset when referencing local variables 220 | """ 221 | 222 | call_addr = self.ins.getOpObjects(0)[0] 223 | 224 | if type(call_addr) == GenericAddress: 225 | symbol = getSymbolAt(call_addr).getName() 226 | 227 | if "GetProcAddress" in symbol or "LoadLibrary" in symbol: 228 | 229 | backwards_counter = 0 230 | backwards_ins = self.ins 231 | 232 | while backwards_counter < 5: 233 | backwards_ins = backwards_ins.getPrevious() 234 | 235 | # Only check for potential references to loading values off register offsets (i.e. stack) 236 | if ( 237 | backwards_ins.getMnemonicString() == "LEA" 238 | and type(backwards_ins.getOpObjects(1)[0]) == Register 239 | ): 240 | 241 | for stack_str in self.stack_strs: 242 | loaded_var_offset_scalar = backwards_ins.getOpObjects(1)[1].subtract(stack_adjustment) 243 | 244 | if loaded_var_offset_scalar.getSignedValue() == stack_str.var.getStackOffset(): 245 | forward_ins = self.ins.getNext() 246 | 247 | if forward_ins.getMnemonicString() == "MOV": 248 | dyn_loaded_addr = forward_ins.getOperandReferences(0)[0].getToAddress() 249 | 250 | # Case for global variables 251 | if dyn_loaded_addr.isMemoryAddress(): 252 | self.dyn_api_handler.global_variable_handler(dyn_loaded_addr, stack_str) 253 | 254 | # Case for local variables 255 | else: 256 | local_variable = self.find_local_variable(dyn_loaded_addr) 257 | 258 | if local_variable: 259 | self.dyn_api_handler.local_variable_handler(local_variable, stack_str) 260 | 261 | return 262 | 263 | backwards_counter += 1 264 | 265 | def instruction_iterator(self): 266 | """ 267 | Handler to iterate over the instructions in the current function 268 | """ 269 | 270 | self.ins = getFirstInstruction(self.current_func) 271 | 272 | # Set a stack adjustment variable 273 | # BP stacks are off by 0x4, and SP stacks are off by a variable amount 274 | if self.ins.getMnemonicString() == "SUB" and self.ins.getOpObjects(0)[0].getName() in ["ESP", "RSP"]: 275 | stack_adjustment = self.ins.getOpObjects(1)[0].getUnsignedValue() 276 | 277 | else: 278 | stack_adjustment = 0x4 279 | 280 | # Check that the instruction exists first to prevent errors being thrown 281 | while ( 282 | self.ins 283 | and self.ins.getAddress() < self.end_of_func_addr 284 | and self.counter != self.MAX_STEPS 285 | ): 286 | 287 | # Case: potential stack string loading OR loading EAX into another address 288 | if self.ins.getMnemonicString() == "MOV": 289 | 290 | # This is safe to do as MOV always has two operands 291 | op1 = self.ins.getOpObjects(0) 292 | op2 = self.ins.getOpObjects(1) 293 | 294 | # Case: If a scalar is being moved into a register offset 295 | if type(op1[0]) == Register and type(op2[0]) == Scalar: 296 | 297 | if op1[0].getName() in self.STACK_REGISTERS and len(op1) > 1 and type(op1[1]) == Scalar: 298 | 299 | stack_char = op2[0].getUnsignedValue() 300 | stack_offset = op1[1].getSignedValue() 301 | self.stack_char_handler(stack_char, stack_offset) 302 | 303 | # TODO: case where a register value is being moved onto the stack 304 | # This could be if a single character is stored in a register, and moved onto the stack that way instead of as a literal 305 | 306 | elif self.ins.getMnemonicString() == "CALL": 307 | self.call_handler(stack_adjustment) 308 | 309 | self.ins = self.ins.getNext() 310 | self.counter += 1 311 | 312 | 313 | class StackStringProgramHandler: 314 | """ 315 | Class to handle the script 316 | 317 | Attributes: 318 | MAX_FUNCTIONS (int): maximum functions to check 319 | 320 | category_path (CategoryPath): the path to the Windows API functions 321 | data_type_managers (list): all loaded data type managers 322 | windows_data_type_manager (DataTypeManager): the Windows specific data type manager 323 | dyn_api_handler (DynamicAPILoadingHandler): the handler to retype/rename the APIs 324 | """ 325 | 326 | MAX_FUNCTIONS = 10000 327 | 328 | def __init__(self, category_path): 329 | self.category_path = category_path 330 | 331 | self.data_type_managers = None 332 | self.windows_data_type_manager = None 333 | self.dyn_api_handler = None 334 | 335 | def get_data_type_managers(self): 336 | """ 337 | Method to get all data type managers 338 | """ 339 | 340 | tool = state.getTool() 341 | service = tool.getService(DataTypeManagerService) 342 | self.data_type_managers = service.getDataTypeManagers() 343 | 344 | def get_windows_data_type_manager(self): 345 | """ 346 | Method to load the Windows specific data type manager 347 | """ 348 | 349 | for data_type_manager in self.data_type_managers: 350 | if "windows" in data_type_manager.getName(): 351 | self.windows_data_type_manager = data_type_manager 352 | break 353 | 354 | def run_function_handler(self, function): 355 | """ 356 | Method to run the StackStringFunctionHandler on a specified function 357 | 358 | Args: 359 | function (Function): the function to parse stack strings in 360 | """ 361 | 362 | end_of_func_addr = function.getBody().getMaxAddress() 363 | 364 | func_handler = StackStringFunctionHandler( 365 | self.dyn_api_handler, function, end_of_func_addr 366 | ) 367 | 368 | func_handler.instruction_iterator() 369 | 370 | def run(self, choice_code): 371 | """ 372 | Method to run the function handler on either a specified function, or all functions 373 | 374 | Args: 375 | choice_code (int): 0 if running on current function, 1 if running on all functions 376 | """ 377 | 378 | self.get_data_type_managers() 379 | self.get_windows_data_type_manager() 380 | 381 | self.dyn_api_handler = DynamicAPILoadingHandler(self.windows_data_type_manager, self.category_path) 382 | 383 | if choice_code == 0: 384 | current_func = getFunctionContaining(currentAddress) 385 | self.run_function_handler(current_func) 386 | 387 | elif choice_code == 1: 388 | func = getFirstFunction() 389 | func_counter = 0 390 | 391 | while func and func_counter < self.MAX_FUNCTIONS: 392 | self.run_function_handler(func) 393 | func = getFunctionAfter(func) 394 | 395 | func_counter += 1 396 | 397 | 398 | def main(): 399 | """ 400 | Entry point to the script 401 | """ 402 | 403 | # Define the category path to load Windows functions 404 | category_path = CategoryPath("/winbase.h/functions") 405 | 406 | program_handler = StackStringProgramHandler(category_path) 407 | 408 | valid_choices = ["Current function", "All functions"] 409 | valid_choices_mapping = {"Current function": 0, "All functions": 1} 410 | 411 | choice = askChoice( 412 | "Stack String option", 413 | "Do you want to run the script on the current function, or all functions?", 414 | valid_choices, 415 | None, 416 | ) 417 | 418 | program_handler.run(valid_choices_mapping[choice]) 419 | 420 | 421 | if __name__ == "__main__": 422 | main() 423 | -------------------------------------------------------------------------------- /examples/StackStringsDynamicAPILoading/stack_string_example_bunched.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | HMODULE loaded_lib; 6 | FARPROC loaded_api; 7 | 8 | int main() { 9 | 10 | char lib[13]; 11 | char api[13]; 12 | 13 | TCHAR username[UNLEN + 1]; 14 | DWORD size = UNLEN + 1; 15 | 16 | // Advapi32.dll 17 | *(lib + 0) = 'A'; 18 | *(lib + 1) = 'd'; 19 | *(lib + 2) = 'v'; 20 | *(lib + 3) = 'a'; 21 | *(lib + 4) = 'p'; 22 | *(lib + 5) = 'i'; 23 | *(lib + 6) = '3'; 24 | *(lib + 7) = '2'; 25 | *(lib + 8) = '.'; 26 | *(lib + 9) = 'd'; 27 | *(lib + 10) = 'l'; 28 | *(lib + 11) = 'l'; 29 | *(lib + 12) = '\0'; 30 | 31 | // GetUserNameA 32 | *(api + 0) = 'G'; 33 | *(api + 1) = 'e'; 34 | *(api + 2) = 't'; 35 | *(api + 3) = 'U'; 36 | *(api + 4) = 's'; 37 | *(api + 5) = 'e'; 38 | *(api + 6) = 'r'; 39 | *(api + 7) = 'N'; 40 | *(api + 8) = 'a'; 41 | *(api + 9) = 'm'; 42 | *(api + 10) = 'e'; 43 | *(api + 11) = 'A'; 44 | *(api + 12) = '\0'; 45 | 46 | loaded_lib = LoadLibrary(lib); 47 | 48 | loaded_api = GetProcAddress(loaded_lib, api); 49 | 50 | printf("Library: %s\n", lib); 51 | printf("API: %s\n", api); 52 | 53 | loaded_api((TCHAR*)username, &size); 54 | 55 | printf("Username: %s\n", username); 56 | 57 | } -------------------------------------------------------------------------------- /examples/StackStringsDynamicAPILoading/stack_string_example_global_var.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | HMODULE loaded_lib; 6 | FARPROC loaded_api; 7 | 8 | int main() { 9 | 10 | char lib[13]; 11 | char api[13]; 12 | 13 | TCHAR username[UNLEN + 1]; 14 | DWORD size = UNLEN + 1; 15 | 16 | // Advapi32.dll 17 | *(lib + 0) = 'A'; 18 | *(lib + 1) = 'd'; 19 | *(lib + 2) = 'v'; 20 | *(lib + 3) = 'a'; 21 | *(lib + 4) = 'p'; 22 | *(lib + 5) = 'i'; 23 | *(lib + 6) = '3'; 24 | *(lib + 7) = '2'; 25 | *(lib + 8) = '.'; 26 | *(lib + 9) = 'd'; 27 | *(lib + 10) = 'l'; 28 | *(lib + 11) = 'l'; 29 | *(lib + 12) = '\0'; 30 | 31 | loaded_lib = LoadLibrary(lib); 32 | 33 | // GetUserNameA 34 | *(api + 0) = 'G'; 35 | *(api + 1) = 'e'; 36 | *(api + 2) = 't'; 37 | *(api + 3) = 'U'; 38 | *(api + 4) = 's'; 39 | *(api + 5) = 'e'; 40 | *(api + 6) = 'r'; 41 | *(api + 7) = 'N'; 42 | *(api + 8) = 'a'; 43 | *(api + 9) = 'm'; 44 | *(api + 10) = 'e'; 45 | *(api + 11) = 'A'; 46 | *(api + 12) = '\0'; 47 | 48 | loaded_api = GetProcAddress(loaded_lib, api); 49 | 50 | printf("Library: %s\n", lib); 51 | printf("API: %s\n", api); 52 | 53 | loaded_api((TCHAR*)username, &size); 54 | 55 | printf("Username: %s\n", username); 56 | 57 | } -------------------------------------------------------------------------------- /examples/StackStringsDynamicAPILoading/stack_string_example_local_var.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() { 6 | 7 | char lib[13]; 8 | char api[13]; 9 | 10 | HMODULE loaded_lib; 11 | FARPROC loaded_api; 12 | 13 | TCHAR username[UNLEN + 1]; 14 | DWORD size = UNLEN + 1; 15 | 16 | // Advapi32.dll 17 | *(lib + 0) = 'A'; 18 | *(lib + 1) = 'd'; 19 | *(lib + 2) = 'v'; 20 | *(lib + 3) = 'a'; 21 | *(lib + 4) = 'p'; 22 | *(lib + 5) = 'i'; 23 | *(lib + 6) = '3'; 24 | *(lib + 7) = '2'; 25 | *(lib + 8) = '.'; 26 | *(lib + 9) = 'd'; 27 | *(lib + 10) = 'l'; 28 | *(lib + 11) = 'l'; 29 | *(lib + 12) = '\0'; 30 | 31 | loaded_lib = LoadLibrary(lib); 32 | 33 | // GetUserNameA 34 | *(api + 0) = 'G'; 35 | *(api + 1) = 'e'; 36 | *(api + 2) = 't'; 37 | *(api + 3) = 'U'; 38 | *(api + 4) = 's'; 39 | *(api + 5) = 'e'; 40 | *(api + 6) = 'r'; 41 | *(api + 7) = 'N'; 42 | *(api + 8) = 'a'; 43 | *(api + 9) = 'm'; 44 | *(api + 10) = 'e'; 45 | *(api + 11) = 'A'; 46 | *(api + 12) = '\0'; 47 | 48 | loaded_api = GetProcAddress(loaded_lib, api); 49 | 50 | printf("Library: %s\n", lib); 51 | printf("API: %s\n", api); 52 | 53 | loaded_api((TCHAR*)username, &size); 54 | 55 | printf("Username: %s\n", username); 56 | 57 | } -------------------------------------------------------------------------------- /examples/StackStringsDynamicAPILoading/stack_string_example_out_of_order.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | HMODULE loaded_lib; 6 | FARPROC loaded_api; 7 | 8 | int main() { 9 | 10 | char lib[13]; 11 | char api[13]; 12 | 13 | TCHAR username[UNLEN + 1]; 14 | DWORD size = UNLEN + 1; 15 | 16 | // GetUserNameA 17 | *(api + 0) = 'G'; 18 | *(api + 1) = 'e'; 19 | *(api + 2) = 't'; 20 | *(api + 3) = 'U'; 21 | *(api + 4) = 's'; 22 | *(api + 5) = 'e'; 23 | *(api + 6) = 'r'; 24 | *(api + 7) = 'N'; 25 | *(api + 8) = 'a'; 26 | *(api + 9) = 'm'; 27 | *(api + 10) = 'e'; 28 | *(api + 11) = 'A'; 29 | *(api + 12) = '\0'; 30 | 31 | // Advapi32.dll 32 | *(lib + 0) = 'A'; 33 | *(lib + 1) = 'd'; 34 | *(lib + 2) = 'v'; 35 | *(lib + 3) = 'a'; 36 | *(lib + 4) = 'p'; 37 | *(lib + 5) = 'i'; 38 | *(lib + 6) = '3'; 39 | *(lib + 7) = '2'; 40 | *(lib + 8) = '.'; 41 | *(lib + 9) = 'd'; 42 | *(lib + 10) = 'l'; 43 | *(lib + 11) = 'l'; 44 | *(lib + 12) = '\0'; 45 | 46 | loaded_lib = LoadLibrary(lib); 47 | 48 | loaded_api = GetProcAddress(loaded_lib, api); 49 | 50 | printf("Library: %s\n", lib); 51 | printf("API: %s\n", api); 52 | 53 | loaded_api((TCHAR*)username, &size); 54 | 55 | printf("Username: %s\n", username); 56 | 57 | } -------------------------------------------------------------------------------- /img/StackStringsDynamicAPILoading_example.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BitsOfBinary/ghidra-scripts/783e4c5499d21a8281c838c9c45205dc79edde4f/img/StackStringsDynamicAPILoading_example.gif --------------------------------------------------------------------------------