Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
8dc03ce
fix: avoid loader-lock work in DllMain detach
H-Chris233 May 25, 2026
0800057
fix: guard WinHttp URL port parsing exceptions
H-Chris233 May 25, 2026
468d8f5
fix: add non-blocking watcher cleanup on explicit unload
H-Chris233 May 25, 2026
7c43c3f
fix: pin module and stop watcher on process detach
H-Chris233 May 25, 2026
9447675
Merge branch 'main' into fix-winhttp-port-parse-guard
H-Chris233 May 26, 2026
37823b9
fix: reject partial-numeric port strings in ParseUrl
H-Chris233 May 26, 2026
9603fbc
Merge branch 'main' into fix-dllmain-detach-loaderlock
H-Chris233 May 27, 2026
f889019
Merge branch 'main' into fix-winhttp-port-parse-guard
H-Chris233 May 28, 2026
161e9b8
add feature portable support
turusudiro May 28, 2026
a36c74c
Merge branch 'main' into fix-winhttp-port-parse-guard
OpenSteam001 Jun 6, 2026
a2fd503
Merge branch 'main' into fix-dllmain-detach-loaderlock
OpenSteam001 Jun 6, 2026
878fb75
ProtectionScan: detect protected entry blob via section flags + entropy
Tesla697 Jun 25, 2026
080d51b
Improve Injection API
Ran-Mewo Jun 25, 2026
c87341e
Merge remote-tracking branch 'upstream/main' into inject-api
Ran-Mewo Jun 25, 2026
f54e36a
improve comment spacing
Ran-Mewo Jun 25, 2026
fa40b33
Ensure owned games allows -onlinefix queueing
Ran-Mewo Jun 25, 2026
42b7a7d
Track env-less Denuvo games + add forcedenuvo / seteticketurl config
Tesla697 Jun 26, 2026
df3d22a
Fix games that rely on P2P
Ran-Mewo Jun 28, 2026
4f253f7
Fix error 54 on Capcom Denuvo: use CredentialStoreThenForge outside a…
Tesla697 Jun 28, 2026
f2dd0d9
add the cloud feature in the README for better visibility
111100001 Jul 25, 2026
092e90f
Pin on-demand eticket mints to the credential store's account
Tesla697 Aug 12, 2026
c105db9
Take the eticket endpoint out of the source, keep the DLL self-contained
Tesla697 Aug 12, 2026
a3ea3ab
Merge branch 'OpenSteam001:main' into denuvo-structural-detection
Tesla697 Aug 12, 2026
aecb3e9
Merge PR #146: Improve Injection API and Fix P2P games
mmxlyo Sep 7, 2026
7f91573
Merge PR #173: Add cloud config documentation to README
mmxlyo Sep 7, 2026
56a3af7
Merge PR #74: Add feature portable support and fix portable runtime p…
mmxlyo Sep 7, 2026
0b863ec
fix(portable): resolve inject DLLs and cloud_redirect relative to por…
mmxlyo Sep 7, 2026
970aab7
Merge PR #148: Structural detection and ownership ticket spoofing
mmxlyo Sep 7, 2026
f20765b
Merge PR #60: Guard WinHttp URL port parsing exceptions
mmxlyo Sep 7, 2026
fb9c50e
Merge PR #57: Avoid loader-lock work in DllMain detach and pin module
mmxlyo Sep 7, 2026
dd60ea9
fix: remove stray merge conflict marker from src/dllmain.cpp
mmxlyo Sep 7, 2026
5b2a83f
fix(portable): redirect all cache, logs, and generated files to porta…
mmxlyo Sep 7, 2026
537a255
fix(denuvo): add nodenuvo Lua directive and prevent false positive on…
mmxlyo Sep 7, 2026
3581c91
feat: merge ost-Injector into project, add English batch scripts, upd…
mmxlyo Sep 7, 2026
18ede75
refactor: move Injector source code from src to tools directory
mmxlyo Sep 7, 2026
868324a
feat: switch to Diversion shadow module memory isolation
mmxlyo Sep 8, 2026
44cf79b
fix(build): resolve HMODULE undeclared identifier and format specifie…
mmxlyo Sep 8, 2026
4a1fdcf
feat(injector): optimize injector subsystem and heuristics against AV…
mmxlyo Sep 8, 2026
b85483f
fix(injector): fix wWinMain argv crash and enhance watcher PID lifecycle
mmxlyo Sep 8, 2026
0d79b82
feat(extract_tickets): auto-generate ready-to-use .lua config alongsi…
mmxlyo Sep 8, 2026
b907670
feat(extract_tickets): extract depot decryption keys into generated l…
mmxlyo Sep 8, 2026
ee9cd58
feat(cloud): transparently redirect GetModuleHandle for CloudRedirect…
mmxlyo Sep 8, 2026
a867d66
fix(extract_tickets): fix compilation errors under MSVC and copy comp…
mmxlyo Sep 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,16 @@ jobs:

