Yonsm.NET 2018-05-30T03:09:50+00:00 Yonsm@msn.com espurna - 很棒的 Iot 设备/模块固件 2018-05-21T10:36:04+00:00 Yonsm http://yonsm.net/espurna espurna是一个非常高效、稳定的固件,支持Sonoff、NodeMCU和论坛的Hassmart 86模块(以及更多我没验证过的模块)。espurna很多配置是静态编译开关配置的,很适合批量产品(不像ESPEasy那样刷入后来动态配置),稳定性非常高,在Sonoff basic和Hassmart 86模块中运行20多天未重启(预期跑个半年没问题,哈哈)。

原作者代码主页:https://github.com/xoseperez/espurna 建议看看我修改过的配置:https://github.com/Yonsm/espurna/tree/Yonsm,具体修改了什么自己对比代码吧或者看git 记录吧…

另外,我为espurna增加了攀藤 PMS5003T/PMS5003ST 传感器的支持,完全重构替换了原作者PMS5003的代码,同时增加了自动休眠支持(否则一个传感器8000个小时/一年内就达到寿命极限了);同时添加了 Sense Air S8 CO2 传感器的支持。这些代码均已 PR 给原作者并已经合并到master主干。相关功能需自己通过编译配置来启用,或者NodeMCU可参考https://github.com/Yonsm/espurna/tree/Yonsm分支中的 nodemcu_lolin 配置。

原帖发布在:https://bbs.hassbian.com/thread-3814-1-1.html,贴中附件是我编译的部分Sonoff basic和Hassmart 86的固件,供不会编译的朋友使用。

]]>
Home Assistant 0.68 开始支持 HomeKit/Siri 了 2018-04-28T10:24:14+00:00 Yonsm http://yonsm.net/homekit Home Assistant 0.68 已经基本支持了绝大部分原来 HomeBridge 的能支持的组件,不用装node的HB,系统依赖大大减少,稳定性能系统消耗也小得多。现在理论上只要python3和libavahi-compat-libdnssd-dev就可以跑完整的HA和homekit功能了,路由器上运行HA应该不是问题了。

我PR了 binary_sensor、device_trackerCO2、light、PM2.5等传感器组件,已经包含在发布版本中了。另外 fan、media_player、vaccum 我当做switch先勉强用一下,需要的话可以参考我个人的HA版本:https://github.com/Yonsm/home-assistant/tree/Yonsm,后面会逐步支持完整的风扇、空气净化器组件,也会尝试PR到HA正式版本中。

HomeKit组件的文档在这:https://www.home-assistant.io/components/homekit/

其实配置很简单,configuration中加入一行 homekit: 可以了

树莓派,执行(装过HB的就不用这个了):sudo apt-get install libavahi-compat-libdnssd-dev 如果是非虚拟环境安装,提示加密错误,执行:pip3 install pycryptodome

]]>
ModBus 空调组件及中央空调接入 Home Assistant 简述 2018-04-28T10:09:04+00:00 Yonsm http://yonsm.net/modbus 写了个通用的 modbus.py 组件接入了HA,支持HomeKit/Siri,天猫精灵,简直是完美啊….

支持通用的ModBus协议的空调,包括走网络TCP的。另外新版HA里面有个modbus组件,写得实在不怎么样,也不具通用性。

Preview1

Preview2

Preview3

Preview4

1. 代码在此:

https://github.com/Yonsm/HAExtra/blob/master/custom_components/climate/modbus.py

复制 homeassistant 的配置目录中 custom_components/climate/ 下,无此目录请创建。

2. 配置方法如下:

modbus:
  type: rtuovertcp
  host: 192.168.x.x
  port: 8899
 
climate:
  - platform: modbus
    name: Daikin
    temperature: {register: 3, register_type: input, scale: 0.1}
    target_temperature: {register:4}
    operation: {register: 5}
    fan: {register: 6}
    is_on: {register: 1, register_type: coil}

完整的功能配置如下:

- platform: modbus
  name: Daikin
  temperature: {registers: [3,7,11], register_type: input, scale: 0.1}
  target_temperature: {registers:[4,8,12]}
  operation: {registers: [5,9,13]}
  fan: {registers: [6,10,14]}
  is_on: {registers: [1,2,3], register_type: coil}
  humidity: {registers: [30,31,32], register_type: input, scale: 0.1}
  target_humidity: {registers:[40,41,42]}
  swing: {registers:[50,51,52]}
  hold: {registers:[60,61,62]}
  away: {registers:[70,71,72]}
  aux: {registers:[80,81,82]}
  operation_list: [无效, 制冷, 制热, 除湿, 送风]
  fan_list: [自动, 一级, 二级, 三级, 四级, 五级]

