이 블로그 검색

2015년 11월 23일 월요일

출처 : http://01073144993.tistory.com/73

자세한 API 스펙은 MSDN에서 참조하시는게 더 빠를것이다. 

BOOL WINAPI CreateProcess(
  _In_opt_     LPCTSTR lpApplicationName,
  _Inout_opt_  LPTSTR lpCommandLine,
  _In_opt_     LPSECURITY_ATTRIBUTES lpProcessAttributes,
  _In_opt_     LPSECURITY_ATTRIBUTES lpThreadAttributes,
  _In_         BOOL bInheritHandles,
  _In_         DWORD dwCreationFlags,
  _In_opt_     LPVOID lpEnvironment,
  _In_opt_     LPCTSTR lpCurrentDirectory,
  _In_         LPSTARTUPINFO lpStartupInfo,
  _Out_        LPPROCESS_INFORMATION lpProcessInformation
);
해킹 공부를 하다보면 기본적으로 알아야하는 API이다.
리눅스 함수 fork()+execv()와 같은 기능을 하고 있으며
생성할 프로세스의 세팅값. 호출후 생성된 프로세스의 정보를 가져와 주는 유용한 옵션이 많다.
반드시 숙지하라! 개발이든 해킹이든 꼼꼼해야한다.
파라미터 1.
LPCTSTR lpApplicationName
실행할 파일의 전체 경로나 부분경로(현재폴더)를 입력 받는다.
단, 확장자를 포함한다. 첫번째 파라미터가 NULL일 경우 두번째 매개변수의
 첫번째 공백까지(문자열) 실행할 파일의 경로가 와야한다. 
파라미터 2.
_Inout_opt_  LPTSTR lpCommandLine
옵션 매개변수이지만 유용하다고 생각한다. 
- lpApplicationName이 NULL이 아니라면 어플리케이션을 실행하고
실행된 프로세스에서 GetCommandLine에 의해 매개변수로 읽혀진다.
- 첫번째 매개변수가 NULL 일 경우 실행파일의 경로가 처음에 와야한다.
유니코드 버전 API일 경우 상수문자열이 와선 안된다. API에 의해 버퍼의 값이
바뀔 수 있기 때문이다.
<실행모듈을 탐색하는 순서>




  1. The directory from which the application loaded.
  2. The current directory for the parent process.
  3. The 32-bit Windows system directory. Use the GetSystemDirectory function to get the path of this directory.
  4. The 16-bit Windows system directory. There is no function that obtains the path of this directory, but it is searched. The name of this directory is System.
  5. The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
  6. The directories that are listed in the PATH environment variable. Note that this function does not search the per-application path specified by the App Paths registry key. To include this per-application path in the search sequence, use the ShellExecute function.
The system adds a terminating null character to the command-line string to separate the file name from the arguments. This divides the original string into two strings for internal processing.


