Wenn ich eine Umgebungsvariable ändere oder hinzufüge, muss ich die Eingabeaufforderung neu starten. Gibt es einen Befehl, den ich ausführen könnte, ohne CMD neu zu starten?
Antworten
Zu viele Anzeigen?Es ist möglich, dies zu tun, indem die Umgebungstabelle innerhalb eines bestimmten Prozesses selbst überschrieben wird.
Als Proof of Concept habe ich diese Beispielanwendung geschrieben, die lediglich eine einzige (bekannte) Umgebungsvariable in einem cmd.exe-Prozess bearbeitet:
typedef DWORD (__stdcall *NtQueryInformationProcessPtr)(HANDLE, DWORD, PVOID, ULONG, PULONG);
int __cdecl main(int argc, char* argv[])
{
HMODULE hNtDll = GetModuleHandleA("ntdll.dll");
NtQueryInformationProcessPtr NtQueryInformationProcess = (NtQueryInformationProcessPtr)GetProcAddress(hNtDll, "NtQueryInformationProcess");
int processId = atoi(argv[1]);
printf("Target PID: %u\n", processId);
// open the process with read+write access
HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, 0, processId);
if(hProcess == NULL)
{
printf("Error opening process (%u)\n", GetLastError());
return 0;
}
// find the location of the PEB
PROCESS_BASIC_INFORMATION pbi = {0};
NTSTATUS status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), NULL);
if(status != 0)
{
printf("Error ProcessBasicInformation (0x%8X)\n", status);
}
printf("PEB: %p\n", pbi.PebBaseAddress);
// find the process parameters
char *processParamsOffset = (char*)pbi.PebBaseAddress + 0x20; // hard coded offset for x64 apps
char *processParameters = NULL;
if(ReadProcessMemory(hProcess, processParamsOffset, &processParameters, sizeof(processParameters), NULL))
{
printf("UserProcessParameters: %p\n", processParameters);
}
else
{
printf("Error ReadProcessMemory (%u)\n", GetLastError());
}
// find the address to the environment table
char *environmentOffset = processParameters + 0x80; // hard coded offset for x64 apps
char *environment = NULL;
ReadProcessMemory(hProcess, environmentOffset, &environment, sizeof(environment), NULL);
printf("environment: %p\n", environment);
// copy the environment table into our own memory for scanning
wchar_t *localEnvBlock = new wchar_t[64*1024];
ReadProcessMemory(hProcess, environment, localEnvBlock, sizeof(wchar_t)*64*1024, NULL);
// find the variable to edit
wchar_t *found = NULL;
wchar_t *varOffset = localEnvBlock;
while(varOffset < localEnvBlock + 64*1024)
{
if(varOffset[0] == '\0')
{
// we reached the end
break;
}
if(wcsncmp(varOffset, L"ENVTEST=", 8) == 0)
{
found = varOffset;
break;
}
varOffset += wcslen(varOffset)+1;
}
// check to see if we found one
if(found)
{
size_t offset = (found - localEnvBlock) * sizeof(wchar_t);
printf("Offset: %Iu\n", offset);
// write a new version (if the size of the value changes then we have to rewrite the entire block)
if(!WriteProcessMemory(hProcess, environment + offset, L"ENVTEST=def", 12*sizeof(wchar_t), NULL))
{
printf("Error WriteProcessMemory (%u)\n", GetLastError());
}
}
// cleanup
delete[] localEnvBlock;
CloseHandle(hProcess);
return 0;
}
Beispielhafte Ausgabe:
>set ENVTEST=abc
>cppTest.exe 13796
Target PID: 13796
PEB: 000007FFFFFD3000
UserProcessParameters: 00000000004B2F30
environment: 000000000052E700
Offset: 1528
>set ENVTEST
ENVTEST=def
Notas
Dieser Ansatz würde sich auch auf Sicherheitsbeschränkungen beschränken. Wenn das Ziel auf einer höheren Ebene oder unter einem höheren Konto (z. B. SYSTEM) ausgeführt wird, hätten wir keine Berechtigung, seinen Speicher zu bearbeiten.
Wenn Sie dies mit einer 32-Bit-Anwendung tun wollten, würden sich die oben hart kodierten Offsets in 0x10 bzw. 0x48 ändern. Diese Offsets können durch Ausgeben der _PEB- und _RTL_USER_PROCESS_PARAMETERS-Strukturen in einem Debugger gefunden werden (z.B. in WinDbg dt _PEB
y dt _RTL_USER_PROCESS_PARAMETERS
)
Um das Proof-of-Concept in das zu ändern, was der OP braucht, würde es einfach die aktuellen System- und Benutzer-Umgebungsvariablen aufzählen (wie in @tsadoks Antwort dokumentiert) und die gesamte Umgebungstabelle in den Speicher des Zielprozesses schreiben.
Bearbeiten: Die Größe des Umgebungsblocks wird ebenfalls in der Struktur _RTL_USER_PROCESS_PARAMETERS gespeichert, aber der Speicher wird auf dem Heap des Prozesses zugewiesen. Von einem externen Prozess aus hätten wir also nicht die Möglichkeit, die Größe zu ändern und ihn zu vergrößern. Ich habe mit VirtualAllocEx herumgespielt, um im Zielprozess zusätzlichen Speicher für den Umgebungsspeicher zuzuweisen, und konnte eine völlig neue Tabelle setzen und lesen. Leider führt jeder Versuch, die Umgebung auf normalem Wege zu ändern, zum Absturz, da die Adresse nicht mehr auf den Heap verweist (es kommt zum Absturz in RtlSizeHeap).
Der einfachste Weg, eine Variable zum Pfad hinzuzufügen, ohne die aktuelle Sitzung neu zu starten, ist, die Eingabeaufforderung zu öffnen und einzugeben:
PATH=(VARIABLE);%path%
und drücken Sie enter .
um zu prüfen, ob Ihre Variable geladen wurde, geben Sie ein
PATH
und drücken Sie enter . Die Variable ist jedoch nur so lange Teil des Pfads, bis Sie neu starten.
Umgebungsvariablen werden in HKEY_LOCAL_MACHINE gespeichert \SYSTEM\ControlSet\Control\Session Manager \Environment.
Viele der nützlichen Umgebungsvariablen, wie z. B. Path, werden als REG_SZ gespeichert. Es gibt mehrere Möglichkeiten, auf die Registry zuzugreifen, darunter REGEDIT:
REGEDIT /E <filename> "HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Session Manager\Environment"
Die Ausgabe beginnt mit magischen Zahlen. Um sie mit dem Befehl find zu durchsuchen, muss sie also eingegeben und umgeleitet werden: type <filename> | findstr -c:\"Path\"
Wenn Sie also nur die Pfadvariable in Ihrer aktuellen Befehlssitzung mit den Systemeigenschaften aktualisieren möchten, funktioniert das folgende Batch-Skript gut:
RefreshPath.cmd:
@echo off
REM This solution requests elevation in order to read from the registry.
if exist %temp%\\env.reg del %temp%\\env.reg /q /f
REGEDIT /E %temp%\\env.reg "HKEY\_LOCAL\_MACHINE\\SYSTEM\\ControlSet001\\Control\\Session Manager\\Environment"
if not exist %temp%\\env.reg (
echo "Unable to write registry to temp location"
exit 1
)
SETLOCAL EnableDelayedExpansion
for /f "tokens=1,2\* delims==" %%i in ('type %temp%\\env.reg ^| findstr -c:\\"Path\\"=') do (
set upath=%%~j
echo !upath:\\\\=\\! >%temp%\\newpath
)
ENDLOCAL
for /f "tokens=\*" %%i in (%temp%\\newpath) do set path=%%i