3. 关于RTU模块

大金、美的、日立、海信等中央空调通用的方案,ModBus RTU模块980买的,再买了个485串口转WIFI的模块200,1180搞定了。ModBus RTU模块在X宝上找了唯一个一,980买的,…。485串口转WIFI买的是“有人”的,墙裂推荐,一看就是认真做事的公司的产品。网上没有现成的方案,过程中全部自己接线、调试,差点快被难度吓到放弃了——终于搞定了,简直是不可能完成的任务…

原帖发布在:https://bbs.hassbian.com/thread-3581-6-1.html

补充:最近看到一个接线视频,虽然不是一样的方案,但接线方法可以参考。

]]>
Saswell 地暖控制面板 - Home Assistant 插件 2018-04-11T12:31:09+00:00 Yonsm http://yonsm.net/saswell 从和Saswell官方app 的网关中拉取数据;过期了会自动拉取 token 无需操心和干预;比官方 app 使用和操作方便多了(官方 app 的日程操作很难用,不如直接在面板上设置);同时 HomeAssistant 启用 homekit 组件后支持在 Siri 中完美控制。

Preview

1. 代码在此:

https://github.com/Yonsm/HAExtra/blob/master/custom_components/climate/saswell.py

复制 homeassistant 的配置目录中 custom_components/climate/ 下,无此目录请创建。

2. 配置方法如下:

climate:
  - platform: saswell
    #name: Saswell
    username: ***@email.com
    password: ********
    #scan_interval: 300

自动列出账户下实际数量的设备,默认五分钟同步一次温度和状态(是不是慢了点儿,不过地暖本来就很慢:)

3. 关于Saswell 温控面板

型号为 SAS920WHL-7W-WIFI,价格368,X东和X猫均可购买(商家跟我没关系)。我自己家公共空间换用了 WIFI 版,卧室都是用199的无 WIFI 周编程版,型号SAS920WHL-7W,每天4-6个时间点定期自动切换,也够用了。

自行更换面板很容易,我原来是沃茨 W-H4111L(意格供暖js竟然不给有周编程功能的 W-H4111P ,不然就不用这么折腾了)

原帖发布在:https://bbs.hassbian.com/thread-3387-1-1.html

]]>
HAGenie - 天猫精灵一步接入 HomeAssistant 2018-02-06T09:13:15+00:00 Yonsm http://yonsm.net/hagenie 一、使用方法