파라미터 3.
_In_opt_     LPSECURITY_ATTRIBUTES lpProcessAttributes
위 매개변수는 구조체이다. NULL을 주면 기본 SecurityDescriptor를 갖게 된다.
typedef struct _SECURITY_ATTRIBUTES {
  DWORD  nLength;
  LPVOID lpSecurityDescriptor;
  BOOL   bInheritHandle;
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;
1번째 파라미터는 sizeof(SECURITY_ATTRIBUTES)를 넣으면된다.
2번째 파라미터는 object에 접근 권한을 조절 해준다. 아직 써본적은 없다. NULL(calling process의 security descriptor)
3번째 파라미터는 핸들을 상속할지 여부를 줄 수 있다.

파라미터 4.
_In_opt_     LPSECURITY_ATTRIBUTES lpThreadAttributes
파라미터 3과 동일하다

파라미터 5.
_In_         BOOL bInheritHandles,
부모 프로세스(calling process)에게 핸들을 상속 받을지 결정한다.
상속 받는다면 부모와 같은 값과 권한을 가지게된다.(같은 값인지 이해가 잘안됨...)

파라미터 6.
_In_         DWORD dwCreationFlags,
눈에 띄는 플래그 몇개를 써보자
Default (0x00000000) : 부모의 콘솔을 상속받는다.
CREATE_NEW_CONSOLE (0x00000010) : 부모 콘솔을 상속받지않고 새로운 콘솔을 띄움
CREATE_NO_WINDOW(0x08000000) : 위 플래그와 같이 사용 할 수 없으며 Console-app을 
Colsole을 띄위지 않고 실행 시킨다.
DEBUG_PROCESS(0x00000001) : 부모 프로세스가 디버거가 되고 새로운 프로세스는 디버기가 된다.
파라미터 7.
  _In_opt_     LPVOID lpEnvironment,
새로운 프로세스의 환경변수 포인터이다. 기본적으로 NULL을 주면 상속 받는다.
파라미터 8.
_In_opt_     LPCTSTR lpCurrentDirectory,
프로세스의 현재 디렉토리를 명시한다. 기본적으로 NULL을 주자. 쓸일이 많이 없을것 같다.

파라미터 9.
  _In_         LPSTARTUPINFO lpStartupInfo,

파라미터 9.
  _Out_        LPPROCESS_INFORMATION lpProcessInformation

파라미터 조합 : 
표준입출력 리다이렉션과 독리적으로 동작함. 관계없음
<부모 콘솔을 사용 하려면 dwCreationFlags NULL을 주면된다.>
부모 프로세스(콘솔)가 ExitProcess(0);로 종료하거나 return;으로 종료 될 경우 콘솔 윈도우는 살아있다. (자식이 사용)
taskmgr.exe (태스크매니저)에서 부모프로세스를 종료 할경우도 살아있다. 즉 자식이 상속받으면 윈도우는 계속 살아있다.

<부모 콘솔을 사용 하지않고 새로운 콘솔을 만듬>
dwCreationFlags = CREATE_NEW_CONSOLE
<부모 콘솔을 사용 하지않고 새로운 콘솔을 만듬 + 새로운 콘솔(윈도우)을 숨김>
dwCreationFlags = CREATE_NEW_CONSOLE
lpStartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
lpStartupInfo.wShowWindow = SW_HIDE;

<부모 콘솔을 사용 하지않고 새로운 콘솔을 만듬 + 새로운 콘솔에게 윈도우를 주지 않음>
dwCreationFlags = CREATE_NO_WINDOW

우선순위(Priority) 스케줄링(Scheduling) 알고리즘

우선순위(Priority) 스케줄링(Scheduling) 알고리즘 :

각각의 프로세스마다 우선순위를 부여해서 우선순위가 높은 프로세스를 먼저 실행한다.

우선순위가 다른 두 프로세스를 동시 실행할 때,

우선순위가 높은 프로세스가 작업을 마치지 않는다면(블로킹 상태가 되거나 I/O 작업을 하지 않는다면)
우선순위가 낮은 프로세스는 절대로 실행되지 않는다. (기아 상태 : Starvation)



라운드 로빈(Round-Robin) 스케줄링 알고리즘 :

우선순위가 동일한 프로세스들의 형평성 유지를 위해
정해진 시간 간격(타임 슬라이스(Time Slice), 퀀텀(Quantum))만큼만 실행하고 CPU 할당을 넘긴다.

타임 슬라이스 ↑ -> 반응속도 ↓
타임 슬라이스 ↓ -> 성능 ↓ (잦은 컨텍스트 스위칭 발생으로 인하여 시스템이 무리가 가서 성능이 저하된다)



Windows의 스케줄링 알고리즘 :


스케줄링이 진행되는 시점 :

1. 매 타임 슬라이스마다 스케줄러 동작.
2. 프로세스가 생성 및 소멸될 때마다 스케줄러 동작
3. 현재 실행 중인 프로세스가 블로킹 상태에 놓일 때마다 스케줄러 동작



Windows 프로세스 우선순위 :

 Priority 의미
 IDLE_PRIORITY_CLASS 기존 우선순위 4
 NORMAL_PRIORITY_CLASS 기존 우선순위 9
 HIGH_PRIORITY_CLASS 기존 우선순위 13
 REALTIME_PRIORITY_CLASS 기존 우선순위 24
 ABOVE_NORMAL_PRIORITY_CLASS NORMAL_PRIORITY_CLASS 보다 높고
 HIGH_PRIORITY_CLASS 보다 낮다.( 9< x < 13 )
 BELOW_NORMAL_PRIORITY_CLASS IDLE_PRIORITY_CLASS 보다 높고
 NORMAL_PRIORITY_CLASS 보다 낮다. ( 4 < x < 9 )

프로세스 우선순위 변경 :

BOOL SetPriorityClass(
        HANDLE hProcess,              // 우선순위를 변경할 프로세스의 핸들
        DWORD dwPriorityClass        // 새롭게 적용할 우선순위 상수값
);


Priority Inversion :

프로세스의 우선순위가 뒤바뀌는 상황

(예 : 

프로세스 A > 프로세스 B > 프로세스 C (우선순위)

프로세스 A가 실행이 되다가 프로세스 C에게 데이터를 받을 일이 생겼다.

프로세스 C가 실행이 되게 하기 위해 자신이 Blocked 상태가 되었다.

하지만 프로세스 C보다 우선순위가 높은 프로세스 B가 실행이 되어 프로세스 C가 실행이 되지 않으므로 

프로세스 A 또한 실행이 될수 없게 된다.
(즉, 프로세스 A 보다 우선순위가 낮은 프로세스 B가 우선순위가 가장 높은것처럼 되었다))

2015년 11월 19일 목요일

SYSTEM 권한(Windows Service) 에서 일반사용자 (user) 권한으로 프로세스 실행

[출처] http://kimsuny.egloos.com/v/2752669 

INT_PTR ExcuteAsUser(TCHAR* szCmd)
{
 BOOL bRet;
 INT_PTR hr;
 HANDLE processToken = NULL;
 TOKEN_PRIVILEGES oldTokenPrivileges = { 0 };
 HANDLE impersonationToken = NULL;
 HANDLE userToken = NULL;
 LPVOID pEnvironment = NULL;
 PROCESS_INFORMATION processInformation = { 0 };
 __try {

  bRet = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &processToken);
  if (!bRet) {
   hr = GetLastError();
   return hr;
  }
  
  // This step might not be necessary because SeTcbPrivilege is enabled by default for Local System
  LUID luid;
  bRet = LookupPrivilegeValue(NULL, SE_TCB_NAME, &luid);
  if (!bRet) {
   hr = GetLastError();
   return hr;
  }
  
  TOKEN_PRIVILEGES adjTokenPrivileges = { 0 };
  adjTokenPrivileges.PrivilegeCount = 1;
  adjTokenPrivileges.Privileges[0].Luid = luid;
  adjTokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  DWORD dwOldTPLen;
  bRet = AdjustTokenPrivileges(processToken, FALSE, &adjTokenPrivileges, sizeof(TOKEN_PRIVILEGES), &oldTokenPrivileges, &dwOldTPLen);
  if (bRet) {
   hr = GetLastError();
   if (hr == ERROR_SUCCESS);
   else if (hr == ERROR_NOT_ALL_ASSIGNED) {
    // Enabled by default
   }
  }
  else {
   hr = GetLastError();
   return hr;
  }

//WTSGetActiveConsoleSessionId, WTSQueryUserToken 함수를 Kernel32.dll에서 직접가져오는 이유는
//원래 함수가 XP이상 지원이라서..
  HMODULE hInstKernel32 = NULL;
  HMODULE hInstWtsapi32 = NULL;
  typedef DWORD (WINAPI *WTSGetActiveConsoleSessionIdPROC)();
  WTSGetActiveConsoleSessionIdPROC WTSGetActiveConsoleSessionId = NULL;
  hInstKernel32 = LoadLibrary("Kernel32.dll");
  if (!hInstKernel32)
  {
   return -1;
  }
  WTSGetActiveConsoleSessionId = (WTSGetActiveConsoleSessionIdPROC)GetProcAddress(hInstKernel32,"WTSGetActiveConsoleSessionId");

  DWORD conSessId = WTSGetActiveConsoleSessionId();
  if (conSessId == 0xFFFFFFFF) {
   // There is no session attached to the console
   return ERROR_SUCCESS;
  }

  typedef BOOL (WINAPI *WTSQueryUserTokenPROC)(ULONG SessionId, PHANDLE phToken );
  WTSQueryUserTokenPROC WTSQueryUserToken = NULL;
  hInstWtsapi32 = LoadLibrary("Wtsapi32.dll");
  if (!hInstWtsapi32)
  {
   return -1;
  }
  WTSQueryUserToken = (WTSQueryUserTokenPROC)GetProcAddress(hInstWtsapi32,"WTSQueryUserToken");
  bRet = WTSQueryUserToken(conSessId, &impersonationToken);
  if (!bRet) {
   hr = GetLastError();
   return hr;
  }

  bRet = DuplicateTokenEx(impersonationToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &userToken);
  if (!bRet) {
   hr = GetLastError();
   return hr;
  }
  STARTUPINFO si = { 0 };
  si.cb = sizeof(STARTUPINFO);
  si.lpDesktop = _T("winsta0\\default");

  bRet = CreateEnvironmentBlock(&pEnvironment, userToken, TRUE);
  if (!bRet) {
   hr = GetLastError();
   return hr;
  }

  bRet = CreateProcessAsUser(
   userToken,
   szCmd,
   NULL,
   NULL,
   NULL,
   FALSE,
   CREATE_UNICODE_ENVIRONMENT,
   pEnvironment,
   NULL,
   &si,
   &processInformation
   );
  if (!bRet) {
   hr = GetLastError();
   return hr;
  }
 }
 __finally {
  if (processInformation.hThread) {
   CloseHandle(processInformation.hThread);
  }
  if (processInformation.hProcess) {
   CloseHandle(processInformation.hProcess);
  }
  if (pEnvironment) {
   bRet = DestroyEnvironmentBlock(pEnvironment);
  }
  if (userToken) {
   CloseHandle(userToken);
  }
  if (impersonationToken) {
   CloseHandle(impersonationToken);
  }
  if (processToken) {
   bRet = AdjustTokenPrivileges(processToken, FALSE, &oldTokenPrivileges, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
   CloseHandle(processToken);
  }
 }
 return 0;
}