윈도우 API 실행
파워쉘에서는 윈도우 API를 실행할 수 있는 총 세 가지 방법이 있다.
Add-Type
.NET 어셈블리가 사용하는 윈도우 API를 찾아 실행하는 방법
윈도우 API의 메모리상 주소를 알아내고, 함수 포인터를 만든 뒤, 다이내믹 Assembly, Module, Type, Method 과 Delegate를 이용해 이 함수 포인터로 윈도우 API를 실행하는 방법
이 페이지에서는 1번과 3번에 대해서 알아본다.
Add-Type
Add-Type 은 마이크로소프트사에서 공식적으로 지원하는 파워쉘에서 윈도우 API를 사용하는 방법 중 하나다. 더 정확하게 말하자면 파워쉘에서 C# 클래스를 작성한 뒤 메모리상에서 컴파일 하고 실행하는 함수 중 하나다.
먼저 PoC 쉘코드로 사용할 쉘코드를 msfvenom을 이용해 제작한다.
msfvenom -p windows/x64/messagebox text="stage0 shellcode" title="choi redteam playbook" -f ps1다음은 파워쉘에서 윈도우 API를 이용해 MessageBox 쉘코드를 실행하는 PoC다.

대응 방안
Add-Type이 실행될 때 C# 코드가 메모리상에서 컴파일 된다고 위에서 얘기했었다. 더 자세하게 얘기하자면 csc.exe 를 이용해 메모리상에서 C# 코드를 컴파일 하지만, 그 와중에 임시 파일을 디스크위에 작성한다.


2022년도 기준 Add-Type 를 이용한 파워쉘에서의 C# 코드 및 윈도우 API 실행은 유명한 TTP라 왠만한 AV/EDR 솔루션들 및 윈도우 디펜더는 이를 기본적으로 막는다.
Dynamic Method
다음 코드들은 다음 두 개의 레퍼런스 - Matt Graeber, mez0, DepthSecurity 라는 분들의 코드 참고했다. 먼저 LookUpFunction 이라는 함수로 현 .NET AppDomain에 있는 어셈블리들 중 System.dll 닷넷 어셈블리를 찾고, 그 중 UnsafeNativeMethod 를 찾는다. 그 뒤, GetMethods() 함수를 통해 닷넷 어셈블리에서 GetProcAddress 와 GetModuleHandle 윈도우API를 찾는다. 그 뒤, 이들을 이용해 파라미터로 받아온 ModuleName과 FunctionName을 찾는다.
결국 $fPointer = LookUpfunction Kernel32.dll VirtualAlloc 과 같은 파워쉘 코드를 이용하면 VirtualAlloc 의 위치를 가르키는 함수 포인터를 생성할 수 있게 된다.
함수 포인터가 있다고 해서 그것을 무작정 사용할 수는 없다. 함수 포인터가 가르키는 메모리는 어떻게 읽어야하는가? 몇개의 argument 가 있고, 어떤 타입의 argument 들이 있는가? 리턴하는 데이터 타입은? 에 대한 설명을 기반으로 함수 포인터를 실행해야 하는데, C#과 파워쉘에서는 이를 Delegate 을 통해서 해결한다.
다음은 파워쉘에서 Delegate 을 만들고 그것을 기반으로 위에서 받아온 함수 포인터를 실행하는 코드다. 다이내믹 어셈블리, 모듈, 타입, 프로토타입, 매써드 등을 만든 뒤 DelegateType을 반환한다.
이 getDelegateType 함수는 다음과 같이 쓴다.
이를 기반으로 위 Add-Type 예시에서 만들었던 메시지 박스 쉘코드를 실행하는 파워쉘 페이로드를 만들면 다음과 같다.
위 Dynamic Method 방법의 경우 C# 이 컴파일이 되는 것이 아니기 때문에 디스크상의 임시 파일이 쓰여질 일도 없다. 따라서 기본적인 AV나 디펜더를 우회하는데 좀 더 유리하다.
레퍼런스
Last updated
