Normally, to change environment variables in a running process, one has to terminate the process, edit the environment variables and re-run the process. In this blog entry we are going to write an IDAPython script that allows us to add, edit or delete environment variables in a running process directly. To achieve this we will use Appcall to manage the variables and a custom viewer that serves as the graphical interface.
In MS Windows, environment variables can be managed using various API calls. For our script we are only interested in 3 calls:
In order to modify the environment variables in a process, we need to issue those API calls in the context of that process. One solution is to inject a DLL into the running process and ask it to call those APIs. Another simpler solution is to write a script that uses Appcall to issue those API calls.
Let us use Appcall to retrieve 3 callable objects corresponding to the APIs in question:
GetEnvironmentStrings = Appcall.proto("kernel32_GetEnvironmentStringsA", "unsigned int __stdcall GetEnvironmentStrings();") SetEnvironmentVariable = Appcall.proto("kernel32_SetEnvironmentVariableA", "int __stdcall SetEnvironmentVariable(const char *lpName, const char *lpValue);") FreeEnvironmentStrings = Appcall.proto("kernel32_FreeEnvironmentStringsA", "int __stdcall FreeEnvironmentStrings(unsigned int block);")
Now we can use those callable objects to call the APIs in the context of the debugged process, for example:
# Add a new environment variable SetEnvironmentVariable("MY_VAR", "Some value")
Please note that:
We need to write a function to retrieve all the environment variables in a list:
To read the debuggee’s memory, we can use idc.Bytes(), idaapi.get_many_bytes() or idaapi.dbg_read_memory(). Since we don’t know how big the block is, we will read one character at a time and process it. For demonstration purposes, we will use yet another mechanism to read from the debuggee’s memory as if we were reading from a file. We will use the loader_input_t class followed by a call to open_memory() function:
def Variables(): """Returns all environment blocks""" # Get all environment strings env_ptr = GetEnvironmentStrings() if env_ptr == 0: return None # Always refresh the memory after a call # that could change the memory configuration idc.RefreshDebuggerMemory() # Open process memory f = idaapi.loader_input_t() f.open_memory(env_ptr) # Parse all environment variables into a list of variables vars = [] var = [] while True: ch = f.get_char() if not ch: break if ch == '\x00': # double-null -> end of list if not var: break vars.append(''.join(var)) var = [] else: var.append(ch) f.close() # Free the block FreeEnvironmentStrings(env_ptr) return vars
Now that we have all the required supporting code, we can write a custom viewer that does the following:
Before running the script, make sure that the debugged process is suspended. Right-click on an environment variable to edit/insert or delete.
Please download the script from here.