sidwelle
asked on
C++, sytem SET windows environment variables
If I can call simple cmd line commands from the system command in C++,
Why can't I use the system function to set environment variables ?
Why can't I use the system function to set environment variables ?
int main(int argc, char* argv[])
{
//printf("Hello World!\n");
system("SET MyVar=MyValue\r\n");
//system("dir");
return 0;
}
Exactly. If you want to set an environment variable persistently, you need to do that in the registry under HKEY_CURRENT_USER\Environm ent or HKEY_LOCAL_MACHINE\SYSTEM\ CurrentCon trolSet\Co ntrol\Sess ion Manager\Environment - using 'system()', you can do that like
Yet I'd rather suggest using the regular registry APIs
system("REG.exe ADD HKCU\\Environment /v MyVar /t REG_SZ /d MyValue");
Yet I'd rather suggest using the regular registry APIs
ASKER
But neither one of these cmds get the value back to parent process 'this console'
how do I get the value back to this console ?
how do I get the value back to this console ?
Check out if this works for you :
-Rahul
#include <iostream>
#include <cstdlib>
int main()
{
putenv("PATH=C:\");
cout<<getenv("PATH");
}
-Rahul
If you want to set that for your console process only, use
or
SetEnvironmentVariable("MyVar","MyValue");
// ...
char buf[MAX_PATH];
GetEnvironmentVariable("MyVar",buf,MAX_PATH);
or
#include <stdlib.h>
#include <stdio.h>
// ...
_putenv("MyVar=MyValue");
char* pVal = getenv("MyVar");
Hi sid,
What are you trying to get back? The system parameter that you're trying to change?
Kent
What are you trying to get back? The system parameter that you're trying to change?
Kent
ASKER
I am trying to set an Environment variable that will persist after my app has terminated.
>>I am trying to set an Environment variable that will persist after my app has
>>terminated.
Then you'll have to go the registry way, sorry.
>>terminated.
Then you'll have to go the registry way, sorry.
Alternatively, if you want to do it the really old fashioned Windows NT way, get setx from http://www.microsoft.com/download/en/details.aspx?id=4200
This is part of the windows resource kit. Then use setx instead of set in your system call. Does the same thing as the registry call but it isn't half as complicated.
This is part of the windows resource kit. Then use setx instead of set in your system call. Does the same thing as the registry call but it isn't half as complicated.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ok --
here's a first working solution.
compile it.
then run it FROM A CMD SHELL, passing name and value as parameters. e.g. SetEnv.exe MYVAR MYVALUE
upon completion, examine the shell's environment with the command SET: you will see MYVAR=MYVALUE.
here's a first working solution.
// SetEnv.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#include <SetupAPI.h>
#include <stdlib.h>
#include <stdio.h>
#include <Psapi.h>
#include <tlhelp32.h>
#pragma comment(lib,"Psapi.lib")
BOOL ShowErr(void)
{
DWORD dwerr = GetLastError();
if(ERROR_SUCCESS == dwerr)
{
printf("No errors to report\n");
return FALSE;
}
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dwerr,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL
);
printf("FAILED with code %x: %s\n", dwerr, (char*)lpMsgBuf);
LocalFree( lpMsgBuf );
return TRUE;
}
BOOL WINAPI GetParentPID( PROCESSENTRY32& procentry )
{
OSVERSIONINFO osver ;
HINSTANCE hInstLib ;
HANDLE hSnapShot ;
BOOL bContinue ;
// ToolHelp Function Pointers.
HANDLE (WINAPI *lpfCreateToolhelp32Snapshot)(DWORD,DWORD) ;
BOOL (WINAPI *lpfProcess32First)(HANDLE,LPPROCESSENTRY32) ;
BOOL (WINAPI *lpfProcess32Next)(HANDLE,LPPROCESSENTRY32) ;
// Check to see if were running under Windows95 or
// Windows NT.
osver.dwOSVersionInfoSize = sizeof( osver ) ;
if( !GetVersionEx( &osver ) )
{
return FALSE ;
}
if( osver.dwPlatformId != VER_PLATFORM_WIN32_NT )
{
ShowErr();
return FALSE;
}
hInstLib = LoadLibraryA( "Kernel32.DLL" ) ;
if( hInstLib == NULL )
{
ShowErr();
return FALSE ;
}
// Get procedure addresses.
// We are linking to these functions of Kernel32
// explicitly, because otherwise a module using
// this code would fail to load under Windows NT,
// which does not have the Toolhelp32
// functions in the Kernel 32.
lpfCreateToolhelp32Snapshot=
(HANDLE(WINAPI *)(DWORD,DWORD))
GetProcAddress( hInstLib,
"CreateToolhelp32Snapshot" ) ;
lpfProcess32First=
(BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32))
GetProcAddress( hInstLib, "Process32First" ) ;
lpfProcess32Next=
(BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32))
GetProcAddress( hInstLib, "Process32Next" ) ;
if( lpfProcess32Next == NULL ||
lpfProcess32First == NULL ||
lpfCreateToolhelp32Snapshot == NULL )
{
FreeLibrary( hInstLib ) ;
return FALSE ;
}
// Get a handle to a Toolhelp snapshot of the systems
// processes.
hSnapShot = lpfCreateToolhelp32Snapshot(
TH32CS_SNAPPROCESS, 0 ) ;
if( hSnapShot == INVALID_HANDLE_VALUE )
{
ShowErr();
FreeLibrary( hInstLib ) ;
return FALSE ;
}
// Get the first process' information.
memset((LPVOID)&procentry,0,sizeof(PROCESSENTRY32));
procentry.dwSize = sizeof(PROCESSENTRY32) ;
bContinue = lpfProcess32First( hSnapShot, &procentry ) ;
DWORD pid = 0;
// While there are processes, keep looping.
DWORD crtpid= GetCurrentProcessId();
while( bContinue )
{
if(crtpid == procentry.th32ProcessID)
pid = procentry.th32ParentProcessID;
procentry.dwSize = sizeof(PROCESSENTRY32) ;
bContinue = !pid && lpfProcess32Next( hSnapShot, &procentry );
}//while ends
// Free the library.
FreeLibrary( hInstLib ) ;
return pid?TRUE:FALSE ;
}
typedef struct _DATA
{
LPVOID SetEnvironmentVariableWAddr;
wchar_t *lpName;
wchar_t *lpValue;
} THRDDATA, *LPTHRDDATA;
static DWORD __declspec(naked) WINAPI ThreadProc(LPTHRDDATA pData)
{
__asm
{
//standard prolog
PUSH EBP
MOV EBP, ESP
//move pData in EBX
PUSH EBX
MOV EBX, DWORD PTR [EBP+0x08]
//call BOOL bRet = pData->SetEnvironmentVariableWAddr(pData->lpName, pData->lpValue)
PUSH DWORD PTR [EBX+0x08]
PUSH DWORD PTR [EBX+0x04]
CALL DWORD PTR [EBX]
//return bRet ? 0 : 1
NEG EAX
SBB EAX, EAX
INC EAX
POP EBX
//standard epilog
POP EBP
RET 0x0004
}
}
int wmain(int argc, wchar_t **argv)
{
if (argc < 2)
return 1;
wchar_t *szEnvVar = argv[1];
wchar_t *szEnvValue = argv[2];
PROCESSENTRY32 selfprocentry;
if(GetParentPID(selfprocentry ))
{
// open process. this must be our cmd.exe
HANDLE hProc = NULL;
if ((hProc = OpenProcess(PROCESS_CREATE_THREAD |
PROCESS_VM_OPERATION |
PROCESS_VM_READ |
PROCESS_VM_WRITE |
PROCESS_QUERY_INFORMATION, FALSE, selfprocentry.th32ParentProcessID)) != NULL)
{
// calc space needed for code and data
size_t len1 = wcslen(szEnvVar) + 1;
size_t len2 = wcslen(szEnvValue) + 1;
HMODULE hKernel = GetModuleHandleW(L"kernel32.dll");
size_t codesize = 32;
size_t datasize = sizeof(THRDDATA);
size_t stringsize1 = len1 * sizeof(wchar_t);
size_t stringsize2 = len2 * sizeof(wchar_t);
// allocate space in the remote process
LPVOID pCode = (LPVOID)VirtualAllocEx(hProc, NULL, codesize + datasize + stringsize1 + stringsize2,
MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (pCode != NULL)
{
// set up pointers to code / data / strings
LPVOID pData = (BYTE*)pCode + codesize;
LPVOID pString1 = (BYTE*)pData + datasize;
LPVOID pString2 = (BYTE*)pString1 + stringsize1;
// we must provide the address of the SetEnvironmentVariableW function
THRDDATA data;
data.SetEnvironmentVariableWAddr = (LPVOID)GetProcAddress(hKernel, "SetEnvironmentVariableW");
data.lpName = (wchar_t*)pString1;
data.lpValue = (wchar_t*)pString2;
// copy code and data to remote process
if (WriteProcessMemory(hProc, pCode, &ThreadProc, codesize, NULL) &&
WriteProcessMemory(hProc, pData, &data, datasize, NULL) &&
WriteProcessMemory(hProc, pString1, szEnvVar, stringsize1, NULL) &&
WriteProcessMemory(hProc, pString2, szEnvValue, stringsize2, NULL))
{
// execute code remotely
HANDLE hThread;
if ((hThread = CreateRemoteThread(hProc, NULL, NULL, (LPTHREAD_START_ROUTINE)pCode,
pData, NULL, NULL)) != NULL)
{
DWORD dwExitCode;
WaitForSingleObject(hThread, 10000) == WAIT_OBJECT_0 &&
GetExitCodeThread(hThread, &dwExitCode) &&
dwExitCode != 0;
CloseHandle(hThread);
}
}
VirtualFreeEx(hProc, pCode, 0, MEM_RELEASE);
}
CloseHandle(hProc);
}
else
return 1;
}
else
return 1;
return 0;
}
compile it.
then run it FROM A CMD SHELL, passing name and value as parameters. e.g. SetEnv.exe MYVAR MYVALUE
upon completion, examine the shell's environment with the command SET: you will see MYVAR=MYVALUE.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
lomo74, thanks for the solution. I have not had a chance to test it yet, but I will in next day or two.
Thanks
BTW, I see that <stdio.h> is included twice, why does that not create a problem ?
Thanks
BTW, I see that <stdio.h> is included twice, why does that not create a problem ?
You won'T be able to set persistent environment variables on Windows without doint that in the registry, I am afraid.
@jkr:
you didn't get the point.
sidwelle does NOT want to set persistent variables (that survive across system restarts).
sidwelle wants to set variables that propagate from his process to the parent shell (CMD.EXE).
the registry is useless in this scenario, because the shell won't read those variables until next restart.
you didn't get the point.
sidwelle does NOT want to set persistent variables (that survive across system restarts).
sidwelle wants to set variables that propagate from his process to the parent shell (CMD.EXE).
the registry is useless in this scenario, because the shell won't read those variables until next restart.
I would take a simple solution as Rahul_Gade suggested: I have modified the putenv() and getenv() function which is deprecated. This will get the value back to your console in the same session.
char *pValue;
size_t len;
_putenv("Myvar=Myval");
errno_t err = _dupenv_s( &pValue, &len, "Myvar" );
if ( err ) return -1;
printf( "Myvar = %s\n", pValue );
free( pValue );
system("pause");
@DeepuAbrahamK:
with _putenv you are setting the variable into the PROCESS.
it will NOT propagate to the console.
>> errno_t err = _dupenv_s( &pValue, &len, "Myvar" );
>> if ( err ) return -1;
>> printf( "Myvar = %s\n", pValue );
>> free( pValue );
>> system("pause");
the above is meaningless: after setting the env var into the process, you read it back. Obviously, it is set.
Try exit the program; then issue the dos "SET" command: Myvar is NOT defined.
with _putenv you are setting the variable into the PROCESS.
it will NOT propagate to the console.
>> errno_t err = _dupenv_s( &pValue, &len, "Myvar" );
>> if ( err ) return -1;
>> printf( "Myvar = %s\n", pValue );
>> free( pValue );
>> system("pause");
the above is meaningless: after setting the env var into the process, you read it back. Obviously, it is set.
Try exit the program; then issue the dos "SET" command: Myvar is NOT defined.
@lomo74, But I could find the Myvar in the environment variable after exiting the app
well, that's not possible by means of your technique IMHO.
that variable must come from elsewhere (e.g. the registry? or a previous SET command?).
please make a test: try to set ANOTHER variable (e.g. MyVar1).
windows is not supposed to work this way, and MSDN is clear about that --
http://msdn.microsoft.com/en-us/library/windows/desktop/ms682009%28v=vs.85%29.aspx
that variable must come from elsewhere (e.g. the registry? or a previous SET command?).
please make a test: try to set ANOTHER variable (e.g. MyVar1).
windows is not supposed to work this way, and MSDN is clear about that --
http://msdn.microsoft.com/en-us/library/windows/desktop/ms682009%28v=vs.85%29.aspx
@lomo74, You are spot on ! @others & @sidwelle, Sorry I have just overlooked ! Thanks
>>you didn't get the point.
>>sidwelle does NOT want to set persistent variables
Um, how would you then explain "I am trying to set an Environment variable that will persist after my app has terminated."?
https://www.experts-exchange.com/questions/27424164/C-sytem-SET-windows-environment-variables.html?cid=1573&anchorAnswerId=37058793#a37058793
Please read the *entire* thread.
>>sidwelle does NOT want to set persistent variables
Um, how would you then explain "I am trying to set an Environment variable that will persist after my app has terminated."?
https://www.experts-exchange.com/questions/27424164/C-sytem-SET-windows-environment-variables.html?cid=1573&anchorAnswerId=37058793#a37058793
Please read the *entire* thread.
@jkr
he wrote:
"after my app has terminated"
NOT
"after my app has terminated and the windows session restarted".
here is your solution's behaviour:
- shell running, variable not set. App is being launched
- app running -> it sets variable in the registry - variable still not visible in the process.
- app terminated -> returning to the shell - variable still not visible in the shell.
- shell closed, session restarted, shell launched -> NOW variable is visible.
mine:
- shell running, variable not set. App is being launched
- app running -> it sets variable in the running shell.
- app terminated -> returning to the shell - NOW variable is visible.
caught it?
Let's wait for sidwelle's comment. He'll decide which solution better suits his needs.
he wrote:
"after my app has terminated"
NOT
"after my app has terminated and the windows session restarted".
here is your solution's behaviour:
- shell running, variable not set. App is being launched
- app running -> it sets variable in the registry - variable still not visible in the process.
- app terminated -> returning to the shell - variable still not visible in the shell.
- shell closed, session restarted, shell launched -> NOW variable is visible.
mine:
- shell running, variable not set. App is being launched
- app running -> it sets variable in the running shell.
- app terminated -> returning to the shell - NOW variable is visible.
caught it?
Let's wait for sidwelle's comment. He'll decide which solution better suits his needs.
ASKER
Iomo74, your scenario is correct. I will find some time to work with it today.
That is what I needed.
That being said, If someone really wanted an all inclusive solution. both solutions should be included.
That is what I needed.
That being said, If someone really wanted an all inclusive solution. both solutions should be included.
ASKER
I keep getting the following error: fatal error C1083: Cannot open include file: 'Psapi.h': No such file or directory
I downloaded the SP6 for VS6, but the error persists. Is there a simple fix for this error ?
I downloaded the SP6 for VS6, but the error persists. Is there a simple fix for this error ?
you have to download and install the Windows SDK (http://www.microsoft.com/download/en/details.aspx?id=8442), then point your compiler to the "include" and "lib" subdirectories of the SDK installation directory so it can find .h and .lib files
ASKER
It just got worse:
I download the file and burned the disk and intalled: Windows SDK for Windows 7 and .Net Framework 4.
In VS6 C++, opened "Tools" > "Options" > "Directories"
Made sure the following directories were included:
C:\PROGRAM FILES\MICROSOFT SDKS\WINDOWS\V7.0A\INCLUDE
C:\PROGRAM FILES\MICROSOFT SDKS\WINDOWS\V7.0A\LIB
Now I get over a hundred errors:
c:\program files\microsoft sdks\windows\v7.0a\include \psapi.h(8 5) : error C2065: '__out_bcount' : undeclared identifier
c:\program files\microsoft sdks\windows\v7.0a\include \psapi.h(8 5) : error C2065: 'cb' : undeclared identifier
... SetTest.exe - 102 error(s), 22 warning(s)
What went wrong ?
I download the file and burned the disk and intalled: Windows SDK for Windows 7 and .Net Framework 4.
In VS6 C++, opened "Tools" > "Options" > "Directories"
Made sure the following directories were included:
C:\PROGRAM FILES\MICROSOFT SDKS\WINDOWS\V7.0A\INCLUDE
C:\PROGRAM FILES\MICROSOFT SDKS\WINDOWS\V7.0A\LIB
Now I get over a hundred errors:
c:\program files\microsoft sdks\windows\v7.0a\include
c:\program files\microsoft sdks\windows\v7.0a\include
... SetTest.exe - 102 error(s), 22 warning(s)
What went wrong ?
what the heck... I'm sorry, I overlooked it... you have VS6: the latest SDK compatible with VS6 is the Windows 2003 Platform SDK (Feb 2003 edition) only available on CD by Microsoft.
remove those dirs from VS options...
ANYWAY -
I just deleted some #include statements and the program compiles fine :-P
leave only the following and try again:
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#include <tlhelp32.h>
#include <tchar.h>
remove those dirs from VS options...
ANYWAY -
I just deleted some #include statements and the program compiles fine :-P
leave only the following and try again:
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#include <tlhelp32.h>
#include <tchar.h>
and, oh -
remove also that
#pragma comment(lib, "Psapi.lib")
remove also that
#pragma comment(lib, "Psapi.lib")
ASKER
I wondered why you wanted the SDK loaded. Followed your instructions and removed the 'includes'
and the dir references. Now when I try to compile I get the following two errors:
But I don't know enough about how the API works to troubleshoot.
C:\Projects\C++\TEST06\Set Test\SetTe st.cpp(203 ) : error C2664: 'WriteProcessMemory' : cannot convert parameter 3 from 'const char *' to 'void *'
Conversion loses qualifiers
C:\Projects\C++\TEST06\Set Test\SetTe st.cpp(204 ) : error C2664: 'WriteProcessMemory' : cannot convert parameter 3 from 'const char *' to 'void *'
Conversion loses qualifiers
Error executing cl.exe.
and the dir references. Now when I try to compile I get the following two errors:
But I don't know enough about how the API works to troubleshoot.
C:\Projects\C++\TEST06\Set
Conversion loses qualifiers
C:\Projects\C++\TEST06\Set
Conversion loses qualifiers
Error executing cl.exe.
>> I wondered why you wanted the SDK loaded
Experts are not infallible. I just made a mistake, and I already apologized.
now to your issue:
add a (void*) typecast in front of szEnvVar and szEnvValue
...
WriteProcessMemory(hProc, pString1, (void *)szEnvVar, stringsize1, NULL) &&
(pString2 == NULL || WriteProcessMemory(hProc, pString2, (void *)szEnvValue, stringsize2, NULL)))
{
...
Experts are not infallible. I just made a mistake, and I already apologized.
now to your issue:
add a (void*) typecast in front of szEnvVar and szEnvValue
...
WriteProcessMemory(hProc, pString1, (void *)szEnvVar, stringsize1, NULL) &&
(pString2 == NULL || WriteProcessMemory(hProc, pString2, (void *)szEnvValue, stringsize2, NULL)))
{
...
ASKER
I did not mean to insult, I appreciate all the work.
I added the typecast and that resolved the two errors listed, but another error showed when I tried to compile:
LINK : fatal error LNK1104: cannot open file "psapi.lib"
But I don't see psapi anywhere in the code ?
I included everything that I have so far.
I added the typecast and that resolved the two errors listed, but another error showed when I tried to compile:
LINK : fatal error LNK1104: cannot open file "psapi.lib"
But I don't see psapi anywhere in the code ?
I included everything that I have so far.
// SetTest.cpp : Defines the entry point for the console application.
//
//#include "stdafx.h"
//int main(int argc, char* argv[])
//{
// printf("Hello World!\n");
// return 0;
//}
// SetParentEnv.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
//#include <SetupAPI.h>
//#include <stdlib.h>
//#include <stdio.h>
//#include <Psapi.h>
//#include "Psapi.h"
#include <tlhelp32.h>
#include <tchar.h>
//#pragma comment(lib, "Psapi.lib")
BOOL WINAPI GetParentPID(PROCESSENTRY32& procentry)
{
OSVERSIONINFO osver;
HINSTANCE hInstLib;
HANDLE hSnapShot;
BOOL bContinue;
// ToolHelp Function Pointers.
HANDLE (WINAPI *lpfCreateToolhelp32Snapshot)(DWORD,DWORD);
BOOL (WINAPI *lpfProcess32First)(HANDLE,LPPROCESSENTRY32);
BOOL (WINAPI *lpfProcess32Next)(HANDLE,LPPROCESSENTRY32);
// Check to see if were running under Windows95 or
// Windows NT.
osver.dwOSVersionInfoSize = sizeof(osver);
if (!GetVersionEx(&osver))
return FALSE;
if (osver.dwPlatformId != VER_PLATFORM_WIN32_NT)
return FALSE;
hInstLib = GetModuleHandle(_T("kernel32.dll"));
if (hInstLib == NULL)
return FALSE ;
// Get procedure addresses.
// We are linking to these functions of Kernel32
// explicitly, because otherwise a module using
// this code would fail to load under Windows NT,
// which does not have the Toolhelp32
// functions in the Kernel 32.
lpfCreateToolhelp32Snapshot =
(HANDLE(WINAPI *)(DWORD,DWORD))
GetProcAddress(hInstLib, "CreateToolhelp32Snapshot");
#ifdef _UNICODE
lpfProcess32First =
(BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32))
GetProcAddress(hInstLib, "Process32FirstW");
lpfProcess32Next =
(BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32))
GetProcAddress(hInstLib, "Process32NextW");
#else
lpfProcess32First =
(BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32))
GetProcAddress(hInstLib, "Process32First");
lpfProcess32Next =
(BOOL(WINAPI *)(HANDLE,LPPROCESSENTRY32))
GetProcAddress(hInstLib, "Process32Next");
#endif
if (lpfProcess32Next == NULL ||
lpfProcess32First == NULL ||
lpfCreateToolhelp32Snapshot == NULL)
{
return FALSE;
}
// Get a handle to a Toolhelp snapshot of the systems
// processes.
hSnapShot = lpfCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapShot == INVALID_HANDLE_VALUE)
return FALSE;
// Get the first process' information.
ZeroMemory(&procentry, sizeof(PROCESSENTRY32));
procentry.dwSize = sizeof(PROCESSENTRY32);
bContinue = lpfProcess32First(hSnapShot, &procentry);
DWORD pid = 0;
// While there are processes, keep looping.
DWORD crtpid = GetCurrentProcessId();
while (bContinue)
{
if (crtpid == procentry.th32ProcessID)
pid = procentry.th32ParentProcessID;
procentry.dwSize = sizeof(PROCESSENTRY32);
bContinue = !pid && lpfProcess32Next(hSnapShot, &procentry);
}
return pid
? TRUE
: FALSE;
}
typedef struct _DATA
{
LPVOID SetEnvironmentVariableAddr;
TCHAR *lpName;
TCHAR *lpValue;
} THRDDATA, *LPTHRDDATA;
static DWORD __declspec(naked) WINAPI ThreadProc(LPTHRDDATA pData)
{
__asm
{
//standard prolog
PUSH EBP
MOV EBP, ESP
//move pData in EBX
PUSH EBX
MOV EBX, DWORD PTR [EBP+0x08]
//call BOOL bRet = pData->SetEnvironmentVariableAddr(pData->lpName, pData->lpValue)
PUSH DWORD PTR [EBX+0x08]
PUSH DWORD PTR [EBX+0x04]
CALL DWORD PTR [EBX]
//return bRet ? 0 : 1
NEG EAX
SBB EAX, EAX
INC EAX
POP EBX
//standard epilog
POP EBP
RET 0x0004
}
}
BOOL WINAPI SetParentEnvironmentVariable(
const TCHAR *szEnvVar,
const TCHAR *szEnvValue)
{
if (szEnvVar == NULL)
return FALSE;
PROCESSENTRY32 selfprocentry;
BOOL bRet = FALSE;
if (GetParentPID(selfprocentry))
{
HANDLE hProc = NULL;
if ((hProc = OpenProcess(PROCESS_CREATE_THREAD |
PROCESS_VM_OPERATION |
PROCESS_VM_READ |
PROCESS_VM_WRITE |
PROCESS_QUERY_INFORMATION, FALSE, selfprocentry.th32ParentProcessID)) != NULL)
{
size_t len1 = _tcslen(szEnvVar) + 1;
size_t len2 = szEnvValue == NULL
? 0
: _tcslen(szEnvValue) + 1;
HMODULE hKernel = GetModuleHandle(_T("kernel32.dll"));
size_t codesize = 32;
size_t datasize = sizeof(THRDDATA);
size_t stringsize1 = len1 * sizeof(TCHAR);
size_t stringsize2 = len2 * sizeof(TCHAR);
LPVOID pCode = (LPVOID)VirtualAllocEx(hProc, NULL, codesize + datasize + stringsize1 + stringsize2,
MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (pCode != NULL)
{
LPVOID pData = (BYTE*)pCode + codesize;
LPVOID pString1 = (BYTE*)pData + datasize;
LPVOID pString2 = szEnvValue == NULL
? NULL
: (BYTE*)pString1 + stringsize1;
THRDDATA data;
#ifdef _UNICODE
data.SetEnvironmentVariableAddr = (LPVOID)GetProcAddress(hKernel, "SetEnvironmentVariableW");
#else
data.SetEnvironmentVariableAddr = (LPVOID)GetProcAddress(hKernel, "SetEnvironmentVariableA");
#endif
data.lpName = (TCHAR*)pString1;
data.lpValue = (TCHAR*)pString2;
if (WriteProcessMemory(hProc, pCode, &ThreadProc, codesize, NULL) &&
WriteProcessMemory(hProc, pData, &data, datasize, NULL) &&
WriteProcessMemory(hProc, pString1, (void *)szEnvVar, stringsize1, NULL) &&
(pString2 == NULL || WriteProcessMemory(hProc, pString2, (void *)szEnvValue, stringsize2, NULL)))
{
HANDLE hThread;
if ((hThread = CreateRemoteThread(hProc, NULL, NULL, (LPTHREAD_START_ROUTINE)pCode,
pData, NULL, NULL)) != NULL)
{
DWORD dwExitCode;
bRet = WaitForSingleObject(hThread, 10000) == WAIT_OBJECT_0 &&
GetExitCodeThread(hThread, &dwExitCode) &&
dwExitCode == 0;
CloseHandle(hThread);
}
}
VirtualFreeEx(hProc, pCode, 0, MEM_RELEASE);
}
CloseHandle(hProc);
}
}
return bRet;
}
int _tmain(int argc, TCHAR **argv)
{
if (argc < 2)
return 1;
TCHAR *szEnvVar = argv[1];
TCHAR *szEnvValue = argc > 2
? argv[2]
: NULL;
//set an environment variable in the PARENT process (typically CMD.EXE)
return SetParentEnvironmentVariable(szEnvVar, szEnvValue)
? 0
: 1;
}
//Toggle HighlightingOpen in New Window
look at the project settings; maybe psapi.lib is listed among the "Object/library modules" in the "Link" tab?
ASKER
Excellent code.
I tried to assign more than 500 pts, I think the answer deserves it.
No one else even came close.
Thanks for the help.
I tried to assign more than 500 pts, I think the answer deserves it.
No one else even came close.
Thanks for the help.
The system() command spawns a process that is exactly like the one that is running the calling program. When the process ends, control is returned back to your program.
If the call to the system() function passes a command to execute the SET command, the command is executed in the new process and the process ends. It doesn't affect the process where your program is running.
Kent