天猫精灵后台配置流程,请参考这篇文章的步骤。直说能用的配置:

  • 账户授权连接:https://hagenie.ga:8122/authorize.py
  • Client ID:https:xxx.xxx.xx:8123(注意不要//)
  • Client Secret:password(如果没有密码随便输入即可,关于密码的建议使用姿势参见最后)
  • Access Token URL:https://hagenie.ga:8122/access.py
  • 开发者网关地址:https://hagenie.ga:8122/gate.py
  • 设备管理跳转连接:(空)

以上配置后直接可以把天猫精灵和 HA 驳接起来了,不需要搭建任何服务器。目前支持除 sensor 外的其它设备的打开和关闭,能自动列出设备,基本零配置搞定全部事情。

二、进阶配置

天猫精灵最重要的三个参数:

  1. zone:仅支持特定的区域,如客厅、餐厅等,详细看这里
  2. deviceName:设备名称也只支持特定的,否则可以显示但它听不懂可能不能控制
  3. deviceType:必须从这个文档中候选

在 gate.py 中会尝试自动从 HA 的配置信息中,根据一些规则尝试自动识别。但有时候可能识别不了,可以在 customize.yaml 中为设备设定天猫精灵能认出来的区域和名称:

hagenie_zone: 客厅
hagenie_deviceName: 吸顶灯
hagenie_deviceType: outlet

以上 hagenie_zone 即可以在HA device entity设备中配置,也可以HA group entity下面给组内所有设备配置。

建议:把设备分组,分组名来自这里,设备名从这里选择 ,这样剩下的事情会自动搞定,不用配置。

三、自定义服务器

注意:以上配置理把所有密码发往我的服务器,虽然我肯定谨守不存、不看、不用三不政策(老社区信用口碑网友:),如果简单测试可以这么玩玩,如果要长期用建议自己搭建服务器,所有代码在这里,持续更新完善中:

https://github.com/Yonsm/HAExtra/tree/master/hagenie

  1. 服务器环境:其中三个 py 文件即可以在 apache 的 cgi-bin 下跑;也可以用 hagenie.py 作为服务器运行(需要完整的 server.pem,记住是需要 chain.pem cert.pem privkey.pem 合一,我被天猫精灵SSL检查这个坑了好久——如果看不明白,建议用前面的简单方式吧),hagenie.py可以在树莓派上跑,但响应比较慢会被天猫精灵服务器直接超时放弃,多试几次可能就好了,但控制起来了比较慢也不爽。

  2. Python 需求:以上支持 python2 或 python3,自动适应;如果 HA 是 https 的需要安装 requests,否则自动使用 urllib 或 urllib2。

  3. 实现细节:直接用 python 做了个伪 oauth2,并且把 oauth流程中流转的数据字段充分利用起来,用来传递 HA URL 地址和密码了…玩玩可以的,不要批评我的 “Oh … Alternative User Token Handover…to…”,哈哈

四、最佳姿势

如果你直接使用 hagenie.ga 服务器,推荐的姿势是使用密码(以便自己访问),但在天猫精灵开发者控制台中不要输入正确的密码(这样不会被 hagenie 服务器看到明文密码了),但在 configuration.yaml 中加入以下配置来信任 hagenie.ga 服务器的 IP 地址:

http:
  api_password: *****
  trusted_networks:
    - 127.0.0.1
    - 101.132.66.191
    - 192.168.1.0/24

原帖发布在:https://bbs.hassbian.com/thread-2700-1-1.html

]]>
斐讯悟空 M1 传感器 - Home Assistant 插件 2018-02-05T05:05:51+00:00 Yonsm http://yonsm.net/phicomm 模拟斐讯的手机 app,从斐讯官方拉取数据;过期了会自动拉取 token 无需操心和干预;也不会和官方的“斐讯空能净” 互斥登录踢出。

Preview

1. 代码在此:

https://github.com/Yonsm/HAExtra/blob/master/custom_components/sensor/phicomm.py

复制 homeassistant 的配置目录中 custom_components/sensor/ 下,无此目录请创建。

2. 配置方法如下:

sensor:
  - platform: phicomm
    #name: Phicomm
    username: 139********
    password: ********
    #scan_interval: 120
    #sensors: [pm25,hcho,temperature,humidity]

自动列出账户下的多个斐讯悟空设备;启用了四个传感器类型;每60秒更新一次;每个传感器均仔细挑选了 mdi 的图标,无需额外配置。

3. 【可选】上面配置好了之后就可以了,但默认是英文名称。建议 customize.yaml 中配置中文名称如下:

sensor.aircat_temperature:
  friendly_name: 温度
sensor.aircat_humidity:
  friendly_name: 湿度
sensor.aircat_pm25:
  friendly_name: 颗粒物
  homebridge_name: 空气质量
sensor.aircat_hcho:
  friendly_name: 甲醛

4. 【可选】建议在 groups.yaml 中添加如下代码进行分组:

xxxx_room:
    entities:
      - sensor.phicomm_temperature
      - sensor.phicomm_humidity
      - sensor.phicomm_pm25
      - sensor.phicomm_hcho

原帖发布在:https://bbs.hassbian.com/thread-2698-1-1.html

]]>
彩云天气传感器 - Home Assistant 插件 2018-02-04T04:48:25+00:00 Yonsm http://yonsm.net/caiyun 对标 HA 内置 yr 天气插件的高质量 HA custom_component。

Preview

1. 代码在此:

https://github.com/Yonsm/HAExtra/blob/master/custom_components/sensor/caiyun.py

复制 homeassistant 的配置目录中 custom_components/sensor/ 下,无此目录请创建。

2. 配置方法和 yr 类似,最简单的配置版本如下:

sensor:
  - platform: caiyun

以上默认启用这些传感器20分钟更新一次,自动使用 HA 中配置的地理位置。

下面是完整的配置项,每个传感器均仔细挑选了 mdi 的图标,无需额外配置:

