Environment variable editor
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.
BackgroundIn MS Windows, environment variables can be managed using various API calls. For our script we are only interested in 3 calls:
- GetEnvironmentStrings: Returns a block of memory pointing to all the environment variables in the process. This block is a list of zero terminated strings, the end of the list is marked with two \0 characters (such list is also known as MULTI_SZ)
- FreeEnvironmentStrings: Frees the block returned by GetEnvironmentStrings()
- SetEnvironmentVariable: Creates or edits an environment variable
Setting up AppcallLet 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 notes that:
- Both GetEnvironmentStrings() and FreeEnvironmentStrings() prototypes are re-declared to accept an unsigned int instead of a character pointer because Appcall does not support the MULTI_SZ type.
- We are using the ASCII version of those APIs. Modifying the script to work with the unicode version is not very difficult to achieve.
Retrieving all environment variablesWe need to write a function to retrieve all the environment variables in a list:
- Call the GetEnvironmentStrings() to retrieve a pointer to the environment block in the process. Save that pointer
- Read the debuggee’s memory at that block and retrieve the variables accordingly.
- Free the block by calling FreeEnvironmentStrings()
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
Writing a custom viewer to manage the environment variablesNow that we have all the required supporting code, we can write a custom viewer that does the following:
- Retrieves all the environment variables: call GetEnvironmentStrings() and parse the result
- Parse each environment variable into KEY/VALUE components
- Add a colored line into the custom viewer with different colors for the KEY and VALUE
- Add popup menu entries and handle keypresses to support three operations:
- Insert: Ask the user for a key and value using the idaapi.askstr() then call the SetEnvironmentVariable() API as shown in the previous example
- Edit: Retrieve the cursor position, extract the key name, ask the user for a new value only (w/o asking for a key) and call the SetEnvironmentVariable() API
- Delete: Retrieve the key name and call SetEnvironmentVariable() API with value pointing to NULL
Please download the script from here.