성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 그냥 RSS Reader 기능과 약간의 UI 편의성 때문에 사용...
[이종효] 오래된 소프트웨어는 보안 위협이 되기도 합니다. 혹시 어떤 기능...
[정성태] @Keystroke IEEE의 문서를 소개해 주시다니... +_...
[손민수 (Keystroke)] 괜히 듀얼채널 구성할 때 한번에 같은 제품 사라고 하는 것이 아...
[정성태] 전각(Full-width)/반각(Half-width) 기능을 토...
[정성태] Vector에 대한 내용은 없습니다. Vector가 닷넷 BCL...
[orion] 글 읽고 찾아보니 디자인 타임에는 InitializeCompon...
[orion] 연휴 전에 재현 프로젝트 올리자 생각해 놓고 여의치 않아서 못 ...
[정성태] 아래의 글에 정리했으니 참고하세요. C# - Typed D...
[정성태] 간단한 재현 프로젝트라도 있을까요? 저런 식으로 설명만 해...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>다른 프로세스에 환경 변수 설정하는 방법</h1> <p> <br /> 이에 대해서 지난번에 Appinit_Dlls를 통해서 구현해 봤었는데요.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Appinit_Dlls로 구현한 환경 변수 설정 DLL ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/883'>http://www.sysnet.pe.kr/2/0/883</a> </pre> <br /> 아쉽게도, Vista 이후부터 강화된 보안으로 인해 가능한 "인증서 서명"을 하는 것이 권장되므로 사용하는 것이 녹록치 않습니다.<br /> <br /> 그래서, 다시 생각해 본 것이 "CreateRemoteThread"입니다. 이를 이용한 DllInjection 기법이 사용되곤 하는데, 특정 exe에만 Appinit_Dlls 레지스트리를 통해서 로드하던 바로 그 dll을 실행시키면 자연스럽게 환경 변수 설정이 되기 때문입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { SetEnvironmentVariable(L"TEST", "1"); // Kernel32.dll return FALSE; // FALSE를 반환하므로 대상 EXE에 어떤 부작용도 발생시키지 않음. } </pre> <br /> 어떻게 구현하는지에 대해서는 웹을 검색해 보면 잘 설명되어 있는 자료를 쉽게/많이 찾을 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > DLL injection with CreateRemoteThread() ; <a target='tab' href='http://blog.gwangyi.kr/entry/DLL-injection-with-CreateRemoteThread'>http://blog.gwangyi.kr/entry/DLL-injection-with-CreateRemoteThread</a> </pre> <br /> 이에 따라서 코딩을 해보면 다음과 같이 끝나고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > #include "stdafx.h" #include <Windows.h> #include <stdio.h> int _tmain(int argc, _TCHAR* argv[]) { // char szDllName[] = "D:\\RemoteSetEnvVar\\Debug\\SetEnvVariable.dll"; char szDllName[] = "D:\\RemoteSetEnvVar\\x64\\Debug\\SetEnvVariable.dll"; HANDLE hProc = NULL; LPVOID pRemoteDll = NULL; BOOL result = 1; do { DWORD dwPID = 5012; if (argc == 2) { dwPID = _ttoi(argv[1]); } hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID); if (hProc == NULL) { break; } pRemoteDll = VirtualAllocEx(hProc, NULL, sizeof(szDllName) + 1, MEM_COMMIT, PAGE_READWRITE); if(!pRemoteDll) { break; } if (!WriteProcessMemory(hProc, pRemoteDll, szDllName, sizeof(szDllName) + 1, NULL)) { break; } HMODULE hKernel32 = GetModuleHandle(L"KERNEL32.DLL"); LPTHREAD_START_ROUTINE pfnLoadLibrary = (LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32, "LoadLibraryA"); if (pfnLoadLibrary == NULL) { break; } HANDLE hThread = CreateRemoteThread(hProc, NULL, 0, pfnLoadLibrary, pRemoteDll, 0, NULL); if (hThread == NULL) { break; } WaitForSingleObject(hThread, 1000 * 5); // 최대 5초 대기 DWORD dwRet = 0; if (GetExitCodeThread(hThread, &dwRet) == TRUE) { TCHAR out[1024]; swprintf_s(out, L"Injection finished.\nExit code: 0x%08lX", dwRet); result = 0; } } while (false); if (pRemoteDll != NULL) { // 이 단계에서 VirtualFree를 호출해서는 안된다. // 왜냐하면, 원격 프로세스에서 CreateRemoteThread가 호출되기까지 시간이 걸릴 수 있으므로, // 그사이 메모리가 해제되어 버리면 대상 프로세스가 비정상 종료될 수 있음. // 결과적으로 보면, Memory Leak이 발생하는 것과 같은데, // 만약, 자주 수행되는 경우라면 LoadLibrary로 올라오는 DLL에서 해제하는 방법을 강구해야 한다. // 여기서는 환경 변수를 설정하는 용도로 사용하므로, 메모리 누수에 대해 그다지 심각하지 않다고 판단되어 이렇게 주석처리하는 것으로 완료! // VirtualFreeEx(hProc, pRemoteDll, 0, MEM_RELEASE); pRemoteDll = NULL; } if (hProc != NULL) { CloseHandle(hProc); hProc = NULL; } return result; } </pre> <br /> 주의할 것은, 32비트 프로세스에 대해서는 위의 코드를 실행하는 프로그램이 32비트여야 하고, 64비트에 대해서도 같은 규칙이 적용됩니다. 만약 어긋날 시에는 CreateRemoteThread의 GetLastError 값이 5로 나와서 "Access Denied" 오류 현상이 발생합니다.<br /> <br /> 아쉽게도 한가지 더 주의할 사항이 있는데요. 대상 프로세스가 세션이 다른 경우에는 다시 오류 코드 8로 "Not enough storage is available to process this command." 값이 나옵니다.<br /> <br /> 이에 대해서는 다음의 글에서 잘 설명이 되어 있는데요.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > DLL Injection in Windows 7 (3) ; <a target='tab' href='http://www.reversecore.com/75'>http://www.reversecore.com/75</a> </pre> <br /> Windows Vista 이후의 환경에서는 기존의 세션 0에서 실행되던 서비스 프로세스들이 별도로 분리되어 문제가 되는 것인데, 이는 NtCreateThreadEx를 사용하여 해결할 수 있습니다. 다만, 위의 글에서도 밝혔지만 그 함수가 undocumented라는 것으로 업무용으로 사용하기가 다소 껄끄럽다는 점이 있는데요. 이에 대해서 ^^ 또 다른 해결책이 하나 생각났습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 관리자 권한이 필요한 작업을 COM+에 대행 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/1290'>http://www.sysnet.pe.kr/2/0/1290</a> </pre> <br /> 즉, SYSTEM 권한으로 실행되는 COM+ EXE 서버를 이용하여 위의 코드를 실행하는 EXE를 실행해 주는 것입니다. 그렇게 되면 서비스 세션이 동일하기 때문에 정상적으로 CreateRemoteThread API가 동작하게 되고 환경 변수 설정이 자연스럽게 해결됩니다.<br /> <br /> <a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=729&boardid=331301885'>첨부된 소스 코드는 본문에서 소개한 예제 코드를 담은 간단한 프로젝트</a>입니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1156
(왼쪽의 숫자를 입력해야 합니다.)