sensor:
  - platform: caiyun
    #name: CaiYun
    #scan_interval: 1200
    #latitude: 30.000
    #longitude: 120.000
    monitored_conditions: # Optional
      - weather
      - temperature
      - humidity
      - cloud_rate
      - pressure
      - wind_direction
      - wind_speed
      - local_precipitation
      - nearest_precipitation
      - precipitation_distance
      - aqi
      - pm25
      - pm10
      - o3
      - co
      - no2
      - so2

3. 【可选】上面配置好了之后就可以了,但默认是英文名称。建议 customize.yaml 中配置中文名称如下:

group.outside:
  friendly_name: 室外
sun.sun:
  friendly_name: 日照

sensor.caiyun_weather:
  friendly_name: 天气
sensor.caiyun_temperature:
  friendly_name: 室外气温
sensor.caiyun_humidity:
  friendly_name: 室外湿度
sensor.caiyun_cloud_rate:
  friendly_name: 云量
sensor.caiyun_pressure:
  friendly_name: 气压
sensor.caiyun_wind_direction:
  friendly_name: 风向
sensor.caiyun_wind_speed:
  friendly_name: 风速
sensor.caiyun_local_precipitation:
  friendly_name: 降水强度
sensor.caiyun_nearest_precipitation:
  friendly_name: 附近降水强度
sensor.caiyun_precipitation_distance:
  friendly_name: 附近降水距离
sensor.caiyun_aqi:
  friendly_name: 空气指数
sensor.caiyun_pm25:
  friendly_name: 室外颗粒物
  homebridge_name: 空气质量
sensor.caiyun_pm10:
  friendly_name: 室外大颗粒物
sensor.caiyun_o3:
  friendly_name: 室外臭氧
sensor.caiyun_co:
  friendly_name: 室外一氧化碳
sensor.caiyun_no2:
  friendly_name: 室外二氧化氮
sensor.caiyun_so2:
  friendly_name: 室外二氧化硫

4. 【可选】建议在 groups.yaml 中添加如下代码进行分组:

outside:
    entities:
      - sun.sun
      - sensor.caiyun_weather
      - sensor.caiyun_temperature
      - sensor.caiyun_humidity
      - sensor.caiyun_cloud_rate
      - sensor.caiyun_pressure
      - sensor.caiyun_wind_direction
      - sensor.caiyun_wind_speed
      - sensor.caiyun_local_precipitation
      - sensor.caiyun_nearest_precipitation
      - sensor.caiyun_precipitation_distance
      - sensor.caiyun_aqi
      - sensor.caiyun_pm25
      - sensor.caiyun_pm10
      - sensor.caiyun_o3
      - sensor.caiyun_co
      - sensor.caiyun_no2
      - sensor.caiyun_so2

原帖发布在:https://bbs.hassbian.com/thread-2697-1-1.html

]]>
BestVideo - ffmpeg 转码脚本 for macOS & Windows 2017-03-10T10:46:31+00:00 Yonsm http://yonsm.net/bestvideo ffmpeg 是全能的多媒体文件转码工具,也是很多 GUI 类转码软件的后端,但实际用下来这些 GUI 转码软件经常达不到各种定制化的要求,最终还是人肉敲命令行用的靠谱。但是每次敲命令行,各种参数记不住、复杂、累。做成批量的脚本,用起来比较舒服,还可方便普通用户,一键双击批量转码。在此基础上,还能基于 macOS bash shell 和 Windows batch 脚本,做到了简单的 srt/ass 脚本编码检测并混合硬字幕功能,不妨一看:

功能:

  1. 批量处理指定目录、当前目录、脚本目录下的 mp4 mkv mov avi wmv vob 文件(其它扩展名自行编辑添加);
  2. 处理指定文件名;
  3. 混合同文件名名 srt/ass 字幕(自动检测 UTF 编码,默认 GB18030 编码);

使用步骤:

  1. 下载附件,解压缩;
  2. (可选)修改 BestVideo.cmd 或 BestVide.sh 中的相关参数(主要是目标宽度和高度,以及其它更进一步的定制化参数修改)。
  3. 把要转码的文件放在通 BestVideo.cmd 或 BestVide.sh 同一个目录;
  4. (可选)如果需要硬编码字幕,确保 srt 和 ass 字幕文件和视频文件同名;
  5. 双击 BestVideo.cmd (Windows)或 BestVide.sh (macOS,之前需确保 chmod +x BestVideo.sh 过),等待转码完成;
  6. 在 OUT 目录下可以找到同名视频文件。