Download `OpenSteamTool-${{ github.event.inputs.version }}-Release.zip` (or the `-Debug` variant if you need logging to `<steam>/opensteamtool directory`).

Extract and copy `dwmapi.dll`, `xinput1_4.dll` and `OpenSteamTool.dll` to your Steam root directory (e.g. `C:\Program Files (x86)\Steam`).

Create a Lua config directory (for example `C:\Program Files (x86)\Steam\config\lua`) and place your Lua scripts there. **NOT** `C:\Program Files (x86)\Steam\config\stplug-in`!
### Method 1: Portable Mode (Recommended, ost-Injector)
1. Extract the zip to any standalone folder outside the Steam directory.
2. Place your Lua unlock scripts in `config/lua/` inside that folder.
3. Run `ost-Injector.exe` to launch/inject into Steam, or run `CreateAutoInjectTask.bat` to set up background auto-injection on system startup.
*(No files are placed in or modify your Steam directory!)*

### Method 2: Standard Mode (DLL Hijacking)
1. Extract and copy `dwmapi.dll`, `xinput1_4.dll` and `OpenSteamTool.dll` directly to your Steam root directory (e.g. `C:\Program Files (x86)\Steam`).
2. Create a Lua config directory (`C:\Program Files (x86)\Steam\config\lua`) and place your Lua scripts there. **NOT** `config\stplug-in`!
files: |
OpenSteamTool-${{ github.event.inputs.version }}-Release.zip
OpenSteamTool-${{ github.event.inputs.version }}-Debug.zip

583 changes: 313 additions & 270 deletions README.md

Large diffs are not rendered by default.

25 changes: 20 additions & 5 deletions README_ES.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,25 @@ La herramienta `extract_tickets` vuelca las cadenas hexadecimales de `AppTicket`
- Soporte para la sincronización con Steam Cloud (este es un proyecto enorme).

## Uso
1. Ejecuta `build.bat` desde la raíz del proyecto para compilarlo.

### Método 1: Modo Portátil (Recomendado, usando ost-Injector)

El modo portátil funciona de forma completamente independiente: **no se coloca ninguna DLL en el directorio de Steam y la carpeta de instalación de Steam permanece intacta**:

1. Extrae el paquete de lanzamiento (que contiene `ost-Injector.exe`, `OpenSteamTool.dll`, `CreateAutoInjectTask.bat`, `DeleteAutoInjectTask.bat`, `config.ini`, etc.) en cualquier carpeta portátil independiente (por ejemplo, `D:\OpenSteamTool_Portable`).
2. Crea una carpeta `config/lua/` en ese directorio y coloca allí tus scripts Lua de desbloqueo (como `games.lua`).
3. Elige un método de inicio:
- **Inicio Manual**: Ejecuta `ost-Injector.exe` directamente. Detectará o iniciará Steam e inyectará `OpenSteamTool.dll` tan pronto como la interfaz de Steam esté lista.
- **Inyección Automática al Iniciar Sesión**: Haz clic derecho en `CreateAutoInjectTask.bat` y selecciona "Ejecutar como administrador" para crear una tarea programada. El inyector se ejecutará silenciosamente en segundo plano (modo `-watch`) y se inyectará automáticamente cada vez que se inicie Steam. Para desinstalar la tarea, haz clic derecho y ejecuta `DeleteAutoInjectTask.bat` como administrador.
- **Línea de Comandos**: `ost-Injector.exe` admite `-watch` (demonio en segundo plano) y `-silent` (inyección silenciosa única). El archivo `config.ini` permite personalizar la ruta del ejecutable de Steam y la ruta de la DLL.

