reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
# IBM i:单一存储层 / MI 层次 / 程序模型 / 指针与 activation group
|
||||
|
||||
<CORE RULE>
|
||||
**两件事决定一切**:所有内容都是**对象**,而一切存储处在**单一地址空间**里。
|
||||
|
||||
```
|
||||
主存与辅存不被区分 → 内存 + 磁盘 = 单一 64 位地址空间
|
||||
对象按【名字】访问,不按硬件地址
|
||||
```
|
||||
|
||||
再加上**两套指针**与**两种存储模型**的分野,`object address` 就绝不能当成"当前由 DRAM 支撑的指针"。
|
||||
|
||||
**不要套 POSIX 文件/进程模型**——这一层的每一个概念在这里都有对应的、但语义不同的东西。
|
||||
</CORE RULE>
|
||||
|
||||
## 一、层次:谁在谁上面
|
||||
|
||||
```
|
||||
MI(Machine Interface) ← 程序与进程在这里是【系统对象】;ILE 支持在此实现
|
||||
├─ timi(technology-independent 接口)
|
||||
└─ PASE ← 走另一条路:直接对着 SLIC 的 syscall 接口,绕过 TIMI
|
||||
SLIC / LIC(System Licensed Internal Code) ← 中断处理、异常路由、部分运行时实现
|
||||
OS(ILE 运行时 / OPM 支持 / 各类服务)
|
||||
```
|
||||
|
||||
- **MI 层跟踪调用流是按"过程(procedure)"而不是按"程序"**——MI 指令里有外部程序调用与"经过程指针调用"两类
|
||||
- **进程**在 MI 上是系统对象(Process Control Space);**activation group 不是系统对象**,它是 MI 进程对象的一部分
|
||||
- **PASE 不经过 TIMI**:它对 SLIC 有 syscall 接口,因此跨 PASE↔SLIC 边界的调用有额外开销
|
||||
|
||||
**逆向含义**:见到一份代码,先判它在**哪一层**——MI 层的对象、ILE 运行时生成的代码、还是 PASE 里的 AIX 风格二进制,三者的分析工具和语义都不同。
|
||||
|
||||
## 二、程序模型:OPM / EPM / ILE
|
||||
|
||||
| 模型 | 特征 |
|
||||
|---|---|
|
||||
| **OPM** | 程序对象直接由源成员编译;调用**多在运行时解析**(晚绑定);通常单语言;**不支持模块、过程、服务程序、绑定目录**;**必须运行在默认 activation group** |
|
||||
| **EPM** | 过渡形态(C/400、Pascal 时代),仍然是运行时解析 |
|
||||
| **ILE** | 引入模块、绑定器、优化翻译器;支持多语言混合 |
|
||||
|
||||
**ILE 的对象三件套**:
|
||||
|
||||
| 对象 | 说明 |
|
||||
|---|---|
|
||||
| **`*MODULE`** | ILE 编译器的输出;含一个或多个**过程**;**不能直接执行**;被绑定进 `*PGM`/`*SRVPGM` |
|
||||
| **`*PGM`** | 绑定一个或多个模块并指定**入口模块**;**单一入口点**;**bind-by-copy**——过程在编译时解析,开销小,但同一模块被绑进多个程序时内存里会有多份副本 |
|
||||
| **`*SRVPGM`** | 多入口点的可执行单元(每个过程一个入口);**bind-by-reference**——客户端存的是符号链接,**在程序被激活时解析**;**没有入口模块** |
|
||||
|
||||
- **签名与 binder language**:binder 描述文件管理服务程序**支持的签名与导出列表**(导出符号、程序级别、签名、级别检查);客户端程序激活时,被绑定的服务程序也会被激活,并做**签名匹配**
|
||||
- **binding directory** 保存绑定信息来源
|
||||
- 模块还可以**导出数据项**:导出方分配并初始化,其他模块导入并共享该存储
|
||||
|
||||
**逆向含义**:`*PGM` 里**内含**它的模块;`*SRVPGM` **不内嵌**在调用者里,而是**首次调用时装入内存**——"改了服务程序不需要重编译调用者"正是这个差异的直接后果。
|
||||
|
||||
## 三、activation group:运行期的存储与资源边界
|
||||
|
||||
activation group 是为运行一个或多个程序而分配的**运行期工作存储容器**,提供:
|
||||
|
||||
```
|
||||
PSSA(程序静态存储区,全局静态数据)
|
||||
PASA / 调用返回栈(局部变量)
|
||||
堆存储(动态数据)
|
||||
```
|
||||
|
||||
- 每个 activation group 有自己的控制信息,因此可以有**不同的保护状态、文件使用、提交控制**
|
||||
- **一个 job 有两个默认 activation group**:操作系统用一个,应用用一个;**OPM 程序必须跑在默认组里**;ILE 程序也可以(但会失去模块/过程/服务程序等特性)
|
||||
- 常见选择:
|
||||
|
||||
| 选择 | 语义 |
|
||||
|---|---|
|
||||
| **命名** | 首个程序创建,后续程序"插进来";**直到 job 结束或显式回收**才消失 |
|
||||
| **`*NEW`** | 系统命名,调用时创建、程序结束时销毁;**服务程序不能用 `*NEW`** |
|
||||
| **`*CALLER`** | 与调用者同组,不新建(服务程序通常的推荐值) |
|
||||
| **`*ENTMOD`** | 由绑定器看入口模块决定 |
|
||||
|
||||
- **覆盖(override)与文件打开的作用域也在这一层**:作用域可以是 activation group、调用层级或 job;从默认组发出的覆盖通常按调用栈层级生效,从 ILE activation group 发出的按该组生效
|
||||
|
||||
**逆向含义**:定位"这个静态变量属于谁、这个文件覆盖为什么没生效",答案在 activation group 而不是在程序里。
|
||||
|
||||
## 四、两套指针与两种存储模型
|
||||
|
||||
| | **系统指针(system pointer)** | **空间指针(space pointer)** |
|
||||
|---|---|---|
|
||||
| 指向 | **对象**(带类型信息) | 空间内的偏移 |
|
||||
| 形态 | 16 字节 tagged | 地址 |
|
||||
| 能做什么 | 解析对象、调用、授权检查 | 直接寻址一块空间内的字节 |
|
||||
|
||||
**两种存储模型并存**:
|
||||
|
||||
| | **single-level storage** | **teraspace** |
|
||||
|---|---|---|
|
||||
| 范围 | 全局(任何拿到指针的 job 都可访问) | **job 本地**(正常只对拥有它的 job 可见) |
|
||||
| 组成 | 多个 16 MB 单元 | 一个连续地址空间,内含多个独立分配区与未分配空隙 |
|
||||
| 大小 | —— | 约 100 TB 量级 |
|
||||
| mmap | **不支持** | 支持 |
|
||||
| 指针 | —— | **8 字节** |
|
||||
| 跨 job 共享 | 传指针即可 | **只能经共享内存 API**(`shmat`/`mmap` 一类) |
|
||||
|
||||
- **teraspace 不是空间对象**:它不是系统对象,**不能用系统指针引用**,但可用空间指针在同一 job 内寻址;**只在 job 开始到 job 结束之间存在**
|
||||
- ILE 程序/服务程序可建成 `*TERASPACE` / `*INHERIT` / `*SNGLVL`;**OPM 程序只能用 `*SNGLVL`**
|
||||
- **`*INHERIT` 采用所在 activation group 的存储模型**,并要求 `ACTGRP(*CALLER)`
|
||||
- **teraspace 模型的程序不能被激活进"根程序用单级存储"的 activation group**(含 OPM 默认组);`*TERASPACE` 不能配默认组
|
||||
- 堆默认跟随存储模型;某些语言可以让堆单独选型(例如把 `malloc` 映到 teraspace 版本)
|
||||
|
||||
**所以逆向时要问三件事**:这个指针是系统指针还是空间指针?这个地址属于单级存储还是 teraspace?这个程序/服务程序是哪种存储模型?(第三问决定了前两问的合法答案。)
|
||||
|
||||
## 五、异常与指针使用限制
|
||||
|
||||
- **space addressing violation**:试图在当前空间范围之外操作,或引用 teraspace 内**未分配的页**
|
||||
- 该异常的**数据里带**:与空间关联的对象的**系统指针**、**空间偏移**、以及**空间类别**——空间类别能区分主/次关联空间、隐式进程空间(自动/静态/activation group 堆/常量/句柄堆)、**teraspace**,以及**供 PASE 使用的 teraspace**
|
||||
- **tagged(16 字节)指针的使用限制**(做数据流与取证时要记住):
|
||||
- 指向 teraspace 的 tagged 空间指针**只能由拥有该 teraspace 的进程使用**——被 `fork` 子进程继承、或被别的进程存进共享内存后,**应视为不可用**
|
||||
- **不得存放进由字节流文件映射来的内存**(`mmap`/`shmat` 那类);即使存在用共享内存对象创建的区域里,**也不能被持有者以外的进程可靠使用**
|
||||
|
||||
## 六、job / 线程边界
|
||||
|
||||
- **teraspace 随 job 生灭**;跨 job 共享必须走共享内存 API
|
||||
- 库解析(例如库列表搜索)是**按线程**的
|
||||
- ILE 运行时**可能按 activation group 各自维护 `errno`**——要判断值,必须在"同一个线程、同一个 activation group"里取
|
||||
- **并非所有系统 API 都线程安全**(API 参考逐个标注);把非线程安全 API 经 JNI 调用会得到不可预期的结果
|
||||
|
||||
## 七、可执行材料识别(拿产物时的第一层)
|
||||
|
||||
| 手段 | 用途 |
|
||||
|---|---|
|
||||
| **DMPOBJ(Dump Object)** | 把对象内容打成 spool file;可用于两份 `*PGM`/`*SRVPGM` 的**差异比对** |
|
||||
| **DBGVIEW(`*LIST`/`*ALL`) 编译** | 借调试 API **提取编译清单**;比二进制比对更省事、更可读 |
|
||||
| 对象压缩(`CPROBJ`/`DCPOBJ`) | `*PGM`/`*SRVPGM`/`*MODULE` 等**可能处于压缩态**;**被使用时系统会自动提供解压版本**——所以"文件看着不像代码"可能只是压缩态 |
|
||||
| 版本/时间戳 | 编译时刻等内嵌结构会让"几乎同时编译的两次产物"**永远不相同**——别据此判被篡改 |
|
||||
|
||||
**注意**:`*MODULE` 通常建在临时库里,**被绑定进程序后就不再需要**——所以在系统上找不到"当初那个模块对象"是正常的,代码在 `*PGM`/`*SRVPGM` 里面。
|
||||
|
||||
## 决策树
|
||||
|
||||
```
|
||||
拿到 IBM i 目标
|
||||
├─ 先判层次
|
||||
│ ├─ MI 层对象 / ILE 运行时产物 / PASE 里的 AIX 风格二进制
|
||||
│ └─ 三者工具与语义不同
|
||||
├─ 拿到的是哪种对象
|
||||
│ ├─ *PGM(bind-by-copy,内含模块)/*SRVPGM(bind-by-reference,多入口)
|
||||
│ ├─ *MODULE(不可直接执行;可能已被绑定后丢弃)
|
||||
│ └─ 压缩态?先用解压/查看工具,别判"不是代码"
|
||||
├─ 引用与存储
|
||||
│ ├─ 系统指针(指向对象)还是空间指针(空间内偏移)?
|
||||
│ ├─ 单级存储(全局、支持传指针共享)还是 teraspace(job 本地、跨 job 需共享内存 API)?
|
||||
│ └─ 程序的存储模型(*TERASPACE/*INHERIT/*SNGLVL)与目标 activation group 是否相容?
|
||||
├─ 静态数据/文件覆盖行为异常
|
||||
│ └─ 查 activation group(PSSA/PASA/堆 与覆盖作用域都在这一层)
|
||||
├─ 异常
|
||||
│ ├─ space addressing violation → 看异常数据里的系统指针/偏移/空间类别
|
||||
│ └─ tagged 指针跨 fork / 跨进程使用 → 按不可用处理
|
||||
└─ 产物比对
|
||||
├─ 优先用编译清单(DBGVIEW)
|
||||
└─ DMPOBJ 比对时排除库名/创建时间/PTF 级别等内嵌差异
|
||||
```
|
||||
|
||||
## 工具与验证
|
||||
|
||||
- 层次与对象:对象类型与所属库;MI 层调用流(按过程追踪)
|
||||
- 程序结构:绑定的模块与入口模块、导出/导入符号、服务程序签名与 binder 语言
|
||||
- 运行期边界:activation group 的存储区(PSSA/PASA/堆)与覆盖作用域
|
||||
- 指针与存储:系统指针/空间指针的区分;teraspace 与单级存储的归属
|
||||
- 验证:能同时说清「这是哪一层的什么对象 + 它的指针是哪种、指向哪种存储 + 它跑在哪个 activation group」
|
||||
|
||||
## 该平台的坑(汇总)
|
||||
|
||||
- **把对象地址当 DRAM 支撑的指针**:内存与磁盘是一个地址空间
|
||||
- **用盘号/柱面/扇区理解存储**:物理磁盘被 LIC 隐藏
|
||||
- **套 POSIX 文件/进程模型**:这里是对象 + 限定名 + 系统指针
|
||||
- **以为库只是目录**:库本身也是对象、是容器对象
|
||||
- **混淆两套指针**:系统指针指向对象,空间指针指向空间内偏移
|
||||
- **混淆两种存储模型**:teraspace 是 job 本地且不是对象;跨 job 只能经共享内存 API
|
||||
- **把 tagged teraspace 指针存进共享内存或让子进程继承后继续用**
|
||||
- **期待在系统上找到"当初那个 `*MODULE`"**:绑定后它通常已不需要
|
||||
- **把压缩态对象判成损坏/非代码**:使用时会自动解压
|
||||
- **拿二进制比对结果判篡改**:编译时间戳等内嵌字段必然造成差异;优先用编译清单
|
||||
- **在错误的 activation group 里读 `errno`**:运行时可能按组维护
|
||||
- **以为 OPM 程序能进任意 activation group 或用 `*TERASPACE`**:不能
|
||||
- **把 PASE 与 native 环境当同一套调用边界**:PASE 绕过 TIMI,跨边界有额外开销
|
||||
Reference in New Issue
Block a user