下载

下载 macOS 版

下载 Windows 版

脚本

macOS 版(在10.12.3测试通过)

#!/bin/sh

inTypes="mp4|mkv|mov|avi|wmv|vob"
outDir=OUT

videoWidth=720
videoHeight=338
videoLevel=3.1
videoProfile=main
videoConstantRateFactor=17

audioChannel=2
audioCodec=aac
audioBitRate=225k
audioSampleRate=44.1k

audioOptions="-acodec $audioCodec -ab $audioBitRate -ar $audioSampleRate -ac $audioChannel"
videoOptions="-s ${videoWidth}x$videoHeight -vcodec libx264 -crf $videoConstantRateFactor -profile:v $videoProfile -level $videoLevel"
cropOptions1="-vf crop=in_w:in_w*$videoHeight/$videoWidth"
cropOptions2="-vf crop=in_h*$videoWidth/$videoHeight:in_h"

CDIR=$(cd "${0%/*}"; pwd)
PATH=$CDIR:$PATH
pushd $PWD >/dev/null

if [ $# = 0 ]; then
	videoPath=.
else
	videoPath="$1"
fi

MakeVideo()
{
	if [ ! -d "$outDir" ]; then mkdir "$outDir"; fi

	subtitle="${1%.*}.ass"
	if [ ! -f "$subtitle" ]; then subtitle="${1%.*}.srt"; fi
	if [ -f "$subtitle" ]; then 
		detectedCharset=`file -b --mime-encoding "$subtitle"`
		if [[ "$detectedCharset" =~ "utf" ]]; then charsetOption=; else charsetOption=":charenc=GB18030"; fi
		echo "Subtitle: $subtitle"
		echo "Charset: $detectedCharset"
		ffmpeg -i "$1" -y $audioOptions $videoOptions $cropOptions1,subtitles="$subtitle"$charsetOption "$outDir/${1%.*}.mp4" </dev/null
		if [ $? == 1 ]; then
			ffmpeg -i "$1" -y $audioOptions $videoOptions $cropOptions2,subtitles="$subtitle"$charsetOption "$outDir/${1%.*}.mp4" </dev/null
		fi
	else
		ffmpeg -i "$1" -y $audioOptions $videoOptions $cropOptions1 "$outDir/${1%.*}.mp4" </dev/null
		if [ $? == 1 ]; then
			ffmpeg -i "$1" -y $audioOptions $videoOptions $cropOptions2 "$outDir/${1%.*}.mp4" </dev/null
		fi
	fi
}

if [ -d "$videoPath" ]; then
	cd "$videoPath"
	find -E . -iregex ".*\.($inTypes)" -maxdepth 1 | while read f ; do MakeVideo "${f##*/}" ; done
	if [ ! -d "$outDir" ]; then
		cd "$CDIR"
		find -E . -iregex ".*\.($inTypes)" -maxdepth 1 | while read f ; do MakeVideo "${f##*/}" ; done
		if [ ! -d "$outDir" ]; then
			echo "Usage: $0 [FILE|DIR|] - Empty means ./ or $CDIR"
		fi
	fi
else
	if [[ "${videoPath}" =~ "/" ]]; then cd "${videoPath%/*}"; fi
	MakeVideo "${videoPath##*/}"
fi

popd >/dev/null

Windows 版(在 Windows 7 下测试通过)

@ECHO OFF

SET inTypes=*.mp4 *.mkv *.mov *.avi *.wmv *.vob
SET outDir=OUT

SET videoWidth=720
SET videoHeight=338
SET videoLevel=3.1
SET videoProfile=main
SET videoConstantRateFactor=17

SET audioChannel=2
SET audioCodec=aac
SET audioBitRate=225k
SET audioSampleRate=44.1k

SET audioOptions=-acodec %audioCodec% -ab %audioBitRate% -ar %audioSampleRate% -ac %audioChannel%
SET videoOptions=-s %videoWidth%x%videoHeight% -vcodec libx264 -crf %videoConstantRateFactor% -profile:v %videoProfile% -level %videoLevel%
SET cropOptions1=-vf crop=in_w:in_w*%videoHeight%/%videoWidth%
SET cropOptions2=-vf crop=in_h*%videoWidth%/%videoHeight%:in_h

PATH=%PATH%;%~dp0
PUSHD %CD%

IF [%1]==[] (
	SET videoPath=.
) ELSE (
	SET videoPath=%1
)

IF EXIST %videoPath%\NUL (
	CD /d %videoPath%
	FOR %%I IN (%inTypes%) DO CALL :MakeVideo "%%~nxI"
) ELSE (
	CD /d %~dp1
	CALL :MakeVideo "%~nx1"
)

POPD
EXIT /b 0

:MakeVideo
	SET subtitleOptions=
	SET subtitle=%~n1.ass
	IF NOT EXIST "%subtitle%" SET subtitle=%~n1.srt
	IF NOT EXIST "%subtitle%" GOTO :endSubtitle
		SET charsetOption=
		CALL :DetectCharset "%subtitle%"
		IF NOT ERRORLEVEL 1 SET charsetOption=:charenc=GB18030
		SET subtitleOptions=",subtitles=%subtitle%:original_size=%videoWidth%x%videoHeight%%charsetOption%"
		ECHO.
		ECHO Subtitle: %subtitle%
		ECHO Charset: %detectedCharset%
	:endSubtitle

	IF NOT EXIST %outDir% MD %outDir%
	@ECHO ON
	ffmpeg -i %1 -y %audioOptions% %videoOptions% %cropOptions1%%subtitleOptions% "%outDir%\%~n1.mp4"
	IF %ERRORLEVEL% EQU 1 ffmpeg -i %1 -y %audioOptions% %videoOptions% %cropOptions2%%subtitleOptions% "%outDir%\%~n1.mp4"
	@ECHO OFF
EXIT /b 0

:DetectCharset
	SET hexFile=%~n1.hex
	CERTUTIL -f -encodehex %1 "%hexFile%" >NUL
	FOR /f "usebackq delims=" %%E IN ("%hexFile%") DO (
		SET "firstLine=%%E" >NUL
		GOTO :endFor
	)
	:endFor
	DEL /Q /F "%hexFile%" >NUL 2>&1

	ECHO %firstLine% | FIND "ef bb bf"     >NUL && SET "detectedCharset=UTF-8"     && EXIT /b 1
	ECHO %firstLine% | FIND "ff fe 00 00"  >NUL && SET "detectedCharset=UTF-32 LE" && EXIT /b 5
	ECHO %firstLine% | FIND "ff fe"        >NUL && SET "detectedCharset=UTF-16"    && EXIT /b 2
	ECHO %firstLine% | FIND "fe ff 00"     >NUL && SET "detectedCharset=UTF-16 BE" && EXIT /b 3
	ECHO %firstLine% | FIND "00 00 fe ff"  >NUL && SET "detectedCharset=UTF-32 BE" && EXIT /b 4
	SET "detectedCharset=ASCII"
EXIT /b 0

仅记录

]]>
LibAhead for iOS - 在未越狱设备上修改三方APP的功能 2015-01-31T03:46:31+00:00 Yonsm http://yonsm.net/libahead 折腾了两个晚上,已搞定在未越狱 iOS 上向第三方 APP 注入 dylib 模块的方案!