### Método 2: Modo Estándar (Secuestro de DLL / DLL Hijacking)

1. Ejecuta `build.bat` desde la raíz del proyecto para compilarlo, o descarga un paquete Release precompilado.
2. Copia los archivos generados `dwmapi.dll`, `xinput1_4.dll` y `OpenSteamTool.dll` al directorio raíz de Steam.
3. Crea un directorio para Lua (por ejemplo, C:\steam\config\lua) y coloca allí tus scripts de Lua. La DLL los cargará y ejecutará automáticamente.
4. Ejemplo de Lua:
3. Crea un directorio para Lua (por ejemplo, `C:\Program Files (x86)\Steam\config\lua`) y coloca allí tus scripts de Lua. La DLL los cargará y ejecutará automáticamente.

### Ejemplo de Configuración Lua
```lua
addappid(1361510) -- desbloquea el juego con appid 1361510

Expand Down Expand Up @@ -256,9 +271,9 @@ build.bat
```

### Archivos de salida
- Debug: `build/Debug/OpenSteamTool.dll`, `build/Debug/dwmapi.dll`, `build/Debug/xinput1_4.dll`
- Debug: `build/Debug/OpenSteamTool.dll`, `build/Debug/dwmapi.dll`, `build/Debug/xinput1_4.dll`, `build/Debug/ost-Injector.exe`, y scripts auxiliares copiados automáticamente.

- Release: `build/Release/OpenSteamTool.dll`, `build/Release/dwmapi.dll`, `build/Release/xinput1_4.dll`
- Release: `build/Release/OpenSteamTool.dll`, `build/Release/dwmapi.dll`, `build/Release/xinput1_4.dll`, `build/Release/ost-Injector.exe`, y scripts auxiliares copiados automáticamente.

