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.