通过这个方案,使“微信去广告显IP删弧宠 for iOS ”成为可能(戏谑,但实现起来确实将比 Win32 下简单容易很多)。目前已实测在微信 iPhone 版中添加代码,强制开启“羊年春晚摇一摇”功能,不仅可以抢先体验提前穿越,还能随意控制原本随机摇出的各种功能(剧透:过年的时候微信摇一摇会有:新春红包、上传全家福到春晚、明信片、明星拜年、播放音乐、“甜蜜时光”、啥也没摇到、春晚节目单等功能:)。

思路和十多年前的 AheadLib for Win32 创意方案一样。决定和 AheadLib 一样,做个自动化生成代码的工具,名字都想好了,LibAhead for iOS。宇宙依然,但青年不再,所以拖拖拉拉是难免的,哈

针对特定 APP 写的 CydiaSubstrate dylib 模块,稍加处理即可集成到第三方 APP 里,并运行于未越狱的设备中。各种系统权限突破之类的还是老实点——干不了的;因为涉及修改 IPA 包,重新签名也是必须的。

后续再把细节和工具慢慢补上……

AppStore 之外(如蒲公英、同步推、快用、PP助手等)下载的 APP 都可能不是安全的——即使是未越狱的手机,基于以上功能完全可以在官方 APP 基础上做各种拦截和功能扩展。