## Descargo de responsabilidad
Este proyecto se proporciona únicamente con fines de investigación y educativos. Eres responsable de cumplir con las leyes locales, los términos de servicio de la plataforma y las licencias de software correspondientes.
39 changes: 26 additions & 13 deletions README_ZH.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,22 +72,21 @@
extract_tickets.exe 1361510
```
3. 它从注册表读取 Steam 安装路径,加载 `steamclient64.dll`,并将所有内容写入可执行文件旁边的 `<appid>/` 文件夹:
- `<appid>.lua` — 自动生成开箱即用的完整 Lua 配置文件(包含 `addappid`、提取的 Depot 解密密钥、`setAppTicket`、`setETicket`,可直接复制到 `config/lua/` 目录使用)
- `depot_<depotid>.key` — 原始 32 字节 Depot 解密密钥(若本地缓存存在)
- `appticket.bin` — 原始应用所有权令牌(二进制)
- `eticket.bin` — 原始加密应用令牌(二进制)
- `tickets.txt` — 包含十六进制字符串的纯文本摘要
- `tickets.txt` — 包含密钥与令牌十六进制字符串的纯文本摘要
```
appid:1361510
depotkey(1361511):5954562e...
appticket(184 bytes):14000000...
eticket(143 bytes):...
```
无法获取的令牌报告为 `appticket:null` / `eticket:null`
4. 将 `tickets.txt` 中的十六进制字符串粘贴到你的 Lua 配置中:
```lua
setAppTicket(1361510, "14000000...")
setETicket(1361510, "...")
```
4. 工具会自动在输出目录生成完整、可直接使用的 `<appid>.lua` 脚本;你也可以运行 `ConvertTicketsToLua.bat`(或 `ConvertTicketsToLua.ps1`)批量将已有的 `tickets.txt` 转换为包含密钥的 `.lua` 文件。

> **注意:** 令牌仅当从**真正拥有**游戏的账户提取时才有效
> **注意:** 令牌和解密密钥仅当从**真正拥有**该游戏的账户提取时才有效。如果尚未在 Steam 下载过该游戏,在 Steam 中点击一次安装/下载即可缓存对应 Depot 的解密密钥到本地。

### 统计和成就
- 为未拥有的游戏启用统计和成就
Expand All @@ -103,10 +102,24 @@

## 使用方法

1. 在项目根目录运行 `build.bat` 构建项目
2. 将生成的 `dwmapi.dll`、`xinput1_4.dll` 和 `OpenSteamTool.dll` 复制到 Steam 根目录
3. 创建 Lua 目录(例如 `C:\steam\config\lua`)并将 Lua 脚本放在那里。DLL 会自动加载并执行它们
4. Lua 示例:
### 方式一:便携模式(推荐,使用 ost-Injector)

便携模式完全独立运行,**无需向 Steam 安装目录放置任何 DLL,也不改动 Steam 文件夹**:

1. 解压构建好的发布包(包含 `ost-Injector.exe`、`OpenSteamTool.dll`、`CreateAutoInjectTask.bat`、`DeleteAutoInjectTask.bat`、`config.ini` 等)到任意独立便携目录(例如 `D:\OpenSteamTool_Portable`)。
2. 在该目录下创建 `config/lua/` 文件夹,并放入游戏或 DLC 解锁脚本(如 `games.lua`)。
3. 选择启动方式:
- **手动启动**:直接双击运行 `ost-Injector.exe`,注入器会自动检测或拉起 Steam,并在 Steam UI 就绪后自动完成注入。
- **开机自动静默注入**:右键以管理员身份运行 `CreateAutoInjectTask.bat`,即可创建开机登录计划任务。注入器将在后台以 `-watch` 模式常驻静默监听,一旦检测到 Steam 启动立即自动完成注入。若需移除自启任务,右键管理员运行 `DeleteAutoInjectTask.bat` 即可。
- **命令行模式**:`ost-Injector.exe` 支持 `-watch`(后台常驻监听)与 `-silent`(单次静默注入)。默认配置文件 `config.ini` 可自定义 Steam 可执行程序路径与目标 DLL 路径。

### 方式二:标准模式(DLL 劫持)

1. 在项目根目录运行 `build.bat` 构建项目,或下载预编译 Release 包。
2. 将生成的 `dwmapi.dll`、`xinput1_4.dll` 和 `OpenSteamTool.dll` 复制到 Steam 根目录。
3. 创建 Lua 目录(例如 `C:\Program Files (x86)\Steam\config\lua`)并将 Lua 脚本放在那里。DLL 会自动加载并执行它们。

### Lua 配置示例
```lua
addappid(1361510) -- 解锁 appid 为 1361510 的游戏

