프로세스 명령줄 정보 삭제하기

@codemaru · January 14, 2013 · 4 min read

cmdline 커맨드 라인 정보는 어디로??

후배 녀석이 자신이 실행시킨 프로그램 명령줄 정보를 못 보게 하는 방법이 없냐고 해서 만들어 본 프로그램입니다. 위에 보이는 것처럼 프로세스 정보를 조회하는 툴에서 명령줄 정보를 볼 수 없도록 삭제해 줍니다. 다.만. 별로 의미없는 이유는 대부분 프로세스 모니터링 프로그램이 프로세스 생성 시점에 바로 정보를 저장해 두었다가 보여주기 때문에 우리가 정보를 지워도 계속 노출된다는 단점이 있습니다. 우리가 정보를 삭제한 다음에 프로세스 모니터링 도구를 띄워서 체크해보면 위에 보이는 것처럼 삭제된 것을 확인할 수 있습니다. 결국 별로 쓰잘때기 없는 코드라는 이야깁니다. ㅋㅋㅋ~

윈도우에서 명령줄 정보는 PEB의 RTL_USER_PROCESS_PARAMETERS 구조체의 CommandLine 필드에 저장됩니다. 그곳에 저장된 정보를 제거해 주면 다른 프로그램에서 해당 정보를 참조하지 못하겠지요. 이런 원리로 아래 프로그램은 명령줄 정보를 삭제하는 방법을 보여주고 있습니다. 코드 흐름만 보여주기 위해서 에러 처리 따위는 모두 삭제돼 있습니다. 32비트에서만 동작하는데요. 관심 있으신 분들은 64비트 버전을 만들어보는 것도 실력 향상에 도움이 될 것 같습니다. 난 고수다. 실력 쫌 되는데 하시는 분들은 WoW64 프로세스에서 32비트나 64비트 프로세스 정보를 제거하는데 도전해 보세요. 64비트는 아래 구조체도 일부 포함시켜두었기 때문에 크게 어렵진 않은데, WoW64는 초큼 어렵습니다 ㅋ~

#include <windows.h>

#pragma pack(1)

typedef struct _PEB32
{
	ULONG reserved[4];
	ULONG param;
} PEB32, *PPEB32;

typedef struct _PEB64
{
	ULONG64 reserved[4];
	ULONG64 param;
} PEB64, *PPEB64;

typedef struct _UNICODE_STRING32
{
	USHORT  Length;
	USHORT  MaximumLength;
	ULONG  Buffer;
} UNICODE_STRING32, *PUNICODE_STRING32;

typedef struct _UNICODE_STRING64 
{
	USHORT  Length;
	USHORT  MaximumLength;
	ULONG Pad1;
	ULONG64  Buffer;
} UNICODE_STRING64, *PUNICODE_STRING64;

typedef struct _RUPP32 {
	ULONG reserved1;
	ULONG reserved2;

	ULONG reserved3;
	ULONG reserved4;

	ULONG reserved5[5];

	ULONG reserved6[3];
	UNICODE_STRING32 path;
	UNICODE_STRING32 image;
	UNICODE_STRING32 cmd;

} RUPP32, *PRUPP32;

typedef struct _RUPP64 {
	ULONG reserved1;
	ULONG reserved2;

	ULONG reserved3;
	ULONG reserved4;

	ULONG64 reserved5[5];

	UNICODE_STRING64 reserved6;
	ULONG64 reserved7;

	UNICODE_STRING64 path;
	UNICODE_STRING64 image;
	UNICODE_STRING64 cmd;

} RUPP64, *PRUPP64;

typedef __success(return >= 0) LONG NTSTATUS, *PNTSTATUS;

typedef struct _PROCESS_BASIC_INFORMATION32
{
    NTSTATUS ExitStatus;
    ULONG PebBaseAddress;
    ULONG AffinityMask;
    ULONG BasePriority;
    HANDLE UniqueProcessId;
    HANDLE InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION32, *PPROCESS_BASIC_INFORMATION32;

typedef struct _PROCESS_BASIC_INFORMATION64 {
    NTSTATUS ExitStatus;
	ULONG Pad1;
    PVOID64 PebBaseAddress;
    ULONGLONG AffinityMask;
    ULONGLONG BasePriority;
    PVOID64 UniqueProcessId;
    PVOID64 InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION64, *PPROCESS_BASIC_INFORMATION64;

#ifdef _WIN64
#define PROCESS_BASIC_INFORMATION PROCESS_BASIC_INFORMATION64
#define PPROCESS_BASIC_INFORMATION PPROCESS_BASIC_INFORMATION64
#else
#define PROCESS_BASIC_INFORMATION PROCESS_BASIC_INFORMATION32
#define PPROCESS_BASIC_INFORMATION PPROCESS_BASIC_INFORMATION32
#endif


#pragma pack()

typedef NTSTATUS (WINAPI *NtQueryInformationProcessT)(HANDLE, LONG, PVOID, ULONG, PULONG);

void CreateProcessAndClean(LPWSTR cmdline)
{
	PROCESS_INFORMATION pi;
	STARTUPINFO si = { sizeof(si) };

	CreateProcess(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);

	printf("press any key to clean command line information.\n");
	getchar();

	ULONG req;
	PROCESS_BASIC_INFORMATION pbi;

	HANDLE process = pi.hProcess;

	NtQueryInformationProcessT pNtQueryInformationProcess;
	
	HMODULE ntdll = GetModuleHandleW(L"ntdll.dll");
	pNtQueryInformationProcess 
		= (NtQueryInformationProcessT) 
			GetProcAddress(ntdll, "NtQueryInformationProcess");

	pNtQueryInformationProcess(process
									, 0
									, &pbi
									, sizeof(pbi)
									, &req);
	SIZE_T readed;
	PEB32 peb32;
	RUPP32 rupp32;

	ReadProcessMemory(process
						, (PVOID) pbi.PebBaseAddress
						, &peb32
						, sizeof(peb32)
						, &readed);

	ReadProcessMemory(process
						, (PVOID) peb32.param
						, &rupp32
						, sizeof(rupp32)
						, &readed);

	SIZE_T written;
	WCHAR cmd[MAX_PATH] = L"";

	ReadProcessMemory(process
						, (PVOID) rupp32.cmd.Buffer
						, cmd
						, sizeof(*cmd) * rupp32.cmd.Length
						, &readed);

	printf("%p %ws %d\n", rupp32.cmd.Buffer, cmd, pi.dwProcessId);

	rupp32.cmd.Length = 0;
	WriteProcessMemory(process
						, (PVOID) peb32.param
						, &rupp32
						, sizeof(rupp32)
						, &written);

	CloseHandle(pi.hProcess);
	CloseHandle(pi.hThread);

}

int _tmain(int argc, _TCHAR* argv[])
{
	WCHAR cmd[] = L"c:\\windows\\system32\\notepad.exe c:\\abc.txt";
	CreateProcessAndClean(cmd);

	getchar();

	return 0;
}
@codemaru
돌아보니 좋은 날도 있었고, 나쁜 날도 있었다. 그런 나의 모든 소소한 일상과 배움을 기록한다. 여기에 기록된 모든 내용은 한 개인의 관점이고 의견이다. 내가 속한 조직과는 1도 상관이 없다.
(C) 2001 YoungJin Shin, 0일째 운영 중