2015.02.06 更新:扯淡了,根本不需要转发,直接修改 Mach-O Load Command 表,插入 dylib 然后重新签名即可搞定一切,自动化工具已完成,未广泛测试:https://github.com/Yonsm/iPAFine

]]>
分析 Substrate 的 THUMB 函数 Hook 实现细节 2014-07-12T00:14:31+00:00 Yonsm http://yonsm.net/armhook ARM 架构的 CPU 有 ARM 和 THUMB 执行态。

####1. 先说 ARM 态(被Hook的函数)到 ARM 态(自己的替换函数)的 HOOK

非常简单,没有看过 Substrate 的时候我就想到并验证过了(8 个字节):

  LDR PC, [PC, #-4]
  replacedFunctionAddress  ; 目标绝对地址(ARM 态的,偶数)

####2. ARM 态到 THUMB 态的 HOOK

和上面应该应该类似,只是 变成 replacedFunctionAddress + 1,转跳后自动切换到 THUMB 态。

  未验证(X!)

####3. THUMB 到 THUMB 态的 HOOK

难理解的来了,经过实际测试发现下面的代码可以 HOOK 任意的函数(包括未导出的私有函数)(注意,hookedFunctionAddress 如果是 THUMB 的,则需要 + 1——MD,在这栽了好长一段时间,感谢曾半仙)

  _MSHookFunction(hookedFunctionAddress + 1, (void *)replacedFunctionAddress, (void **)&pOriginalFunction); 

用以上 Substrate 的方法实现 THUMB 到 THUMB 的 HOOK 之后,我用 GDB 查看了一下内存,总共修改了12个字节,如下:

  (gdb) x/3xw _mh_execute_header+0x1073E0
  0x15e3e0 <_mh_execute_header+1078240>:	0x46c04778	0xe51ff004	0x0029b6b9  

反汇编代码(注意 THUMB 模式的 disas 地址要 +1 变成奇数):

  (gdb) disas _mh_execute_header+0x1073E1 _mh_execute_header+0x1073EC
  Dump of assembler code from 0x15e3e1 to 0x15e3ec:
  0x0015e3e1 <_mh_execute_header+1078241>:	bx	pc
  0x0015e3e3 <_mh_execute_header+1078243>:	nop			(mov r8, r8) 
  0x0015e3e5 <_mh_execute_header+1078245>:	blx	0x562e24 ; 请忽略
  0x0015e3e9 <_mh_execute_header+1078249>:	undefined ; 请忽略
  0x0015e3eb <_mh_execute_header+1078251>:	lsls	r1, r5, #0 ; 请忽略

第一条指令(C0 46)就是 THUMB 的 BX PC,第二条指令是 78 47 是 THUMB 的 NOP。后面的指令因为实际上是 BX 成 ARM 态了,所以请忽略。

BX PC 后实际上是转跳到了 ARM 态的 0x0015e3e4 地址,继续反汇编如下:

  (gdb) disas _mh_execute_header+0x1073E4 _mh_execute_header+0x1073EC
  Dump of assembler code from 0x15e3e4 to 0x15e3ec:
  0x0015e3e4 <_mh_execute_header+1078244>:	ldr	pc, [pc, #-4]	; 0x15e3e8 <_mh_execute_header+1078248>
  0x0015e3e8 <_mh_execute_header+1078248>:	strheq	r11, [r9], -r9 ; 这个就是和 replacedFunctionAddress + 1 了

可以看到从 0x0015e3e4 这里开始和上面提到的第一种情况(从ARM到THUMB)一样了。

补充:感谢 riusksk 做了一个直观图解,非常容易看明白:

####4. 从 THUMB 到 ARM

  你猜~~

####关于转跳

  * 如果操作数类型是imm, 那就是互换状态. ARM下到thumb, thumb下调用就到ARM;
  * 如果操作数是寄存器 根据低位地址,奇数为 THUMB,偶数为 ARM。

知道了上述 HOOK 方法,再构造一个 pOriginalFunction,结合 Inject Dylib 的方法,就可以自己实现 Substrate 的完整功能了。

(上面仅 3 是对 Substate 的 Hook 分析,其它是我 YY 的,不确定 Substrate 也是这样的实现,有兴趣的话可以自己反汇编/反编译 Substrate 去看实现细节)

]]>