# Manual syscalls

수동 시스템 콜은 Windows의 ntdll.dll에 있는 네이티브 API를 사용하지 않고, 직접 커스텀한 syscall stub을 통해 syscall 명령어를 실행하여 커널 함수를 호출하는 기법입니다.

## Direct syscalls

일반적인 경우에서 사용자가 시스템 콜을 호출할 때는 커널 모드로 진입하는 네이티브 API를 호출하여 간접적으로 커널 모드로 진입합니다.

<figure><img src="/files/bbRb2b7JRdnJQtHUQirM" alt=""><figcaption><p><a href="https://paolozaino.wordpress.com/2013/05/22/system-calls-part-i/">https://paolozaino.wordpress.com/2013/05/22/system-calls-part-i/</a></p></figcaption></figure>

하지만 커널 모드로 진입하는 API 자체를 직접 빌드한다면 네이티브 API를 호출하지 않고도 커널 모드에 진입하는 것이 가능합니다.

Visual Studio에서는 어셈블리 코드를 C(++)로 작성된 프로그램으로 컴파일 할 수 있는 사용자 지정 빌드(MASM)가 존재하여 직접 시스템 콜 호출 코드를 만들 수 있습니다.

{% code title="custom syscall stub" %}

```asm
.code

	NtOpenProcess proc
		mov r10, rcx
		mov eax, 26h
		syscall
		ret
	NtOpenProcess endp

	NtClose proc
		mov r10, rcx
		mov eax, 0Fh
		syscall
		ret
	NtClose endp

end
```

{% endcode %}

위 코드에서는 NtOpenProcess API에 대해 직접 호출을 하기 때문에 네이티브 API를 호출하지 않습니다.

다음 사진은 정상적으로 NtOpenProcess를 호출하는 파일과, 직접 syscalls를 호출하는 파일에 대해 브레이크 포인트 및 호출 스택을 확인하여 NtOpenProcess가 사용되었는지 확인한 결과입니다.

<div><figure><img src="/files/qe5WioGb793mrhjPDQkN" alt=""><figcaption><p>정상 syscalls 호출</p></figcaption></figure> <figure><img src="/files/7zPJTWfILf4f8N0Jzk4X" alt=""><figcaption><p>직접 syscalls 호출</p></figcaption></figure></div>

사진에서 확인할 수 있듯 정상 호출의 경우 NtOpenProcess에서 브레이크 포인트를 지정하면 실행이 멈추는 반면, 직접 시스템 호출의 경우 실행이 종료되고 호출 스택에서도 NtOpenProcess는 기록되지 않습니다.

## Indirect syscalls

사용자 모드에서 커널 모드로 진입하는 네이티브 API 호출에서는 대부분 동일한 syscall stub을 따르며, 유일한 차이점으로 SSN(System Service Number)이 있습니다.

네이티브 API 종류에 상관없이 syscall stub이 동일하다는 것은 SSN을 조작하면 다른 API를 호출할 수 있다는 것을 의미합니다.

<figure><img src="/files/sO1fNLdNCnhLBZ6xtitb" alt=""><figcaption><p><a href="https://github.com/VirtualAlllocEx/DEFCON-31-Syscalls-Workshop/wiki/04:-Chapter-2-%7C-Windows-OS-System-Calls">https://github.com/VirtualAlllocEx/DEFCON-31-Syscalls-Workshop/wiki/04:-Chapter-2-%7C-Windows-OS-System-Calls</a></p></figcaption></figure>

NtOpenProcess를 사용하고자 하지만 EDR이 해당 함수를 후킹하여 모니터링 중이라고 가정하겠습니다.

공격자는 NtOpenProcess의 SSN(0x26)을 설정한 뒤, EDR이 후킹하지 않는 함수(여기선 NtClose) 내부의 syscall 명령어 위치로 직접 점프합니다. 이를 통해 호출 스택에는 NtClose에서 정상적으로 syscall이 발생한 것처럼 보이지만, 커널에서는 SSN 값에 따라 실제로 NtOpenProcess가 실행됩니다.

{% hint style="info" %}
jmp는 call과 달리 return address를 저장하지 않고, 호출 위치로 돌아오지 않기 때문에 호출 스택이 남지 않습니다.
{% endhint %}

{% code title="custom syscall stub" %}

```asm
EXTERN ntCloseSyscall:QWORD

.code

	IndirectSyscalls proc
		mov r10, rcx
		mov eax, 26h					; <- ssn of NtOpenProcess
		jmp QWORD PTR [ntCloseSyscall]  ; <- jmp to syscall in NtClose
	IndirectSyscalls endp

end
```

{% endcode %}

위 코드에서 만약 call을 사용했다면 호출 스택은 `main → IndirectSyscalls → ntdll!NtClose (syscall)` 가 되는 반면 jmp를 사용하면 `main → ntdll!NtClose (syscall)` 가 됩니다.

이로 인해 공격자는 커스텀 네이티브 API 함수 내에서 사용하고자 하는 API의 SSN을 통해 API를 호출하는 동시에, EDR에서 주로 후킹하지 않는 비주류 API로 점프하여 탐지를 우회할 수 있게됩니다.

## References

{% embed url="<https://kk-eezz.tistory.com/41>" %}

{% embed url="<https://github.com/VirtualAlllocEx/DEFCON-31-Syscalls-Workshop/wiki/04:-Chapter-2-%7C-Windows-OS-System-Calls>" %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://www.pentestwiki.com/defense-evasion/system-calls/manual-syscalls.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
