mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-22 20:53:37 +00:00
Translated ['macos-hardening/macos-security-and-privilege-escalation/mac
This commit is contained in:
parent
dd00ffec15
commit
01d6f3ddad
1 changed files with 202 additions and 79 deletions
|
@ -2,31 +2,31 @@
|
|||
|
||||
<details>
|
||||
|
||||
<summary><strong>从零开始学习AWS黑客技术</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE(HackTricks AWS红队专家)</strong></a><strong>!</strong></summary>
|
||||
<summary><strong>从零开始学习AWS黑客技术,成为专家</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE(HackTricks AWS红队专家)</strong></a><strong>!</strong></summary>
|
||||
|
||||
支持HackTricks的其他方式:
|
||||
|
||||
* 如果您想看到您的**公司在HackTricks中做广告**或**下载PDF格式的HackTricks**,请查看[**订阅计划**](https://github.com/sponsors/carlospolop)!
|
||||
* 获取[**官方PEASS和HackTricks周边产品**](https://peass.creator-spring.com)
|
||||
* 探索[**PEASS家族**](https://opensea.io/collection/the-peass-family),我们的独家[NFT](https://opensea.io/collection/the-peass-family)收藏品
|
||||
* **加入** 💬 [**Discord群**](https://discord.gg/hRep4RUj7f) 或 [**电报群**](https://t.me/peass) 或在**Twitter**上**关注**我们 🐦 [**@carlospolopm**](https://twitter.com/hacktricks_live)**。**
|
||||
* 获取[**官方PEASS & HackTricks周边产品**](https://peass.creator-spring.com)
|
||||
* 探索[**PEASS家族**](https://opensea.io/collection/the-peass-family),我们的独家[NFTs](https://opensea.io/collection/the-peass-family)收藏品
|
||||
* **加入** 💬 [**Discord群**](https://discord.gg/hRep4RUj7f) 或 [**电报群**](https://t.me/peass) 或 **关注**我们的**Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks\_live)**。**
|
||||
* 通过向[**HackTricks**](https://github.com/carlospolop/hacktricks)和[**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github仓库提交PR来分享您的黑客技巧。
|
||||
|
||||
</details>
|
||||
|
||||
## **异常级别 - EL(ARM64v8)**
|
||||
|
||||
在ARMv8架构中,执行级别,称为异常级别(ELs),定义了执行环境的特权级别和功能。有四个异常级别,从EL0到EL3,每个都有不同的目的:
|
||||
在ARMv8架构中,执行级别称为异常级别(ELs),定义了执行环境的特权级别和功能。有四个异常级别,从EL0到EL3,每个都有不同的用途:
|
||||
|
||||
1. **EL0 - 用户模式**:
|
||||
* 这是最低特权级别,用于执行常规应用程序代码。
|
||||
* 在EL0上运行的应用程序彼此之间以及与系统软件隔离,增强安全性和稳定性。
|
||||
* 在EL0运行的应用程序彼此之间以及与系统软件隔离,增强安全性和稳定性。
|
||||
2. **EL1 - 操作系统内核模式**:
|
||||
* 大多数操作系统内核在此级别运行。
|
||||
* EL1比EL0具有更多特权,并且可以访问系统资源,但受一些限制以确保系统完整性。
|
||||
3. **EL2 - 虚拟化模式**:
|
||||
* 此级别用于虚拟化。在EL2上运行的虚拟机监视程序可以管理在同一物理硬件上运行的多个操作系统(每个操作系统在其自己的EL1中)。
|
||||
* EL2提供了用于隔离和控制虚拟化环境的功能。
|
||||
* 此级别用于虚拟化。在EL2运行的虚拟机监视程序可以管理在同一物理硬件上运行的多个操作系统(每个在其自己的EL1中)。
|
||||
* EL2提供了隔离和控制虚拟化环境的功能。
|
||||
4. **EL3 - 安全监视器模式**:
|
||||
* 这是最高特权级别,通常用于安全引导和受信任执行环境。
|
||||
* EL3可以管理和控制安全和非安全状态之间的访问(例如安全引导,受信任操作系统等)。
|
||||
|
@ -35,24 +35,24 @@
|
|||
|
||||
## **寄存器(ARM64v8)**
|
||||
|
||||
ARM64有**31个通用寄存器**,标记为`x0`到`x30`。每个寄存器可以存储一个**64位**(8字节)的值。对于需要仅使用32位值的操作,可以使用相同的寄存器以32位模式访问,名称为w0到w30。
|
||||
ARM64有**31个通用寄存器**,标记为`x0`到`x30`。每个可以存储**64位**(8字节)值。对于需要仅使用32位值的操作,可以使用相同的寄存器以32位模式访问,名称为w0到w30。
|
||||
|
||||
1. **`x0`**到**`x7`** - 这些通常用作临时寄存器和用于向子例程传递参数。
|
||||
* **`x0`**还携带函数的返回数据
|
||||
2. **`x8`** - 在Linux内核中,`x8`用作`svc`指令的系统调用号。**在macOS中使用x16!**
|
||||
3. **`x9`**到**`x15`** - 更多临时寄存器,通常用于本地变量。
|
||||
4. **`x16`**和**`x17`** - **过程内调用寄存器**。用于立即值的临时寄存器。它们还用于间接函数调用和PLT(过程链接表)存根。
|
||||
3. **`x9`**到**`x15`** - 更多临时寄存器,通常用于局部变量。
|
||||
4. **`x16`**和**`x17`** - **函数内调用寄存器**。用于立即值的临时寄存器。它们还用于间接函数调用和PLT(过程链接表)存根。
|
||||
* **`x16`**在**macOS**中用作**`svc`**指令的**系统调用号**。
|
||||
5. **`x18`** - **平台寄存器**。它可以用作通用寄存器,但在某些平台上,此寄存器保留用于特定平台用途:在Windows中用作当前线程环境块的指针,或者在Linux内核中用作指向当前**执行任务结构**的指针。
|
||||
6. **`x19`**到**`x28`** - 这些是被调用者保存的寄存器。函数必须保留这些寄存器的值供调用者使用,因此它们存储在堆栈中,并在返回给调用者之前恢复。
|
||||
7. **`x29`** - **帧指针**,用于跟踪堆栈帧。当由于调用函数而创建新的堆栈帧时,**`x29`**寄存器被**存储在堆栈中**,并将新的帧指针地址(**`sp`**地址)**存储在此寄存器中**。
|
||||
* 此寄存器也可以用作**通用寄存器**,尽管通常用作**本地变量的引用**。
|
||||
8. **`x30`**或**`lr`**- **链接寄存器**。在执行`BL`(带链接的分支)或`BLR`(带链接到寄存器的分支)指令时,将**返回地址**存储在此寄存器中通过将**`pc`**值存储在此寄存器中。
|
||||
7. **`x29`** - **帧指针**,用于跟踪堆栈帧。当因为调用函数而创建新的堆栈帧时,**`x29`**寄存器被**存储在堆栈中**,并且新的帧指针地址(**`sp`**地址)被**存储在此寄存器中**。
|
||||
* 此寄存器也可以用作**通用寄存器**,尽管通常用作**局部变量的引用**。
|
||||
8. **`x30`**或**`lr`**- **链接寄存器**。在执行`BL`(带链接的分支)或`BLR`(带链接到寄存器的分支)指令时,通过将**`pc`**值存储在此寄存器中来保存**返回地址**。
|
||||
* 它也可以像其他寄存器一样使用。
|
||||
9. **`sp`** - **堆栈指针**,用于跟踪堆栈顶部。
|
||||
* **`sp`**值应始终保持至少**四字对齐**,否则可能会发生对齐异常。
|
||||
10. **`pc`** - **程序计数器**,指向下一条指令。此寄存器只能通过异常生成、异常返回和分支来更新。可以读取此寄存器的唯一普通指令是带链接的分支指令(BL、BLR)以将**`pc`**地址存储在**`lr`**(链接寄存器)中。
|
||||
11. **`xzr`** - **零寄存器**。在其**32**位寄存器形式中也称为**`wzr`**。可用于轻松获取零值(常见操作)或使用**`subs`**执行比较,例如**`subs XZR,Xn,#10`**将结果数据存储在**`xzr`**中。
|
||||
10. **`pc`** - **程序计数器**,指向下一条指令。此寄存器只能通过异常生成、异常返回和分支来更新。唯一可以读取此寄存器的普通指令是带链接的分支指令(BL、BLR),用于将**`pc`**地址存储在**`lr`**(链接寄存器)中。
|
||||
11. **`xzr`** - **零寄存器**。在其**32**位寄存器形式中也称为**`wzr`**。可用于轻松获取零值(常见操作)或使用**`subs`**执行比较,例如**`subs XZR, Xn, #10`**,将结果数据存储在任何地方(在**`xzr`**中)。
|
||||
|
||||
**`Wn`**寄存器是**`Xn`**寄存器的**32位**版本。
|
||||
|
||||
|
@ -63,73 +63,196 @@ ARM64有**31个通用寄存器**,标记为`x0`到`x30`。每个寄存器可以
|
|||
### 系统寄存器
|
||||
|
||||
**数百个系统寄存器**,也称为特殊目的寄存器(SPRs),用于**监视**和**控制****处理器**的行为。\
|
||||
只能使用专用特殊指令**`mrs`**和**`msr`**读取或设置这些特殊寄存器。
|
||||
只能使用专用指令**`mrs`**和**`msr`**读取或设置它们。
|
||||
|
||||
在逆向工程中通常会发现特殊寄存器**`TPIDR_EL0`**和**`TPIDDR_EL0`**。`EL0`后缀表示可以从中访问寄存器的**最小异常**(在本例中,EL0是常规程序运行的异常级别)。\
|
||||
它们通常用于存储**内存中线程本地存储**区域的**基址**。通常第一个对于在EL0中运行的程序是可读写的,但第二个可以从EL0读取并从EL1写入(如内核)。
|
||||
在逆向工程中通常会发现特殊寄存器**`TPIDR_EL0`**和**`TPIDDR_EL0`**。`EL0`后缀表示可以从中访问寄存器的**最低异常**(在本例中EL0是常规程序运行的特权级别)。\
|
||||
它们通常用于存储内存中线程本地存储区域的**基址**。通常第一个对于在EL0中运行的程序是可读写的,但第二个可以从EL0中读取并从EL1中写入(如内核)。
|
||||
|
||||
* `mrs x0, TPIDR_EL0 ; 将TPIDR_EL0读入x0`
|
||||
* `msr TPIDR_EL0, X0 ; 将x0写入TPIDR_EL0`
|
||||
|
||||
### **PSTATE**
|
||||
|
||||
**PSTATE**包含几个进程组件,序列化到操作系统可见的**`SPSR_ELx`**特殊寄存器中,其中X是**触发的权限级别**的**权限**。\
|
||||
**PSTATE** 包含几个进程组件,序列化到操作系统可见的 **`SPSR_ELx`** 特殊寄存器中,其中 X 是触发的异常的 **权限级别**(这允许在异常结束时恢复进程状态)。\
|
||||
这些是可访问的字段:
|
||||
|
||||
<figure><img src="../../../.gitbook/assets/image (724).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
* **`N`**、**`Z`**、**`C`**和**`V`**条件标志:
|
||||
* **`N`**表示操作产生了负结果
|
||||
* **`Z`**表示操作产生了零
|
||||
* **`C`**表示操作进行了
|
||||
* **`V`**表示操作产生了有符号溢出:
|
||||
* 两个正数的和产生负结果。
|
||||
* 两个负数的和产生正结果。
|
||||
* 在减法中,当从较小的正数中减去较大的负数(或反之),并且结果无法表示在给定位大小范围内时。
|
||||
* **`N`**、**`Z`**、**`C`** 和 **`V`** 条件标志:
|
||||
* **`N`** 表示操作产生了负结果
|
||||
* **`Z`** 表示操作产生了零
|
||||
* **`C`** 表示操作进行了进位
|
||||
* **`V`** 表示操作产生了有符号溢出:
|
||||
* 两个正数相加得到负结果。
|
||||
* 两个负数相加得到正结果。
|
||||
* 在减法中,当从较小的正数中减去较大的负数(或反之),并且结果无法在给定位大小的范围内表示时。
|
||||
* 显然,处理器不知道操作是有符号还是无符号的,因此它将在操作中检查 C 和 V,并指示是否发生了进位。
|
||||
|
||||
{% hint style="warning" %}
|
||||
并非所有指令都会更新这些标志。一些指令,如**`CMP`**或**`TST`**会,而具有s后缀的其他指令,如**`ADDS`**也会。
|
||||
并非所有指令都会更新这些标志。一些指令如 **`CMP`** 或 **`TST`** 会更新,而像 **`ADDS`** 这样带有 s 后缀的指令也会更新。
|
||||
{% endhint %}
|
||||
|
||||
* 当前**寄存器宽度(`nRW`)**标志:如果标志的值为0,则程序在恢复后将在AArch64执行状态下运行。
|
||||
* 当前**异常级别**(**`EL`**):在EL0中运行的常规程序将具有值0
|
||||
* **单步执行**标志(**`SS`**):调试器使用此标志进行单步执行,通过异常将SS标志设置为1。程序将运行一步并发出单步执行异常。
|
||||
* **非法异常**状态标志(**`IL`**):用于标记特权软件执行无效的异常级别转移,此标志设置为1,处理器触发非法状态异常。
|
||||
* **`DAIF`**标志:这些标志允许特权程序有选择地屏蔽某些外部异常。
|
||||
* 如果**`A`**为1,则表示将触发**异步中止**。**`I`**配置为响应外部硬件**中断请求**(IRQs)。F与**快速中断请求**(FIRs)相关。
|
||||
* **堆栈指针选择**标志(**`SPS`**):在EL1及以上运行的特权程序可以在使用自己的堆栈指针寄存器和用户模型之间切换(例如,在`SP_EL1`和`EL0`之间)。通过写入**`SPSel`**特殊寄存器执行此切换。无法从EL0执行此操作。
|
||||
* 当前的 **寄存器宽度 (`nRW`) 标志**:如果标志的值为 0,则程序在恢复后将在 AArch64 执行状态下运行。
|
||||
* 当前的 **异常级别**(**`EL`**):在 EL0 中运行的常规程序将具有值 0
|
||||
* **单步执行** 标志(**`SS`**):调试器使用单步执行标志将 SS 标志设置为 1,通过异常在 **`SPSR_ELx`** 中运行一步并发出单步执行异常。
|
||||
* **非法异常** 状态标志(**`IL`**):用于标记特权软件执行无效的异常级别转移时,此标志设置为 1,处理器触发非法状态异常。
|
||||
* **`DAIF`** 标志:这些标志允许特权程序有选择地屏蔽某些外部异常。
|
||||
* 如果 **`A`** 为 1,则会触发 **异步中止**。**`I`** 配置为响应外部硬件 **中断请求**(IRQs)。而 F 与 **快速中断请求**(FIRs)有关。
|
||||
* **堆栈指针选择** 标志(**`SPS`**):在 EL1 及以上特权程序中运行时,可以在使用自己的堆栈指针寄存器和用户模型之间进行切换(例如,在 `SP_EL1` 和 `EL0` 之间)。通过写入 **`SPSel`** 特殊寄存器来执行此切换。无法从 EL0 中执行此操作。
|
||||
|
||||
## **调用约定(ARM64v8)**
|
||||
|
||||
ARM64调用约定指定函数的**前八个参数**通过寄存器**`x0`到`x7`**传递。**额外**参数通过**堆栈**传递。返回值通过寄存器**`x0`**传回,如果其为128位长,则也可以通过**`x1`**传回。必须在函数调用之间**保留**寄存器**`x19`**到**`x30`**和**`sp`**。
|
||||
ARM64 调用约定指定函数的 **前八个参数** 通过寄存器 **`x0` 到 `x7`** 传递。**额外** 参数通过 **堆栈** 传递。**返回** 值通过寄存器 **`x0`** 返回,如果返回值为 128 位,则也可以返回到 **`x1`**。必须在函数调用之间保留 **`x19`** 到 **`x30`** 和 **`sp`** 寄存器。
|
||||
|
||||
在汇编中阅读函数时,查找**函数序言和尾声**。**序言**通常涉及**保存帧指针(`x29`)**,**设置新帧指针**和**分配堆栈空间**。**尾声**通常涉及**恢复保存的帧指针**和**从函数返回**。
|
||||
在汇编中阅读函数时,查找 **函数序言和尾声**。**序言** 通常涉及 **保存帧指针 (`x29`)**,**设置** 新的 **帧指针**,和 **分配堆栈空间**。**尾声** 通常涉及 **恢复保存的帧指针** 和 **从函数返回**。
|
||||
|
||||
### Swift中的调用约定
|
||||
### Swift 中的调用约定
|
||||
|
||||
Swift有自己的**调用约定**,可以在[**https://github.com/apple/swift/blob/main/docs/ABI/CallConvSummary.rst#arm64**](https://github.com/apple/swift/blob/main/docs/ABI/CallConvSummary.rst#arm64)中找到。
|
||||
Swift 有其自己的 **调用约定**,可以在 [**https://github.com/apple/swift/blob/main/docs/ABI/CallConvSummary.rst#arm64**](https://github.com/apple/swift/blob/main/docs/ABI/CallConvSummary.rst#arm64) 找到。
|
||||
|
||||
## **常见指令(ARM64v8)**
|
||||
|
||||
ARM64指令通常具有**格式`opcode dst, src1, src2`**,其中**`opcode`**是要执行的操作(如`add`、`sub`、`mov`等),**`dst`**是将结果存储的**目标**寄存器,**`src1`**和**`src2`**是**源**寄存器。也可以使用立即值代替源寄存器。
|
||||
ARM64 指令通常具有格式 `opcode dst, src1, src2`,其中 **`opcode`** 是要执行的 **操作**(如 `add`、`sub`、`mov` 等),**`dst`** 是将存储结果的 **目标** 寄存器,**`src1`** 和 **`src2`** 是 **源** 寄存器。也可以使用立即值代替源寄存器。
|
||||
|
||||
* **`mov`**:将一个值从一个**寄存器**移动到另一个寄存器。
|
||||
* 示例:`mov x0, x1` — 这将从`x1`移动的值到`x0`。
|
||||
* **`ldr`**:将**内存**中的值**加载**到**寄存器**中。
|
||||
* 示例:`ldr x0, [x1]` — 这将从`x1`指向的内存位置加载的值到`x0`。
|
||||
* **`str`**:将**寄存器**中的值**存储**到**内存**中。
|
||||
* 示例:`str x0, [x1]` — 这将`x0`中的值存储到`x1`指向的内存位置。
|
||||
* **`ldp`**:**加载一对寄存器**。此指令从**连续内存**位置加载两个寄存器。内存地址通常是通过将另一个寄存器中的值与偏移量相加来形成的。
|
||||
* 示例:`ldp x0, x1, [x2]` — 这将从`x2`和`x2 + 8`的内存位置分别加载`x0`和`x1`。
|
||||
* **`stp`**:**存储一对寄存器**。此指令将两个寄存器存储到**连续内存**位置。内存地址通常是通过将另一个寄存器中的值与偏移量相加来形成的。
|
||||
* 示例:`stp x0, x1, [x2]` — 这将`x0`和`x1`存储到`x2`和`x2 + 8`的内存位置。
|
||||
* **`mov`**:将一个 **寄存器** 中的值移动到另一个寄存器中。
|
||||
* 示例:`mov x0, x1` — 这将从 `x1` 移动值到 `x0`。
|
||||
* **`ldr`**:将 **内存** 中的值加载到 **寄存器** 中。
|
||||
* 示例:`ldr x0, [x1]` — 这将从 `x1` 指向的内存位置加载值到 `x0`。
|
||||
* **偏移模式**:指示影响原始指针的偏移量,例如:
|
||||
* `ldr x2, [x1, #8]`,这将在 x2 中加载 x1 + 8 的值
|
||||
* `ldr x2, [x0, x1, lsl #2]`,这将在 x2 中加载数组 x0 中位置 x1(索引)\* 4 处的对象
|
||||
* **预索引模式**:这将对原始进行计算,获取结果并将新原始存储在原始中。
|
||||
* `ldr x2, [x1, #8]!`,这将在 `x2` 中加载 `x1 + 8`,并将 `x1` 更新为 `x1 + 8` 的结果
|
||||
* `str lr, [sp, #-4]!`,将链接寄存器存储在 sp 中,并更新寄存器 sp
|
||||
* **后索引模式**:与前一个类似,但首先访问内存地址,然后计算并存储偏移量。
|
||||
* `ldr x0, [x1], #8`,加载 `x1` 到 `x0`,并更新 `x1` 为 `x1 + 8`
|
||||
* **相对于 PC 的寻址**:在这种情况下,相对于 PC 寄存器计算要加载的地址
|
||||
* `ldr x1, =_start`,这将加载 `_start` 符号开始的地址到 x1,相对于当前 PC。
|
||||
* **`str`**:将 **寄存器** 中的值存储到 **内存** 中。
|
||||
* 示例:`str x0, [x1]` — 这将将 `x0` 中的值存储到 `x1` 指向的内存位置。
|
||||
* **`ldp`**:**加载一对寄存器**。此指令从 **连续内存** 位置加载两个寄存器。内存地址通常是通过将偏移量添加到另一个寄存器中形成的。
|
||||
* 示例:`ldp x0, x1, [x2]` — 这将从 `x2` 和 `x2 + 8` 处的内存位置分别加载 `x0` 和 `x1`。
|
||||
* **`stp`**:**存储一对寄存器**。此指令将两个寄存器存储到 **连续内存** 位置。内存地址通常是通过将偏移量添加到另一个寄存器中形成的。
|
||||
* 示例:`stp x0, x1, [sp]` — 这将 `x0` 和 `x1` 存储到 `sp` 和 `sp + 8` 处的内存位置。
|
||||
* `stp x0, x1, [sp, #16]!` — 这将 `x0` 和 `x1` 存储到 `sp+16` 和 `sp + 24` 处的内存位置,并将 `sp` 更新为 `sp+16`。
|
||||
* **`add`**:将两个寄存器的值相加并将结果存储在一个寄存器中。
|
||||
* 语法:add(s) Xn1, Xn2, Xn3 | #imm, \[shift #N | RRX]
|
||||
* Xn1 -> 目标
|
||||
* Xn2 -> 操作数1
|
||||
* Xn3 | #imm -> 操作数2(寄存器或立即数)
|
||||
* \[shift #N | RRX] -> 执行移位或调用RRX
|
||||
* 示例:`add x0, x1, x2` — 这将`x1`和`x2
|
||||
* Xn1 -> 目的地
|
||||
* Xn2 -> 操作数 1
|
||||
* Xn3 | #imm -> 操作数 2(寄存器或立即数)
|
||||
* \[shift #N | RRX] -> 执行移位或调用 RRX
|
||||
* 示例:`add x0, x1, x2` — 这将将 `x1` 和 `x2` 中的值相加,并将结果存储在 `x0` 中。
|
||||
* `add x5, x5, #1, lsl #12` — 这等同于 4096(左移 12 次的 1) -> 1 0000 0000 0000 0000
|
||||
* **`adds`** 这执行一个 `add` 并更新标志
|
||||
* **`sub`**:将两个寄存器的值相减并将结果存储在一个寄存器中。
|
||||
* 检查 **`add`** **语法**。
|
||||
* 示例:`sub x0, x1, x2` — 这将从 `x1` 中减去 `x2` 的值,并将结果存储在 `x0` 中。
|
||||
* **`subs`** 这类似于 sub,但会更新标志位
|
||||
* **`mul`**:将两个寄存器的值相乘,并将结果存储在一个寄存器中。
|
||||
* 示例:`mul x0, x1, x2` — 这将对 `x1` 和 `x2` 中的值进行相乘,并将结果存储在 `x0` 中。
|
||||
* **`div`**:将一个寄存器的值除以另一个寄存器的值,并将结果存储在一个寄存器中。
|
||||
* 示例:`div x0, x1, x2` — 这将对 `x1` 中的值除以 `x2` 中的值,并将结果存储在 `x0` 中。
|
||||
* **`lsl`**、**`lsr`**、**`asr`**、**`ror`**、**`rrx`**:
|
||||
* **逻辑左移**:从末尾添加 0 并将其他位向前移动(乘以 n 次 2)
|
||||
* **逻辑右移**:在开头添加 1 并将其他位向后移动(在无符号情况下除以 n 次 2)
|
||||
* **算术右移**:类似于 **`lsr`**,但如果最高有效位为 1,则添加 1(在有符号情况下除以 n 次 2)
|
||||
* **右旋转**:类似于 **`lsr`**,但从右侧移除的内容会附加到左侧
|
||||
* **带扩展的右旋转**:类似于 **`ror`**,但将进位标志作为“最高有效位”。因此,将进位标志移动到位 31,将移除的位移动到进位标志。
|
||||
* **`bfm`**:**位字段移动**,这些操作会从一个值中复制位 `0...n`,并将其放置在位置 `m..m+n`。**`#s`** 指定最左侧位的位置,**`#r`** 指定右旋转量。
|
||||
* 位字段移动:`BFM Xd, Xn, #r`
|
||||
* 有符号位字段移动:`SBFM Xd, Xn, #r, #s`
|
||||
* 无符号位字段移动:`UBFM Xd, Xn, #r, #s`
|
||||
* **位字段提取和插入**:从一个寄存器中复制位字段并将其复制到另一个寄存器中。
|
||||
* **`BFI X1, X2, #3, #4`** 从 X2 的第 3 位开始插入 4 位到 X1
|
||||
* **`BFXIL X1, X2, #3, #4`** 从 X2 的第 3 位提取四位并将其复制到 X1
|
||||
* **`SBFIZ X1, X2, #3, #4`** 从 X2 中扩展 4 位并从第 3 位开始插入 X1,将右侧位清零
|
||||
* **`SBFX X1, X2, #3, #4`** 从 X2 的第 3 位开始提取 4 位,进行符号扩展,并将结果放入 X1
|
||||
* **`UBFIZ X1, X2, #3, #4`** 从 X2 中扩展 4 位并从第 3 位开始插入 X1,将右侧位清零
|
||||
* **`UBFX X1, X2, #3, #4`** 从 X2 的第 3 位开始提取 4 位,并将零扩展的结果放入 X1。
|
||||
* **符号扩展至 X**:扩展值的符号(或在无符号版本中仅添加 0)以便执行操作:
|
||||
* **`SXTB X1, W2`** 将字节的符号从 W2 扩展到 X1(`W2` 是 `X2` 的一半)以填充 64 位
|
||||
* **`SXTH X1, W2`** 将 16 位数字的符号从 W2 扩展到 X1 以填充 64 位
|
||||
* **`SXTW X1, W2`** 将字节的符号从 W2 扩展到 X1 以填充 64 位
|
||||
* **`UXTB X1, W2`** 将字节的 0(无符号)从 W2 添加到 X1 以填充 64 位
|
||||
* **`extr`**:从指定的**连接的一对寄存器**中提取位。
|
||||
* 示例:`EXTR W3, W2, W1, #3` 这将**连接 W1+W2** 并从 W2 的第 3 位到 W1 的第 3 位获取并将其存储在 W3 中。
|
||||
* **`cmp`**:比较两个寄存器并设置条件标志。这是 `subs` 的别名,将目标寄存器设置为零寄存器。用于判断 `m == n`。
|
||||
* 支持与 `subs` 相同的语法
|
||||
* 示例:`cmp x0, x1` — 这将比较 `x0` 和 `x1` 中的值,并相应地设置条件标志。
|
||||
* **`cmn`**:**比较负数**操作数。在这种情况下,它是 `adds` 的别名并支持相同的语法。用于判断 `m == -n`。
|
||||
* **`ccmp`**:条件比较,仅在先前的比较为真时执行比较,并明确设置 nzcv 位。
|
||||
* `cmp x1, x2; ccmp x3, x4, 0, NE; blt _func` -> 如果 x1 != x2 并且 x3 < x4,则跳转到 func
|
||||
* 这是因为**`ccmp`**仅在**先前的 `cmp` 为 `NE` 时执行**,如果不是,则位 `nzcv` 将设置为 0(这不会满足 `blt` 比较)。
|
||||
* 这也可以用作 `ccmn`(与 `cmp` 相同,但是负数,类似于 `cmp` vs `cmn`)。
|
||||
* **`tst`**:检查比较的值是否都为 1(类似于 ANDS,但不会在任何地方存储结果)。用于检查具有值的寄存器,并检查值中指示的寄存器的任何位是否为 1。
|
||||
* 示例:`tst X1, #7` 检查 X1 的最后 3 位中是否有任何位为 1
|
||||
* **`teq`**:异或操作,丢弃结果
|
||||
* **`b`**:无条件跳转
|
||||
* 示例:`b myFunction` 
|
||||
* 请注意,这不会将链接寄存器填充为返回地址(不适用于需要返回的子程序调用)
|
||||
* **`bl`**:带链接的分支,用于**调用**子程序。将返回地址存储在 `x30` 中。
|
||||
* 示例:`bl myFunction` — 这将调用函数 `myFunction` 并将返回地址存储在 `x30` 中。
|
||||
* 请注意,这不会将链接寄存器填充为返回地址(不适用于需要返回的子程序调用)
|
||||
* **`blr`**:带链接到寄存器的分支,用于调用寄存器中指定的子程序的目标。将返回地址存储在 `x30` 中。(这是 
|
||||
* 示例:`blr x1` — 这将调用地址包含在 `x1` 中的函数,并将返回地址存储在 `x30` 中。
|
||||
* **`ret`**:从子程序**返回**,通常使用**`x30`** 中的地址。
|
||||
* 示例:`ret` — 这将使用 `x30` 中的返回地址从当前子程序返回。
|
||||
* **`b.<cond>`**:条件分支
|
||||
* **`b.eq`**:**如果相等则跳转**,基于先前的 `cmp` 指令。
|
||||
* 示例:`b.eq label` — 如果先前的 `cmp` 指令找到两个相等的值,则跳转到 `label`。
|
||||
* **`b.ne`**:**如果不相等则跳转**。此指令检查条件标志(由先前的比较指令设置),如果比较的值不相等,则跳转到标签或地址。
|
||||
* 示例:在 `cmp x0, x1` 指令之后,`b.ne label` — 如果 `x0` 和 `x1` 中的值不相等,则跳转到 `label`。
|
||||
* **`cbz`**:**比较并在零时跳转**。此指令将一个寄存器与零进行比较,如果它们相等,则跳转到标签或地址。
|
||||
* 示例:`cbz x0, label` — 如果 `x0` 中的值为零,则跳转到 `label`。
|
||||
* **`cbnz`**:**比较并在非零时跳转**。此指令将一个寄存器与零进行比较,如果它们不相等,则跳转到标签或地址。
|
||||
* 示例:`cbnz x0, label` — 如果`x0`中的值非零,则跳转到`label`。
|
||||
* **`tbnz`**:测试位并在非零时跳转
|
||||
* 示例:`tbnz x0, #8, label`
|
||||
* **`tbz`**:测试位并在零时跳转
|
||||
* 示例:`tbz x0, #8, label`
|
||||
* **条件选择操作**:这些操作的行为取决于条件位的值。
|
||||
* `csel Xd, Xn, Xm, cond` -> `csel X0, X1, X2, EQ` -> 如果为真,则 X0 = X1,如果为假,则 X0 = X2
|
||||
* `csinc Xd, Xn, Xm, cond` -> 如果为真,则 Xd = Xn,如果为假,则 Xd = Xm + 1
|
||||
* `cinc Xd, Xn, cond` -> 如果为真,则 Xd = Xn + 1,如果为假,则 Xd = Xn
|
||||
* `csinv Xd, Xn, Xm, cond` -> 如果为真,则 Xd = Xn,如果为假,则 Xd = NOT(Xm)
|
||||
* `cinv Xd, Xn, cond` -> 如果为真,则 Xd = NOT(Xn),如果为假,则 Xd = Xn
|
||||
* `csneg Xd, Xn, Xm, cond` -> 如果为真,则 Xd = Xn,如果为假,则 Xd = - Xm
|
||||
* `cneg Xd, Xn, cond` -> 如果为真,则 Xd = - Xn,如果为假,则 Xd = Xn
|
||||
* `cset Xd, Xn, Xm, cond` -> 如果为真,则 Xd = 1,如果为假,则 Xd = 0
|
||||
* `csetm Xd, Xn, Xm, cond` -> 如果为真,则 Xd = \<all 1>,如果为假,则 Xd = 0
|
||||
* **`adrp`**:计算**符号的页地址**并将其存储在寄存器中。
|
||||
* 示例:`adrp x0, symbol` — 这将计算`symbol`的页地址并将其存储在`x0`中。
|
||||
* **`ldrsw`**:从内存中**加载**带符号的**32位**值并将其**符号扩展为64位**。
|
||||
* 示例:`ldrsw x0, [x1]` — 这会从`x1`指向的内存位置加载带符号的32位值,将其符号扩展为64位,并将其存储在`x0`中。
|
||||
* **`stur`**:将寄存器值**存储到内存位置**,使用另一个寄存器的偏移量。
|
||||
* 示例:`stur x0, [x1, #4]` — 这将`x0`中的值存储到比`x1`当前地址大4个字节的内存地址中。
|
||||
* **`svc`**:进行**系统调用**。它代表"Supervisor Call"。当处理器执行此指令时,它会从用户模式切换到内核模式,并跳转到内存中内核系统调用处理代码所在的特定位置。
|
||||
* 示例:
|
||||
|
||||
```armasm
|
||||
mov x8, 93 ; 将退出系统调用号(93)加载到寄存器 x8 中。
|
||||
mov x0, 0 ; 将退出状态码(0)加载到寄存器 x0 中。
|
||||
svc 0 ; 进行系统调用。
|
||||
```
|
||||
|
||||
### **函数序言**
|
||||
|
||||
1. **将链接寄存器和帧指针保存到堆栈中**:
|
||||
|
||||
{% code overflow="wrap" %}
|
||||
```armasm
|
||||
stp x29, x30, [sp, #-16]! ; store pair x29 and x30 to the stack and decrement the stack pointer
|
||||
```
|
||||
{% endcode %}
|
||||
|
||||
2. **设置新的帧指针**:`mov x29, sp`(为当前函数设置新的帧指针)
|
||||
3. **为局部变量在栈上分配空间**(如果需要):`sub sp, sp, <size>`(其中 `<size>` 是所需字节数)
|
||||
|
||||
### **函数尾声**
|
||||
|
||||
1. **释放局部变量(如果有分配的)**:`add sp, sp, <size>`
|
||||
2. **恢复链接寄存器和帧指针**:
|
||||
|
||||
{% code overflow="wrap" %}
|
||||
```armasm
|
||||
ldp x29, x30, [sp], #16 ; load pair x29 and x30 from the stack and increment the stack pointer
|
||||
```
|
||||
|
@ -140,13 +263,13 @@ ldp x29, x30, [sp], #16 ; load pair x29 and x30 from the stack and increment th
|
|||
## AARCH32 执行状态
|
||||
|
||||
Armv8-A 支持执行 32 位程序。**AArch32** 可以在 **两种指令集**之一中运行:**`A32`** 和 **`T32`**,并可以通过 **`interworking`** 在它们之间切换。\
|
||||
**特权** 64 位程序可以通过执行例外级别转移到较低特权的 32 位程序来调度 **执行 32 位** 程序。\
|
||||
请注意,从 64 位到 32 位的过渡发生在例外级别的降低时(例如,EL1 中的 64 位程序触发 EL0 中的程序)。当 `AArch32` 进程线程准备好执行时,通过将 **`SPSR_ELx`** 特殊寄存器的 **第 4 位设置为 1** 来完成这一过渡,`SPSR_ELx` 的其余部分存储 **`AArch32`** 程序的 CPSR。然后,特权进程调用 **`ERET`** 指令,使处理器转换到 **`AArch32`** 进入 A32 或 T32,具体取决于 CPSR**。**
|
||||
**特权** 64 位程序可以通过执行将执行权转移到较低特权的 32 位程序的例外级别转移来调度 **执行 32 位** 程序。\
|
||||
请注意,从 64 位到 32 位的过渡发生在较低的异常级别(例如,EL1 中的 64 位程序触发 EL0 中的程序)。当 `AArch32` 进程线程准备好执行时,通过将 **`SPSR_ELx`** 特殊寄存器的 **第 4 位设置为 1** 来完成这一过渡,而 `SPSR_ELx` 的其余部分存储了 **`AArch32`** 程序的 CPSR。然后,特权进程调用 **`ERET`** 指令,使处理器转换到 **`AArch32`** 进入 A32 或 T32,具体取决于 CPSR\*\*。\*\*
|
||||
|
||||
**`interworking`** 使用 CPSR 的 J 和 T 位。`J=0` 和 `T=0` 表示 **`A32`**,`J=0` 和 `T=1` 表示 **T32**。这基本上意味着将 **最低位设置为 1** 以指示指令集为 T32。\
|
||||
这是在 **interworking 分支指令** 中设置的,但也可以直接使用其他指令设置,当 PC 被设置为目标寄存器时。示例:
|
||||
这是在 **interworking 分支指令** 中设置的,但也可以直接使用其他指令设置,当 PC 被设置为目标寄存器时。例如:
|
||||
|
||||
另一个示例:
|
||||
另一个例子:
|
||||
```armasm
|
||||
_start:
|
||||
.code 32 ; Begin using A32
|
||||
|
@ -167,12 +290,12 @@ mov r0, #8
|
|||
- **`r13`**:堆栈指针
|
||||
- **`r14`**:链接寄存器
|
||||
|
||||
此外,寄存器在**`banked registries`**中备份。这些地方存储寄存器的值,允许在异常处理和特权操作中执行**快速上下文切换**,避免每次手动保存和恢复寄存器的需要。\
|
||||
这是通过**将处理器状态从`CPSR`保存到所采取的处理器模式的`SPSR`**来完成的。在异常返回时,**从`SPSR`恢复`CPSR`**。
|
||||
此外,寄存器在**`banked registries`**中备份。这些地方存储寄存器的值,允许在异常处理和特权操作中执行**快速上下文切换**,避免每次都需要手动保存和恢复寄存器。\
|
||||
这是通过将处理器状态从`CPSR`保存到所采取的处理器模式的`SPSR`来完成的。在异常返回时,**从`SPSR`恢复`CPSR`**。
|
||||
|
||||
### CPSR - 当前程序状态寄存器
|
||||
|
||||
在AArch32中,CPSR的工作方式类似于AArch64中的**`PSTATE`**,当发生异常时也存储在**`SPSR_ELx`**中以便稍后恢复执行:
|
||||
在AArch32中,CPSR的工作方式类似于AArch64中的**`PSTATE`**,当发生异常时,它也存储在**`SPSR_ELx`**中以便稍后恢复执行:
|
||||
|
||||
<figure><img src="../../../.gitbook/assets/image (725).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
|
@ -187,20 +310,20 @@ mov r0, #8
|
|||
- **`Q`**标志:在执行专门的饱和算术指令时,当**整数饱和发生**时设置为1。一旦设置为**`1`**,它将保持该值,直到手动设置为0。此外,没有任何隐式检查其值的指令,必须通过手动读取来完成。
|
||||
- **`GE`**(大于或等于)标志:用于SIMD(单指令,多数据)操作,例如“并行加法”和“并行减法”。这些操作允许在单个指令中处理多个数据点。
|
||||
|
||||
例如,**`UADD8`**指令**并行添加四对字节**(来自两个32位操作数),并将结果存储在32位寄存器中。然后,基于这些结果,它**在`APSR`中设置`GE`标志**。每个GE标志对应于一个字节加法,指示该字节对的加法是否**溢出**。
|
||||
例如,**`UADD8`**指令**并行添加四对字节**(来自两个32位操作数)并将结果存储在32位寄存器中。然后,基于这些结果,它在`APSR`中**设置`GE`标志**。每个GE标志对应于一个字节加法,指示该字节对的加法是否**溢出**。
|
||||
|
||||
**`SEL`**指令使用这些GE标志执行条件操作。
|
||||
|
||||
#### 执行状态寄存器
|
||||
|
||||
- **`J`**和**`T`**位:**`J`**应为0,如果**`T`**为0,则使用指令集A32,如果为1,则使用T32。
|
||||
- **IT块状态寄存器**(`ITSTATE`):这些是位10-15和25-26。它们存储**`IT`**前缀组内指令的条件。
|
||||
- **`E`**位:指示**字节序**。 
|
||||
- **模式和异常掩码位**(0-4):它们确定当前的执行状态。第**5**个指示程序是否以32位(1)或64位(0)运行。其他4个表示当前正在使用的**异常模式**(当发生异常并正在处理时)。设置的数字表示在处理此异常时,如果触发另一个异常,则**当前优先级**。
|
||||
- **`J`**和**`T`**位:**`J`**应为0,如果**`T`**为0,则使用A32指令集,如果为1,则使用T32指令集。
|
||||
- **IT块状态寄存器**(`ITSTATE`):这些是位10-15和25-26。它们存储`IT`前缀组内指令的条件。
|
||||
- **`E`**位:指示**字节序**。
|
||||
- **模式和异常掩码位**(0-4):它们确定当前的执行状态。第**5**个指示程序是否以32位(1)或64位(0)运行。其他4个表示当前正在使用的**异常模式**(当发生异常并正在处理时)。设置的数字表示在处理此异常时触发另一个异常的当前优先级。
|
||||
|
||||
<figure><img src="../../../.gitbook/assets/image (728).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
- **`AIF`**:可以使用位**`A`**,`I`,`F`来禁用某些异常。如果**`A`**为1,则表示将触发**异步中止**。**`I`**配置为响应外部硬件**中断请求**(IRQs)。F与**快速中断请求**(FIRs)有关。
|
||||
- **`AIF`**:可以使用**`A`**,`I`,`F`位禁用某些异常。如果**`A`**为1,则会触发**异步中止**。**`I`**配置为响应外部硬件**中断请求**(IRQs)。F与**快速中断请求**(FIRs)有关。
|
||||
|
||||
## macOS
|
||||
|
||||
|
@ -210,7 +333,7 @@ mov r0, #8
|
|||
|
||||
### Mach陷阱
|
||||
|
||||
查看[**syscall\_sw.c**](https://opensource.apple.com/source/xnu/xnu-3789.1.32/osfmk/kern/syscall\_sw.c.auto.html)。Mach陷阱将具有**x16 < 0**,因此您需要使用前一列表中的数字调用带有**减号**的函数:**`_kernelrpc_mach_vm_allocate_trap`**是**`-10`**。
|
||||
查看[**syscall_sw.c**](https://opensource.apple.com/source/xnu/xnu-3789.1.32/osfmk/kern/syscall_sw.c.auto.html)。Mach陷阱将具有**x16 < 0**,因此您需要使用前一个列表中的数字并加上**负号**进行调用:**`_kernelrpc_mach_vm_allocate_trap`**是**`-10`**。
|
||||
|
||||
您还可以在反汇编器中检查**`libsystem_kernel.dylib`**,以找出如何调用这些(以及BSD)系统调用:
|
||||
```bash
|
||||
|
@ -221,7 +344,7 @@ dyldex -e libsystem_kernel.dylib /System/Volumes/Preboot/Cryptexes/OS/System/Lib
|
|||
dyldex -e libsystem_kernel.dylib /System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64
|
||||
```
|
||||
{% hint style="success" %}
|
||||
有时候检查来自`libsystem_kernel.dylib`的**反编译**代码比检查**源代码**更容易,因为几个系统调用(BSD和Mach)的代码是通过脚本生成的(请检查源代码中的注释),而在dylib中,您可以找到正在被调用的内容。
|
||||
有时候检查**`libsystem_kernel.dylib`**中的**反编译**代码比检查**源代码**更容易,因为几个系统调用(BSD和Mach)的代码是通过脚本生成的(请查看源代码中的注释),而在dylib中,你可以找到正在被调用的内容。
|
||||
{% endhint %}
|
||||
|
||||
### Shellcodes
|
||||
|
@ -234,7 +357,7 @@ ld -o shell shell.o -macosx_version_min 13.0 -lSystem -L /Library/Developer/Comm
|
|||
# You could also use this
|
||||
ld -o shell shell.o -syslibroot $(xcrun -sdk macosx --show-sdk-path) -lSystem
|
||||
```
|
||||
要提取字节:
|
||||
提取字节:
|
||||
```bash
|
||||
# Code from https://github.com/daem0nc0re/macOS_ARM64_Shellcode/blob/master/helper/extract.sh
|
||||
for c in $(objdump -d "s.o" | grep -E '[0-9a-f]+:' | cut -f 1 | cut -d : -f 2) ; do
|
||||
|
@ -344,7 +467,7 @@ svc #0x1337 ; Make the syscall. The number 0x1337 doesn't actually matter,
|
|||
```
|
||||
#### 使用cat命令读取
|
||||
|
||||
目标是执行`execve("/bin/cat", ["/bin/cat", "/etc/passwd"], NULL)`,因此第二个参数(x1)是一个参数数组(在内存中表示为地址的堆栈)。
|
||||
目标是执行`execve("/bin/cat", ["/bin/cat", "/etc/passwd"], NULL)`,因此第二个参数(x1)是一个参数数组(在内存中意味着地址的堆栈)。
|
||||
```armasm
|
||||
.section __TEXT,__text ; Begin a new section of type __TEXT and name __text
|
||||
.global _main ; Declare a global symbol _main
|
||||
|
@ -370,7 +493,7 @@ cat_path: .asciz "/bin/cat"
|
|||
.align 2
|
||||
passwd_path: .asciz "/etc/passwd"
|
||||
```
|
||||
#### 通过从 fork 中使用 sh 调用命令,使主进程不被杀死
|
||||
#### 通过从 fork 中使用 sh 调用命令,以便主进程不被杀死
|
||||
```armasm
|
||||
.section __TEXT,__text ; Begin a new section of type __TEXT and name __text
|
||||
.global _main ; Declare a global symbol _main
|
||||
|
@ -416,7 +539,7 @@ touch_command: .asciz "touch /tmp/lalala"
|
|||
```
|
||||
#### 绑定 shell
|
||||
|
||||
从 [https://raw.githubusercontent.com/daem0nc0re/macOS\_ARM64\_Shellcode/master/bindshell.s](https://raw.githubusercontent.com/daem0nc0re/macOS\_ARM64\_Shellcode/master/bindshell.s) 获取在**端口 4444**上的绑定 shell
|
||||
从 [https://raw.githubusercontent.com/daem0nc0re/macOS\_ARM64\_Shellcode/master/bindshell.s](https://raw.githubusercontent.com/daem0nc0re/macOS\_ARM64\_Shellcode/master/bindshell.s) 获取在**端口 4444**上的绑定 shell。
|
||||
```armasm
|
||||
.section __TEXT,__text
|
||||
.global _main
|
||||
|
@ -500,7 +623,7 @@ svc #0x1337
|
|||
```
|
||||
#### 反向 shell
|
||||
|
||||
从 [https://github.com/daem0nc0re/macOS\_ARM64\_Shellcode/blob/master/reverseshell.s](https://github.com/daem0nc0re/macOS\_ARM64\_Shellcode/blob/master/reverseshell.s) 获取 revshell 到 **127.0.0.1:4444**
|
||||
从 [https://github.com/daem0nc0re/macOS\_ARM64\_Shellcode/blob/master/reverseshell.s](https://github.com/daem0nc0re/macOS\_ARM64\_Shellcode/blob/master/reverseshell.s) 下载 revshell 到 **127.0.0.1:4444**
|
||||
```armasm
|
||||
.section __TEXT,__text
|
||||
.global _main
|
||||
|
@ -569,14 +692,14 @@ svc #0x1337
|
|||
```
|
||||
<details>
|
||||
|
||||
<summary><strong>从零开始学习AWS黑客技术,成为专家</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE(HackTricks AWS Red Team Expert)</strong></a><strong>!</strong></summary>
|
||||
<summary><strong>从零开始学习AWS黑客技术,成为专家</strong> <a href="https://training.hacktricks.xyz/courses/arte"><strong>htARTE(HackTricks AWS红队专家)</strong></a><strong>!</strong></summary>
|
||||
|
||||
其他支持HackTricks的方式:
|
||||
|
||||
* 如果您想看到您的**公司在HackTricks中做广告**或**下载PDF格式的HackTricks**,请查看[**订阅计划**](https://github.com/sponsors/carlospolop)!
|
||||
* 获取[**官方PEASS & HackTricks周边产品**](https://peass.creator-spring.com)
|
||||
* 探索[**PEASS家族**](https://opensea.io/collection/the-peass-family),我们的独家[**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* **加入** 💬 [**Discord群组**](https://discord.gg/hRep4RUj7f) 或 [**电报群组**](https://t.me/peass) 或 **关注**我们的**Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks_live)**。**
|
||||
* **加入** 💬 [**Discord群**](https://discord.gg/hRep4RUj7f) 或 [**电报群**](https://t.me/peass) 或 **关注**我们的**Twitter** 🐦 [**@carlospolopm**](https://twitter.com/hacktricks\_live)**。**
|
||||
* 通过向[**HackTricks**](https://github.com/carlospolop/hacktricks)和[**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github仓库提交PR来分享您的黑客技巧。
|
||||
|
||||
</details>
|
||||
|
|
Loading…
Reference in a new issue