Expand Down Expand Up @@ -264,8 +277,8 @@ build.bat
```

### 输出
- Debug:`build/Debug/OpenSteamTool.dll`、`build/Debug/dwmapi.dll`、`build/Debug/xinput1_4.dll`
- Release:`build/Release/OpenSteamTool.dll`、`build/Release/dwmapi.dll`、`build/Release/xinput1_4.dll`
- Debug:`build/Debug/OpenSteamTool.dll`、`build/Debug/dwmapi.dll`、`build/Debug/xinput1_4.dll`、`build/Debug/ost-Injector.exe` 以及自动复制的辅助脚本
- Release:`build/Release/OpenSteamTool.dll`、`build/Release/dwmapi.dll`、`build/Release/xinput1_4.dll`、`build/Release/ost-Injector.exe` 以及自动复制的辅助脚本

## 免责声明
本项目仅供研究和教育目的使用。你负责遵守当地法律、平台服务条款和软件许可证。
5 changes: 5 additions & 0 deletions build.bat
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ for %%C in (%CONFIGS%) do (
cmake --build build --config %%C
if errorlevel 1 goto :fail

REM ost-Injector and extract_tickets build steps
echo [INFO] Building tool ost-Injector for %%C
cmake --build build --config %%C --target ost-Injector
if errorlevel 1 goto :fail

REM extract_tickets is EXCLUDE_FROM_ALL, so build it explicitly. It lands in
REM build\tools\%%C\ rather than the shipped output directory.
echo [INFO] Building tool extract_tickets for %%C
Expand Down
21 changes: 12 additions & 9 deletions opensteamtool.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,27 +71,30 @@ enable_api = true
[lua]
# paths = []

[inject]
# Optional library injection into game processes.
# The injected library must match the target process architecture.
enabled = false
# library_x64 = "OpenSteamTool.GameHook.x64.dll"
# library_x86 = "OpenSteamTool.GameHook.x86.dll"
# Optional library injection into game processes. Each [[inject]] entry is loaded
# when every condition it sets matches the launch.
# Example:
# [[inject]]
# path = "OpenSteamToolHook.dll" # bare name resolves next to steam.exe; absolute path used as-is
# when_cmdline = "-my_special_hook" # optional: require this substring in the launch command (default: any)
# when_appids = [1361510] # optional: restrict to these appids (default: any)
# all_games = false # optional: true injects into every game, false only into Lua-added games (default: false)

[cloud]
# Optional Steam Cloud save redirection for unlocked ("lua") games, powered by
# CloudRedirect (https://github.com/Selectively11/CloudRedirect).
# When enabled, OpenSteamTool loads cloud_redirect.dll inside Steam, registers
# every addappid() game as a redirected app, and routes their Steam Cloud RPCs
# through CloudRedirect's cloud-save engine.
# through CloudRedirect's cloud-save engine (fully compatible with Diversion memory isolation).
#
# Provider sign-in (Google Drive / OneDrive / local folder) is still done through
# CloudRedirect's own companion app — OpenSteamTool only hosts the DLL.
enabled = false
# Path to cloud_redirect.dll. Absolute, or relative to the Steam root directory.
# Defaults to "<Steam>/cloud_redirect.dll" when unset.
# Path to cloud_redirect.dll. Absolute, or relative to opensteamtool.toml, DLL dir, or Steam root.
# Defaults to searching alongside opensteamtool.toml, OpenSteamTool.dll, then Steam root.
# library = "cloud_redirect.dll"


[remote]
# Optional metadata mirror. Leave unset to use GitHub with jsDelivr fallback.
# A custom mirror replaces the built-in remote sources and must include all
Expand Down
26 changes: 26 additions & 0 deletions scripts/CreateAutoInjectTask.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
@echo off
chcp 65001 >nul
echo =======================================================
echo OpenSteamTool - Setup Auto Inject Task
echo =======================================================
echo.
echo Creating scheduled task "OpenSteamTool_AutoInject"...
schtasks /create /tn "OpenSteamTool_AutoInject" /tr "\"%~dp0ost-Injector.exe\" -watch" /sc onlogon /rl highest /f
if %errorlevel% equ 0 (
echo.
echo =======================================================
echo [SUCCESS] Scheduled task "OpenSteamTool_AutoInject" created!
echo Starting background watcher service right now...
schtasks /run /tn "OpenSteamTool_AutoInject"
echo The background watcher is now running and will auto-start upon logon,
echo automatically injecting OpenSteamTool.dll whenever Steam starts.
echo =======================================================
) else (
echo.
echo =======================================================
echo [FAILED] Failed to create scheduled task.
echo Please right-click this script and select "Run as administrator".
echo =======================================================
)
echo.
pause
21 changes: 21 additions & 0 deletions scripts/DeleteAutoInjectTask.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
@echo off
chcp 65001 >nul
echo =======================================================
echo OpenSteamTool - Remove Auto Inject Task
echo =======================================================
echo.
echo Deleting scheduled task "OpenSteamTool_AutoInject"...
schtasks /delete /tn "OpenSteamTool_AutoInject" /f
if %errorlevel% equ 0 (
echo.
echo =======================================================
echo [SUCCESS] Scheduled task removed successfully!
echo =======================================================
) else (
echo.
echo =======================================================
echo [INFO] Task does not exist or has already been removed.
echo =======================================================
)
echo.
pause
3 changes: 3 additions & 0 deletions scripts/config.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[Settings]
ExePath=C:\Program Files (x86)\Steam\steam.exe
DllPath=OpenSteamTool.dll
27 changes: 27 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ add_library(OpenSteamTool SHARED

# Shared utilities
Utils/Tickets/AppTicket.cpp
Utils/Tickets/EticketClient.cpp
Utils/Config/Config.cpp
Utils/Config/ConfigFileWatcher.cpp
Utils/Config/LuaConfig.cpp
Expand Down Expand Up @@ -168,6 +169,22 @@ target_compile_definitions(OpenSteamTool PRIVATE
$<$<CONFIG:Debug>:OPENSTEAMTOOL_LOGGING_ENABLED>
)

# Backend endpoint for on-demand eticket minting (strict Denuvo titles that bind
# their encrypted app ticket to a launch nonce). Empty by default: the feature is
# off and the DLL never makes a network request. Point your own build at your own
# backend and the resulting DLL is self-contained, no Lua config needed:
#
# cmake -B build -DOST_ETICKET_URL="https://your-host/eticket"
#
# Left out of the source deliberately so no single deployment's backend ships
# baked into a public tree. seteticketurl() in the Lua config overrides it.
set(OST_ETICKET_URL "" CACHE STRING
"Backend URL for on-demand eticket minting (empty disables the feature)")
if(OST_ETICKET_URL)
target_compile_definitions(OpenSteamTool PRIVATE
OST_ETICKET_URL="${OST_ETICKET_URL}")
endif()

# ---------------------------------------------------------------------------
# dwmapi.dll hijack — small loader DLL placed alongside Steam.
# ---------------------------------------------------------------------------
Expand All @@ -182,3 +199,13 @@ add_library(xinput1_4 SHARED
xinput1_4/xinput1_4.cpp
xinput1_4/xinput1_4.def
)

# ---------------------------------------------------------------------------
# ost-Injector — portable injector executable (defined in tools/CMakeLists.txt).
# Ensure ost-Injector is always built alongside OpenSteamTool.
# ---------------------------------------------------------------------------
if(TARGET ost-Injector)
add_dependencies(OpenSteamTool ost-Injector)
endif()


13 changes: 13 additions & 0 deletions src/Hook/Hooks_IPC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,24 @@ namespace {
PipeManager::OnHandshake(pipe);
}

// Detect the first SteamNetworkingSockets call (interface 46) so GetAppID can
// flip to 480 for P2P games. Skipped once already seen or when not in onlinefix.
static void DetectNetworkingSockets(CUtlBuffer* pRead) {
if (!Hooks_Misc::IsOnlineFixActive() || Hooks_Misc::ShouldReportOnlineFixAppId()) return;
IPCMessages::IPCRequest request{pRead};
if (!request.ok() || request.command() != EIPCCommand::InterfaceCall) return;
IPCMessages::IPCInterfaceCall call{request.body()};
if (!call.ok()) return;
if (call.interfaceID() == EIPCInterface::IClientNetworkingSocketsSerialized)
Hooks_Misc::NotifyNetworkingSocketsUsed();
}

HOOK_FUNC(IPCProcessMessage, bool,void* pServer, HSteamPipe hSteamPipe,
CUtlBuffer* pRead, CUtlBuffer* pWrite)
{
// handle handshake messages
HandleHandshake(pServer, hSteamPipe, pRead);
DetectNetworkingSockets(pRead);

IPCDispatch dispatch = ResolveDispatch(pServer, hSteamPipe, pRead);
// If we didn't find a handler for this message, just pass through to the original function.
Expand Down
Loading