一个人的闲言碎语

dr0v

blog.drov.com.cn
一个人碎碎念。
About Me
A lazy security employee.

2021年12月9日星期四


# Codeql for mac 

关键问题:

1. 安装codeql-cli

官方答案是 [codeql-cli-binaries/releases](https://github.com/github/codeql-cli-binaries/releases) 上下载。

我比较懒,所以直接 ```brew install codeql``` . 感谢官方的努力,使得懒人安装成为可能。


2. 报错  *ERROR: Referenced pack 'codeql-cpp' not found.*

这是让我头疼了两天的问题,基本 google 不到相关解决方案(完全是因为大家都比较老实,按照官方方案安装)。

原因是 ql 的依赖库 [ql](https://github.com/Semmle/ql) 找不到,这个在官方的安装方案中,是需要与 codeql 放在同一目录的。

所以,brew install 后,如果要达到同样的效果,需要在 
```
/opt/homebrew/Caskroom/codeql/2.7.2/
``` 目录,
```
git clone https://github.com/Semmle/ql
```

3. 依然会报错 *Error: Connection is disposed.*

原因不知道,在 VSCode 的 setting.json 中,设置路径可解除报错,感觉是 codeql 自己无法自动识别库路径——即使你给它放在了同一目录下。
```
"codeQL.cli.executablePath": "/opt/homebrew/Caskroom/codeql/2.7.2/codeql/codeql"
```
Categories:

2021年9月23日星期四


# Mac With Apple M1 安装 airtest 失败解决方案

项目需要,需要利用 airtest 进行一些Android 模拟器的脚本测试,所以需要 pip install airtest. (关键的其实是 pocoui ,最终追踪下来,安装这个模块时出错的。)

然后就理所当然的出错了:

```
pip install airtest

.......

Error: Failed building wheel for numpy
```

网上找了各种问题原因,归结如下:

1. python 版本过高导致,最好使用稳定的3.8
2. python 版本过高导致,最好使用稳定的3.6
3. wheel 有问题,重装 wheel

按照1 的方案,安装了 python 3.8。 在 mac 下切换版本(homebrew 安装的环境下):

```
arch -arm64 brew install python@3.8

brew unlink python@3.9 && brew link --overwrite python@3.8

```

此时,pip3 就是 ``` python3.8 -m pip ``` 了。

但,安装依然报同样的错。

然后参考方案2,准备安装 python3.6 / python 3.7 。 结果,系统不支持。

```
arch -arm64 pip3 install python@3.7                                                                                    
python@3.7: The x86_64 architecture is required for this software.
Error: python@3.7: An unsatisfied requirement failed this build.
```

game over .  (由此可以看出,明显是版本问题导致无法正常 pip 安装模块,跟 wheel 也就没啥关系了)

## 峰回路转的'绿色版'解决方案

这下没办法了,所以思考了一下,装个 airtestIDE 先手工一下,然后脚本处理好了,再同步到服务器进行调试。

安装 airtestIDE 过程省略。

发现 airtestIDE 使用一切正常,那是不是他的依赖都是 OK 的、预编译好了的?python 能不能直接用他的模块呢?

于是,打开包内容 -> Contents -> MacOS ,发现真有关键的 poco 模块,于是,将其中关键的 poco 、 airtest、airtest_selenium 模块都直接复制到 python 的 site-packages 中。

再运行,补充一些需要的模块,大功告成!

```
get free device number: 3
deviceList: ['emulator-5554-0', 'emulator-5554-1', 'emulator-5554-2']
[pkg:]:getEmu cnt: 1 left_time:120
[pkg:]:error adb:0 out :List of devices attached

    ... ...    
    ... ...    
    ... ...    

```
Categories: , ,

2021年7月8日星期四


## finder显示完整路径

使用终端命令行:
```
defaults write com.apple.finder _FXShowPosixPathInTitle -bool TRUE;killall Finder
```

## 右键 Open With VSCode
```
1. 点击Dock => 其他 => 自动操作
2. 点击新建文稿
3. 点服务,点击选取

6、在左侧面板选择“实用工具”;然后找到”运行Shell脚本“,把它拽到右侧面板里;
在右侧“服务”收到选定选择文件夹,位置Finder(访达);“运行Shell脚本”的面板里,选择Shell”/bin/bash“,传递输入“作为自变量”,然后修改Shell脚本

复制以下内容:
for f in "$@"
do
    open -a "Visual Studio Code" "$f"
done

7、之后,保存cmd+s,保存为Open With VSCode

8、选择电脑上任意一个文件夹,点击右键“服务”/"快速操作",就可以看到“Open With VSCode”菜单
```


## 偶现终端中文显示乱码

1. 运行```locale```命令查看本地编码方式
2. ```vi .zshrc```修改默认编码格式,添加如下内容

```
export LC_ALL="zh_CN.UTF-8"
export LANG="zh_CN.UTF-8"
```

3. ```source .zshrc```生效修改



## 错误: Cannot install under Rosetta 2 in ARM default prefix (/opt/homebrew)!
安裝 python3.8

一般正常都是执行 brew 安装即可,但最近在 M1 上却初现了如下错误:

```
brew install python@3.8
Error: Cannot install under Rosetta 2 in ARM default prefix (/opt/homebrew)!
To rerun under ARM use:
    arch -arm64 brew install ...
To install under x86_64, install Homebrew into /usr/local.
```


直接按照提示执行如下命令即可:

```
arch -arm64 brew install python@3.8
```

然后有需求切换默认的 python 版本则:

```
brew link --overwrite python@3.8

```
Categories: , , , ,

2021年6月10日星期四

# Apple M1电脑装 Android 模拟器(AVD)
> 新版 mbp or mba 使用 M1芯片,暂时还不支持虚拟化,所以很多模拟器都不可用了,虽然 google 释出了android-emulator-m1-preview,但还存在很多 bug,麻烦。网上看到一个通过 Android Studio下载 arm64版本的 AVD 的可行方案,记录一下。

## 具体步骤

1. 更新你的 Android Studio 到最新版本。

2. Android Studio 右上角打开 Android Virtual Device Manager - 点选 Create Vitrual Device。

    **重点在选择System Image的时候,选 Other Images 的标签。ABI 显示支持的芯片是 arm64-v8a。**
![](https://miro.medium.com/max/700/1*hm967b0pOG-EwOYj8zzYVw.png)

3. 下载完成,启动时会遇到:The emulator process for AVD was killed。

4. 打开Terminal,执行: ~/Library/Android/sdk/emulator/darwin-aarch64-replace.sh
```
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   651  100   651    0     0    649      0  0:00:01  0:00:01 --:--:--   649
100 91.8M  100 91.8M    0     0  7862k      0  0:00:11  0:00:11 --:--:-- 10.9M
unzip emulator-darwin-aarch64-0.2.zip
Archive:  emulator-darwin-aarch64-0.2.zip
   creating: emulator-darwin-aarch64-0.2/
  inflating: emulator-darwin-aarch64-0.2/NOTICE.csv
  ...
  inflating: emulator-darwin-aarch64-0.2/lib/ui_controller_service.proto  
  inflating: emulator-darwin-aarch64-0.2/emulator  
rm: ./lib64/qt/libexec/qtwebengine_resources.pak: No such file or directory

```
5. 此时,需要手动补充文件,Terminal 执行下方命令打开目录: open ~/Library/Android/sdk/emulator/  
6. 将 emulator-darwin-aarch64-0.2 目录中的所有文件移动到上一级,覆盖。
7. 打开AVD面板,启动模拟器,即可正常使用。

## 参考
1. [3分钟搞定Apple M1电脑使用Android模拟器](https://zhuanlan.zhihu.com/p/372158270)
Categories: , ,

2021年5月14日星期五

# 蜜罐与蜜罐识别

## 蜜罐概述

>Honeypots are any security resource whose value lies in being probed, attacked, or compromised. 

蜜罐是⼀种安全威胁的主动防御技术,它通过模拟⼀个或多个易受攻击的主机或服务来吸引攻击 者,捕获攻击流量与样本,发现⽹络威胁、提取威胁特征,蜜罐的价值在于被探测、攻陷。其在 本质上来说,是⼀个与攻击者进⾏攻防博弈的过程。蜜罐提供服务,攻击者提供访问,通过蜜罐 对攻击者的吸引,攻击者对蜜罐进⾏攻击,在攻击的过程中,有经验的攻击者也可能识别出⽬标 是⼀个蜜罐。

蜜罐的基本功能:

1. ⽹络欺骗。 使⼊侵者相信存在有价值的、可利⽤的安全弱点,蜜罐的价值就是在其被探测、
攻击或者攻陷的时候得以体现,⽹络欺骗技术是蜜罐技术体系中最为关键的核⼼技术,常⻅的有模拟服务端⼝、模拟系统漏洞和应⽤服务、流量仿真等。
2. 数据捕获。 ⼀般分为三层实现,最外层由防⽕墙来对出⼊蜜罐系统的⽹络连接进⾏⽇志记
录;中间层由⼊侵检测系统来完成,抓取蜜罐系统内所有的⽹络包;最⾥层的由蜜罐主机来完成,捕获蜜罐主机的所有系统⽇志、⽤户击键序列和屏幕显示。
3. 数据分析。 要从⼤量的⽹络数据中提取出攻击⾏为的特征和模型是相当困难的,数据分析是
蜜罐技术中的难点,主要包括⽹络协议分析、⽹络⾏为分析、攻击特征分析和⼊侵报警等。数据分析对捕获的各种攻击数据进⾏融合与挖掘,分析⿊客的⼯具、策略及动机,提取未知攻击的特征,或为管理者提供实时信息。
4. 数据控制。 数据控制是蜜罐的核⼼功能之⼀,⽤于保障蜜罐⾃身的安全。蜜罐作为⽹络攻击
者的攻击⽬标,若被攻破将得不到任何有价值的信息,还可能被⼊侵者利⽤作为攻击其他系统的跳
板。虽然允许所有对蜜罐的访问,但却要对从蜜罐外出的⽹络连接进⾏控制,使其不会成为⼊侵者的跳板危害其他系统。

![蜜罐基本体系架构图](https://c4pr1c3.github.io/cuc-ns/chap0x11/attach/images/image003.png)

## 蜜罐关键技术

### ⽹络欺骗技术

其设计⽬标是为了让⽹络攻击者产⽣攻击或⼊侵蜜罐的兴趣。典型技术如:蜜罐主机、陷阱⽹络、诱导和欺骗信息设计。

- 蜜罐主机
    
    主要分为:空系统,⽆业务模拟的真实完整操作系统及应⽤程序。镜像系统,对⽣产业务进⾏镜像模拟。虚拟系统,基于虚拟机软件不包含真实业务和业务数据的镜像系统。
- 陷阱⽹络

    由多个蜜罐主机、路由器、防⽕墙、IDS、审计系统等组成的供攻击者⼊侵的⽹络。⽬前的陷阱⽹络已经进化到主要通过虚拟化和云计算来实现,不再需要⼤量真实物理主机和⽹络设备。
- 诱导
    
    主要指的是基于地址转换技术和基于代理技术将蜜罐主机隐藏在⼀个受控隔离内⽹,引诱攻击者主动攻击,从⽽可以在⽹关或代理服务器上进⾏便利的数据捕获。
- 欺骗信息设计

    例如:端⼝扫描欺骗设计、主机操作系统信息欺骗设计、后⻔欺骗信息设计、Web 扫描欺骗信息设计和⼝令欺骗信息设计。

### 数据控制

之所以需要数据控制,是因为在蜜罐捕获到(恶意)访问⾏为时要么是⾃动响应要么是需要⼈⼯⼲预。⽆论是哪种⽅式,都可能存在联动操作的延时,这就给了攻击者时间和机会彻底攻陷蜜罐系统,从⽽以被攻陷的蜜罐为跳板攻击真实主机和⽹络。

因此在设计蜜罐的数据控制机制时,通常⾄少需要设计两层数据控制。第⼀,这是符合纵深防御原则的,充分考虑了单⼀数据控制失败的情况,增加⾃动发现并阻⽌提权和破坏性强攻击(⼊侵)⾏为的机会。第⼆,可以设置多重伪装,尽可能避免被攻击者察觉他在攻击的是⼀个蜜罐系统。

### 数据捕获

蜜罐相⽐较于⼊侵检测既有继承,同时⼜有⼀些特别的数据捕获来源。

- 系统层⾯,蜜罐可以实现键盘捕获、屏幕记录和进程访问历史记录。
- ⽹络层⾯,蜜罐⽀持攻击图(路径)的重建。
- 数据层⾯,蜜罐⽀持基于蜜信的传播路径重构。

为了保证捕获数据的安全性,通常不能在蜜罐系统本地存储捕获的数据,必需采⽤远程存储⽅式。

### 数据分析

⼊侵检测数据分析算法都可以⽤于蜜罐中的数据分析,例如基于异常的算法:基于特征选择异常检测、基于⻉叶斯推理异常检测、基于模式预测异常检测和基于神经⽹络异常检测。但是在实践中,蜜罐和⼊侵检测对于数据分析的应⽤还是存在⼀些差异的,如下表格所示:

||蜜罐|入侵检测
-- |--|--
目的|还原入侵|发现入侵
手段|异常检测|误用检测、异常检测

⼊侵检测对于数据分析的应⽤主要专注于对于⼊侵⾏为的发现,识别⼊侵类型及⼊侵来源;⽽蜜罐则通过对⼊侵⾏为的精确还原,在发现⼊侵⾏为的基础上进⼀步分析出攻击意图、攻击者身份,并进⾏⻛险影响的评估。


## 常见蜜罐产品

蜜罐可以分为数据库蜜罐、web蜜罐、服务蜜罐、工控蜜罐及端点蜜罐,各种蜜罐对应的相关产品如下图所示:

![](https://cdn.nlark.com/yuque/0/2021/png/735646/1611653397560-58328fe6-8381-48fe-8a96-a5ae0ef94998.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_10%2Ctext_VGlkZeWuieWFqOWboumYnw%3D%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10)

## 开源蜜罐识别

互联网中的低交互蜜罐大部分为开源蜜罐。由于其特有的开放特性,人们能够对其特征进行识别和规避。

### 协议的返回特征

部分开源蜜罐在模拟协议时,在响应中可能未能实现完全的随机,导致遗留了明显的特征可供反蜜罐人员用于识别(e.g. version/libevent/rusage_user )

![协议响应特征的蜜罐](https://mmbiz.qpic.cn/sz_mmbiz_png/R2O64T36XGvr5phyGbLPtTlicEC8JymOGhTGzZbGLxEnNkXIZibrm0WGOSFhqS8QvTgz52jwHRDq9uR5n8MRU10w/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1)

### 协议实现的缺陷
在部分开源的蜜罐中模拟实现部分协议并不完善,可以通过发送一些特定的请求包获得的响应来判断是否为蜜罐。

- ssh协议: 协商版本号阶段,由于开源蜜罐只支持固定版本,当遇到不支持版本时会报错。
- Mysql协议: 抓取读取的文件名
- telnet协议: 在命令为空或者直接回车换行时,会响应default模板,该模板内容为test。

### 明显的WEB的特征
部分开源蜜罐提供了web服务,这些web服务中常常会带有一些明显的特征,可以根据这些特征来检测蜜罐。如特定的js文件、build_hash或者版本号等。

- HFish: 在默认8080端口实现了一个WordPress登录页面,页面中由一个名为x.js的javascript文件用来记录尝试爆破的登录名密码。直接通过判断wordpress登录页是否存在x.js文件就可判断是否为蜜罐。
- glastopf: 可以通过页面最下方的blog comments的输入框进行识别。

![具有明显WEB特征的蜜罐](https://mmbiz.qpic.cn/sz_mmbiz_png/R2O64T36XGvr5phyGbLPtTlicEC8JymOGTDBAd8jWNqyX2Hfib3mrklBB2sAxzom8e5ibUv4POgDnZyEhzibkTxLpQ/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1)

### 上下文特征
部分开源蜜罐存在命令执行上下文明显的特征,
- Cowrie: a. 设备名称为localhost; b. 设备中所有进程启动于6月22日或6月23日; c. 存在用户名richard。
- 最新版的Cowrie: 在默认配置下一些一些命令得到的结果是固定不变的。如:cat /proc/meminfo 这个命令无论执行多少次得到的内容都是不变的,而这真实的系统中是不可能的。
- HFish: 和telnet协议一样SSH协议在直接进行回车换行时会默认执行default输出test

### Fuzz testing 特征
部分蜜罐借用Fuzz testing的思想实现了蜜罐系统,因此会有如下特征:

- 响应任意端口的TCP SYN Packet。
- 根据协议特征,永远返回正确的响应。
- 返回预定义或者随机的Payload特征库集合。

该类蜜罐可以通过跨服务的特征进行判断,如开放了HTTP服务同时响应了upnp协议,或者根据server的长度或者个数来判断。

## 工控蜜罐识别

### IP地址识别
一般工控设备如plc、dcs、rtu等很少直接暴露到互联网,在业务需要情况下可能会将工控设备直接暴露在互联网,比如设备远程维护等。

通过查询IP地址对应的ISP,来判断所述IP是否属于云服务器提供商的IP地址。当所述IP属于云服务器,并且开放了PLC的工控协议服务如modbus、s7、ethernetip等,则可判定IP为工控蜜罐。

### 操作系统及MAC厂商指纹识别

一般工控设备均为嵌入式设备,如plc、dcs、rtu大多使用实时操作系统如vxworks、QNX,HMI设备一般使用wincc操作系统等;通过TCP/IP操作系统指纹识别,获取目标IP的TCP/IP协议栈指纹,当目标IP的操作系统被识别为Linux的非嵌入式操作系统。并且该设备未经过路由转发与映射,通常可认为是蜜罐系统。

一般工控蜜罐,如服务蜜罐大多部署在linux vmware及docker容器里面,而仿真服务部署在windows系统上面。

### 指纹特征识别

- Openplc modbus蜜罐服务指纹特征识别

    安装部署Openplc,并且阅读源码如下,分析蜜罐特征,可以看出openplc只实现了1-16功能码,而重要的设备信息功能码如报告从设备信息17功能码、获取设备信息43功能、施耐德获取cpu、工程等信息90私有功能码均未实现。

    可以构造17、43、90功能码去请求openplc,获取设备信息,均会返回非法功能码指纹

- openplc http协议指纹识别

    openplc默认开放了http 8080端口,浏览器访问http://ip:8080 查看plc管理页面与真实工控plc设备不一样,并提取http指纹特征。

- Conpot S7 协议 蜜罐服务指纹特征识别

    阅读conpot s7comm源码,找到S7服务蜜罐通用特征:"Original Siemens Equipment","88111222","IM151-8 PN/DP CPU"

    特征2:最后6个字节始终为 “\x00\x00\x00\x00\x00\x00”

- Conpot ENIP蜜罐服务指纹特征识别

    阅读conpot enip蜜罐服务代码,发现向设备发送63功能码获取设备信息,返回的数据device_status== \x60\x31 和device_state == \xff

- 其它工控蜜罐

    其它蜜罐及仿真服务程序包括,conpot、openplc、CryPLH2、仿真程序包括modbus tester、Mod Rssim、snap7、opendnp3、qtester104、DNP3_testhaness、EtherNetIP Virtual等。

## 蜜罐反识别方法

通过对蜜罐部署方式及特征分析,如果我们部署蜜罐服务时候,可以考虑采用以下方法进行规避。

1. 如果蜜罐服务要部署在外网,尽量避免使用云服务厂商提供的服务。
2. 如果蜜罐服务部署在企业内网,尽量在蜜罐服务或者仿真程序前面部署一个vxworks系统的设备,并在上面开启端口转发服务,或者利用Qmenu模拟运行vxworks操作系统,使攻击者误以为扫描到的工控设备使用的是vxworks实时操作系统。
3. 定期修改蜜罐内部特征,如conpot蜜罐模板信息,conpot源码中返回报文的协议特征。同一个ip蜜罐节点,尽量只开放如真实plc设备的端口和服务,如502、80、161等端口,避免开放过多及无效的应用端口,被攻击者快速识别蜜罐。
4. 修改宿主机mac地址,尽可能修改成西门子、GE等工控设备的MAC地址,增加攻击者识别蜜罐的难度。
5. 服务蜜罐尽可能实现协议规约必须的功能码,如施耐德plc的17、42、90信息获取功能码任何一个。
6. 尽可能实现高交付服务蜜罐,也可以采用虚实结合部署方式进行部署,增加蜜罐识别难度。

## 参考
1. [Tide安全团队/工控安全Wiki/蜜罐技术](https://www.yuque.com/tidesec/ics/a4f46ba681b34d017fbed21398cb2517)
2. [浅析开源蜜罐识别与全网测绘](https://www.secrss.com/articles/28577)
3. [一种工控蜜罐识别与反识别技术研究与应用实践](https://www.freebuf.com/articles/ics-articles/230402.html)

2021年5月10日星期一

## 1. 不被允许的“姨妈”

3月31日,新浪微博的文学博主“亚非文学 bot”发布退博声明。2 月底某男星粉丝举报AO3平台事件引发的舆论海啸裹挟了众多非传统饭圈的网络群体,被网络舆论戏称为“227 大团结”,余波未止,而此次退出微博的亚非文学 bot 与后续为声援也退博的中东欧诗歌bot则再一次将该粉丝群体推向风口浪尖。亚非文学 bot 为十几位小语种文学的学生运营,整理并翻译了大量文学作品和相关研究,尽管此前确实曾动过退博的念头,但导火索依然是粉丝的攻击。


退博数日前,bot曾转发了含该男星“泥塑黑称”的账号投稿一一“赞姨娇俏bot'ーー并在其粉丝质问后“违规”地回敬了一句人格化的嘲讽,“天呐,我以上帝的名义发誓,失去了您的关注实在是太让我心痛了,”点燃了粉丝们的怒火,对bot进行辱骂与人肉威胁。

“泥塑”是饭圈文化中的一种特殊现象,广义上的“泥塑”指放大女艺人的男性气质或反之,而狭义的也是更为粉丝群体所默认的“泥塑”指女化。因为跨性别的处理,泥塑的支持者往往热衷于鼓吹泥塑体现了先进的女权主义思想,以及更高级的足以欣赏“矛盾美学”的审美品味,通过泥塑,这些粉丝们建构起了“先锋者”的自我形象,泥塑此时成为一种暗含优越性的文化资本,泥塑得越彻底、越夸张意味着越具有智识上的优越。尽管泥塑粉倡导性别价值多元、美学表达多元,但对泥塑对象的普遍口味和要求还是“年轻的美女”,因为“漂亮”才有资格当“女的”,不是任何人都配被泥塑。当不同饭圈的泥塑粉发生矛盾时,对对方的攻击离不开对其偶像的侮辱,对偶像的攻击时常从外貌开始,即剥夺他成为“美女”的权利。

“赞姨”不仅是对自己以俊朗外貌闻名的偶像进行泥塑,而且是“调侃”甚至“侮辱”意味的泥塑,为其粉丝所不能容忍。

上世纪六七十年代,福柯在《疯癫与文明》《规训与惩罚》等著作中提出“凝视理论”,将“凝视”解读为一种权力关系,其中凝视者为主体,被凝视者则是被压抑的客体。1975 年女性主义电影评论家 Laura Mulvey 由此发展,提出男性凝视(male gaze)的概念,女性作为被观看的对象受制于男性的审美和背后强大的男权势力。泥塑文本中常见的“女儿”“姐姐”甚至是“小妈”都是梦幻的、浪漫主义的,这些女性与现实的牵绊无关而仅仅是幻想的造物,相比之下,“姨化”所指涉的“姨母”“大妈”这样容貌衰老且让人联想到琐碎的日常生活的女性,显然为泥塑所不容,“姨”的出现无情击破了粉丝的梦境

与少女审美相伴相生的是女性普遍对外貌与年龄的焦虑,这种焦虑也渗透进了自诩先进的泥塑之中。从这一角度来说,泥塑和造梦自慰的玛丽苏极为密切。我们很难看到有粉丝会去泥塑一个年长的男演员,也几乎不会在泥塑文本中看到“奶奶”“外婆”这样的主角,在“美女”的世界里,这些年长的不再能取悦男性性欲的女性是不仅被静音的对象,还是需要被反驳和驱逐的污名。

然而在泥塑的矛盾心态背后,是什么支撑起粉丝对其他群体与个人不断的侵犯和攻讦,在他们自诩正义和苦情的内部叙事之后,其狂热心态和扩张举动之下的核心逻辑为何,依然是我们要关心的问题。

## (2)双向造神:“我和他/她都知道,但是,但是”

粉丝们的狂热或许可以在他们塑造的明星形象中找到答案。许多粉丝群体都热衷于将自家爱豆想象成“美强惨”。一方面,他颜值不凡,才华出众;但另一方面,如此优秀的爱豆又处在一个被资本和恶意包围的”无情世界”之中:被资本方摆布,被经纪人操弄,更常被其他的群体中伤。由此,明星的形象酷似宗教叙事中的受难者——德行崇高,并无罪过,却在污浊的尘世中受尽苦难。

而粉丝们往往以追随受难者的殉道者自居。在明星面前,他们自觉渺小,“在现实中,我都不配给这样的美人花钱”是他们常挂嘴边的口头禅。他们唯一能做的就是扮演“无情世界的感情”,帮助爱豆在残酷的商业逻辑下取得成功。当下的粉丝们极少对文化工业的压迫与收编懵然不知,大颇为矛盾的是,他们利用这份清醒加剧了自己的沉沦,而这正是殉道的意义所在——纵然清楚商业资本掌控的无情世界将吞噬普通的消费者粉丝,粉丝们仍愿飞蛾扑火,为爱豆付出自己满溢的情感,更不吝惜自己的金钱,自我感动于殉道者的意象。

这种“受难者”和“殉道者”的叙事之所以在如今的粉丝心理中大行其道,年轻一代原子化的,孤独的生活状态是不可忽视的因素。在精神分析理论中,个人的孤独、渺小一直是一个重要的主题。按照精神分析的理论,在人的成长过程中存在一个所谓的“俄狄浦斯时刻”。在俄狄浦斯时刻前的口腔期,当一个孩子最初吮吸母亲的乳房时,他感到自己的全部欲望(吮吸)都能得到外界的回应和满足。然而,在成长的过程中,他们终将发现外界是一个“异己”的存在,并非所有的欲望都可以被满足,相反,有时尽管不愿,但欲望必须服从于外界的规则。由此,“孤独”和“无力”的感受一直伴随孩子的成长,这种孤独包括而不仅仅是人际关系意义上的孤独,更是外在世界带来的异己感和面对异己世界的无力感。

而今天,作为追星主要群体的年轻一代比他们的长辈更加孤独。中年人或许已有了家庭,拥有稳定的人际关系。在他们的成长过程中,街坊、亲戚、邻里的关系也更为深厚,进而多少缓解了外部世界的“异己”感受。但对于“原子化”的年轻一代来说,追星往往成为了令这种“孤独感”爆发的窗口。

因此,“受难者”和“殉道者”的叙事就成为了孤独感投射的结果。粉丝们之所以认为爱豆身处“无情世界”,是因为他们自己就被异己感和无力感环绕。而当他们看到自己的投入使爱豆一步步在无情世界中杀出一条血路时,他们感到自己的欲望得到了满足,努力得到了回应,异己感和无力感被消解,这也就成为了粉丝们自豪感和成就感的重要来源。而当粉丝们痴迷于这样的自豪感,将“殉道者”的叙事当作了超越孤独和异己感的良方,追星过程中的狂热也就不难被理解。

在追星实践中,“受难”-“殉道”叙事带来的狂热在很多情况下都体现为对消费主义的热情。要帮助自己的爱豆在“无情世界”中突破重围,最直接的方法就是通过支持他所代言的商品来为他赢得 “顶级流量”的身份。这一身份不仅意味着诸多现实的利好,更意味着殉道者们成功完成了自己追随受难者的使命。

除此之外,鲍德里亚早已指出,在当今社会,消费的目的很大程度上已不再是商品带来的使用价值,而是作为“符号”的商品——它标定了个人的身份与认同。因此,支持自家爱豆带货,并让自己的购买行为为超话中的其他同道所知,粉丝们方能确证自己“忠实粉丝”的身份,进而享受属于受难者的自豪和成就感。

## (3)焦虑的迁徙:从消费到安利

相比其他符号性的消费,明星消费更显得“没完没了”,粉丝们往往不知疲倦地为自己的爱豆持续投入。这是因为“顶级流量”的身份富于不确定性:“顶流”的头衔永远基于和其他明星的比较,充满竞争和较量。而且只要竞争还在继续,头衔的归属就时刻可能改变。

因此,每一次“明星带货”都是一场充满了不确定的考验。粉丝们每小时都会统计自家爱豆的带货数量,以便确定他体现出了与“顶级流量”相称的带货能力。然而,一次统计最多只能证明在当次统计之前,爱豆保住了顶级流量的地位,未来瞬息万变,这一头衔可以维持多久难以预料。换言之,“顶级流量”的身份只能从过去中得到确认,却不得不面对来自未来的挑战——这种时间上的错置带来了不可能被消除的不确定性。由此,消费成为了齐格蒙特·鲍曼所说的驱魔仪式,成为了粉丝们缓解焦虑的救命稻草。

作为驱魔仪式,消费的功用在于舒缓不确定性带来的焦虑感。并且无论最后的结果怎样,消费的效果都无法被否定:如果销量巨大,那么“我”便成为了消费大军中的一员,如果销量不够理想,在痛苦之余“我”也能以自己已经尽力来自我安慰。简言之,在充满了不确定性的争夺“顶级流量”的残酷游戏中,不断的“消费”是最能有效应对一切焦虑的驱魔手段。

正因为饭圈要求粉丝们不断地进行消费和投入,为自己的爱豆确立顶流身份。由此,不断吸收新的粉丝,获得新的购买力,就成为了亟欲帮助爱豆维持顶流头衔的粉丝们的必然选择。

在饭圈中,旨在赢得新粉丝的“安利”极为重要。甚至有人喊出了“安利是合格粉丝的终生事业”这样的口号。在消费主义的规训下,原本“自娱自乐”的理想已经从根本上不再可能,一旦为自家爱豆赢得“顶级流量”身份的欲望出现在饭圈之中,饭圈的扩张和话语的传播就已成必然。

而在吸收新血的同时,对流量和商业价值的追逐让粉丝们对一些在他们看来“损害明星商业价值的行为”发起了征讨。一个十分典型的例子就是饭圈对于“泥塑”的态度。泥塑在饭圈遭到抵制的一个重要原因就是,将男爱豆女化的“泥塑”行为破坏了爱豆的“苏感”(对女性群体的吸引力),进而影响爱豆商业价值。由此,基于商业价值和“顶级流量”的身份,消费主义为粉丝们划定了敌我界限。

因此,自我身份建构的需求和多方面推动的饭圈滥化使饭圈不断干涉或吞并其他意识形态,演变为一个凶悍的且组织性极强的机器。至于其未来可能的走向,和在更大的领域可能产生的社会影响,都不可被小觑。征讨和扩张,这几乎是饭圈自诞生就已注定的轨迹。

2021年4月20日星期二

# 手机窃听

>华为面试时几个面试官反复提及手机窃听的技术手段问题。
>
>我仅回答了画像圈定问题,没有涉及到潜在技术问题。
>
>所以回来后检索了一下相关信息,才知道原来还有侧信道攻击的事。

常规认为的方式是,APP 申请了麦克风权限,后台静默监听关键字——类似 siri 的响应。

这里闹出过一个全民皆知的笑话——[NEX化身“流氓鉴定器”?升降式摄像头让偷拍无处可藏!](https://www.sohu.com/a/239463633_100206155)

而2019年3·15,《IT 时报》提出了两个技术实现方案,在未授权麦克风权限的情况下,窃听用户谈话关键词。
1. 加速器“窃听”扬声器
2. “浏览器指纹”乱点鸳鸯谱

## 加速器“窃听”扬声器

在网络与分布式系统安全会议(NDSS)上,浙江大学网络空间安全学院任奎团队、加拿大麦吉尔大学、多伦多大学学者团队展示了一项最新的研究成果——智能手机App可在用户不知情、无需系统授权的情况下,利用手机内置加速度传感器采集手机扬声器所发出声音的震动信号,实现对用户语音的窃听。

![](https://n.sinaimg.cn/tech/crawl/245/w550h495/20200315/8134-iquxrui6911223.jpg)

标准的侧信道攻击。

通过加速器感知手机扬声器播放声音时的震动,然后后台算法还原语音,基本可以做到:
- 语音密码识别
- 语音敏感词识别
- 语音还原

但这里技术上**未还原环境音潜在的被窃听风险**。

## “浏览器指纹”乱点鸳鸯谱

这就是我回答里提到的,群体画像精准定位。

这个技术实现难度低,主要窃取和共享在同一局域网内或同一位置区域长期接触的家人、同事、朋友的使用习惯并进行配对和共享推荐,造成的危害相对较小。

![](https://n.sinaimg.cn/tech/crawl/141/w550h391/20200315/f1dc-iquxrui6911552.jpg)

这种应该是普遍的手法,安全可靠,群体画像后广告精准投放,转化会更高。当然也会给人留下被偷听的'幸存者偏差'错觉。

## 参考
1. [原来,手机是这样“窃听”你的](https://tech.sina.com.cn/i/2020-03-15/doc-iimxxstf9273990.shtml)

2021年4月16日星期五

# hook原理
>在面微信的时候被开发的面试官问到了hook原理。我简单介绍了一般是替换函数地址的形式,保障先调用替换函数,再调用原始函数。
>
>面试官的理解是有两种hook方式:一种是地址替换型,一种是代码插入型。
>
>由此可知我对hook的不甚了解,所以专门找了相关资料进行学习。如文为学习总结。

## hook方式

Hook技术无论对安全软件还是恶意软件都是十分关键的一项技术,其本质就是劫持函数调用。但是由于处于Linux 用户态,每个进程都有自己独立的进程空间,所以必须先注入到所要Hook 的进程空间,修改其内存中的进程代码,替换其过程表的符号地址。

APP 劫持三步走:

1. 注入进程
	- ptrace
	- dlopen    
2. hook 目标函数
	- Java Hook
		- Static Field Hook:静态成员hook
		- Method Hook:函数hook
	- Native So Hook
		- GOT Hook:全局偏移表hook
		- SYM Hook:符号表hook
		- Inline Hook:函数内联hook
3. 执行自身代码
	- 获取敏感信息
	- 修改返回值
	- etc.

![基于 ptrace的 hook 工作流程](https://box.kancloud.cn/4a4a6f8a7696648d181d7743f8e9a5f2_646x432.png)

## xposed hook 原理分析

xposed 虽然目前已经不更新了,不过依然是 Android 平台最著名、最广泛使用的 hook 框架。

xposed hook 工作原理:

1. 获取 root 权限
2. 替换/system/bin/app_process
3. app_process在启动过程中会加载XposedBridge.jar,完成对Zygote进程及其创建的Dalvik虚拟机的劫持
4. XposedBridge.jar中会根据用户所编写的 xposed 模块,对对应 classloader 中的 method 进行替换

```
/**
  *
  * 将输入的Class中的Method方法的nativeFunc替换为xposedCallHandler 
  * 
  * @param env JniEnv
  * @param reflectedMethodIndirect 待反射的函数
  * @param declaredClassIndirect 定义的class
  * @param slot 函数偏移量
  * @param additionalInfoIndirect 添加的函数
  * 
  */
void XposedBridge_hookMethodNative(JNIEnv* env, jclass clazz, jobject reflectedMethodIndirect,
            jobject declaredClassIndirect, jint slot, jobject additionalInfoIndirect) {
    // 容错
    if (declaredClassIndirect == NULL || reflectedMethodIndirect == NULL) {
        dvmThrowIllegalArgumentException("method and declaredClass must not be null");
        return;
    }

    // 根据函数的偏移量,从classloader中找到准备替换的函数。
    ClassObject* declaredClass = (ClassObject*) dvmDecodeIndirectRef(dvmThreadSelf(), declaredClassIndirect);
    Method* method = dvmSlotToMethod(declaredClass, slot);
    if (method == NULL) {
        dvmThrowNoSuchMethodError("Could not get internal representation for method");
        return;
    }

    if (isMethodHooked(method)) {
        // already hooked
        return;
    }

    // 保存替换前的数据信息
    XposedHookInfo* hookInfo = (XposedHookInfo*) calloc(1, sizeof(XposedHookInfo));
    memcpy(hookInfo, method, sizeof(hookInfo->originalMethodStruct));
    hookInfo->reflectedMethod = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(reflectedMethodIndirect));
    hookInfo->additionalInfo = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(additionalInfoIndirect));

    // 替换函数方法 , 让nativeFunction指向本地的hookedMethodCallback
    SET_METHOD_FLAG(method, ACC_NATIVE);
    method->nativeFunc = &hookedMethodCallback;
    method->insns = (const u2*) hookInfo;
    method->registersSize = method->insSize;
    method->outsSize = 0;

    if (PTR_gDvmJit != NULL) {
        // reset JIT cache
        char currentValue = *((char*)PTR_gDvmJit + MEMBER_OFFSET_VAR(DvmJitGlobals,codeCacheFull));
        if (currentValue == 0 || currentValue == 1) {
            MEMBER_VAL(PTR_gDvmJit, DvmJitGlobals, codeCacheFull) = true;
        } else {
            ALOGE("Unexpected current value for codeCacheFull: %d", currentValue);
        }
    }
}

```


![app_process](https://img-blog.csdn.net/20150821093617938)

## frida hook 原理
frida代码结构:
```
frida-core: Frida core library intended for static linking into bindings
frida-gum: Low-level code instrumentation library used by frida-core bindings
frida-python: Frida Python bindings
frida-node: Frida Node.js bindings
frida-qml: Frida Qml plugin
frida-swift: Frida Swift bindings
frida-tools: Frida CLI tools
capstone: instruction disammbler
```

frida的工作模式有两种:
- attach模式

    attach到已经存在的进程,核心原理是ptrace修改进程内存,如果进程处于调试状态(traceid不等于0),则attach失败
- spawn模式

    启动一个新的进程并挂起,在启动的同时注入frida代码,适用于在进程启动前的一些hook,如hook RegisterNative等,注入完成后调用resume恢复进程。

frida 的 hook 区分了 art 模式和 dalvik 模式。

### Dalvik hook 实现

frida兼容了低版本的Android, 低于Android 5.0时,采用Dalvik虚拟机,其核心实现在replaceDalvikImplementation函数中。

frida-dalvik-hook 的原理和 xposed 的 hook 原理是一样的,把 java 函数变成 native 函数,然后修改入口信息为自定义函数信息。

![dalvik 虚拟机执行 java 函数过程](https://mabin004.github.io/images/pasted-83.png)

```
//https://android.googlesource.com/platform/dalvik/+/6d874d2bda563ada1034d2b3219b35d800fc6860/vm/oo/Object.h#418
struct Method {   
    ClassObject*    clazz;   /* method所属的类 public、native等*/
    u4              accessFlags; /* 访问标记 */
    u2             methodIndex; //method索引
    //三个size为边界值,对于native函数,这3个size均等于参数列表的size
    u2              registersSize;  /* ins + locals */
    u2              outsSize;
    u2              insSize;
    const char*     name;//函数名称
    /*
     * Method prototype descriptor string (return and argument types)
     */
    DexProto        prototype;
    /* short-form method descriptor string */
    const char*     shorty;
    /*
     * The remaining items are not used for abstract or native methods.
     * (JNI is currently hijacking "insns" as a function pointer, set
     * after the first call.  For internal-native this stays null.)
     */
    /* the actual code */
    const u2*       insns;          /* instructions, in memory-mapped .dex */
    /* cached JNI argument and return-type hints */
    int             jniArgInfo;
    /*
     * Native method ptr; could be actual function or a JNI bridge.  We
     * don't currently discriminate between DalvikBridgeFunc and
     * DalvikNativeFunc; the former takes an argument superset (i.e. two
     * extra args) which will be ignored.  If necessary we can use
     * insns==NULL to detect JNI bridge vs. internal native.
     */
    DalvikBridgeFunc nativeFunc;
    /*
     * Register map data, if available.  This will point into the DEX file
     * if the data was computed during pre-verification, or into the
     * linear alloc area if not.
     */
    const RegisterMap* registerMap;

};

…
…
…

function replaceDalvikImplementation (fn) {
  if (fn === null && dalvikOriginalMethod === null) {
    return;
  }
//备份原来的method,
  if (dalvikOriginalMethod === null) {
    dalvikOriginalMethod = Memory.dup(methodId, DVM_METHOD_SIZE);
    dalvikTargetMethodId = Memory.dup(methodId, DVM_METHOD_SIZE);
  }

  if (fn !== null) {
   //自定的代码
    implementation = implement(f, fn);

    let argsSize = argTypes.reduce((acc, t) => (acc + t.size), 0);
    if (type === INSTANCE_METHOD) {
      argsSize++;
    }
    // 把method变成native函数
    /*
     * make method native (with kAccNative)
     * insSize and registersSize are set to arguments size
     */
    const accessFlags = (Memory.readU32(methodId.add(DVM_METHOD_OFFSET_ACCESS_FLAGS)) | kAccNative) >>> 0;
    const registersSize = argsSize;
    const outsSize = 0;
    const insSize = argsSize;

    Memory.writeU32(methodId.add(DVM_METHOD_OFFSET_ACCESS_FLAGS), accessFlags);
    Memory.writeU16(methodId.add(DVM_METHOD_OFFSET_REGISTERS_SIZE), registersSize);
    Memory.writeU16(methodId.add(DVM_METHOD_OFFSET_OUTS_SIZE), outsSize);
    Memory.writeU16(methodId.add(DVM_METHOD_OFFSET_INS_SIZE), insSize);
    Memory.writeU32(methodId.add(DVM_METHOD_OFFSET_JNI_ARG_INFO), computeDalvikJniArgInfo(methodId));
    //调用dvmUseJNIBridge为这个Method设置一个Bridge,本质上是修改结构体中的nativeFunc为自定义的implementation函数
    api.dvmUseJNIBridge(methodId, implementation);

    patchedMethods.add(f);
  } else {
    patchedMethods.delete(f);

    Memory.copy(methodId, dalvikOriginalMethod, DVM_METHOD_SIZE);
    implementation = null;
  }
}

```

### ART hook实现
frida的ART hook实现也是把java method转为native method, 但ART的运行机制不同于Dalvik, 其实现也较为复杂。

ART虚拟机执行 Java 方法主要有两种模式:
- quick code 模式:执行 arm 汇编指令
- Interpreter 模式:由解释器解释执行 Dalvik 字节码

所以 frida 要将 java method 转为 native method,需要将ARTMethod 结构进行如下修改:
```
patchMethod(methodId, {
  //jnicode入口entry_point_from_jni_改为自定义的代码
  'jniCode': implementation,
  //修改为access_flags_为native
  'accessFlags': (Memory.readU32(methodId.add(artMethodOffset.accessFlags)) | kAccNative | kAccFastNative) >>> 0,
  //art_quick_generic_jni_trampoline函数的地址
  'quickCode': api.artQuickGenericJniTrampoline,
  //artInterpreterToCompiledCodeBridge函数地址
  'interpreterCode': api.artInterpreterToCompiledCodeBridge
});
```


参考链接

1. [动态注入技术(hook技术)](https://www.kancloud.cn/alex_wsc/android/504478)
2. [Xposed源码剖析——概述](https://blog.csdn.net/yzzst/article/details/47659987)
3. [Xposed源码剖析——app_process作用详解](https://blog.csdn.net/yzzst/article/details/47829657)
4. [Xposed源码剖析——Xposed初始化](https://blog.csdn.net/yzzst/article/details/47834077)
5. [Xposed源码剖析——hook具体实现](https://blog.csdn.net/yzzst/article/details/47913867)
6. [Frida源码分析](https://mabin004.github.io/2018/07/31/Mac%E4%B8%8A%E7%BC%96%E8%AF%91Frida/)
Categories: , ,

2021年2月25日星期四

//调用方法
function printStack() {
var Exception= Java.use("java.lang.Exception");
var ins = Exception.$new("Exception");
var straces = ins.getStackTrace();

if (undefined == straces || null == straces)
{
return;
}

console.log("=============================Stack start=======================");
// console.log(JSON.stringify(straces[0]));
for (var i = 0; i < straces.length; i++)
{
var str = " " + JSON.stringify(straces[i].getClassName());//关键变更点,getStackTrace获取的是类,需要取类名信息才能有效打印
console.log(str);
}

console.log("");
console.log("=============================Stack end=======================\r\n");
Exception.$dispose();
}
//以下为使用方法
Java.perform(function () {

const StringBuilder = Java.use('java.lang.StringBuilder');
StringBuilder.toString.implementation = function () {
// send("in StringBuiler");
var result = this.toString();
console.log("=============================Stack Print=======================\r\n");
console.log(result);
return result;
};

}); 

2021年2月22日星期一

 打开后缀为xls的excel文件,alt+f11呼出VBA。

双击ThisWorkbook,新建一个代码。

然后复制粘贴如下爆破代码,点击运行即可。

Public Sub 工作表保护密码破解()
    Const DBLSPACE As String = vbNewLine & vbNewLine
    Const AUTHORS As String = DBLSPACE & vbNewLine & _
        "作者:McCormick   JE McGimpsey "
    Const HEADER As String = "工作表保护密码破解"
    Const VERSION As String = DBLSPACE & "版本 Version 1.1.1"
    Const REPBACK As String = DBLSPACE & ""
    Const ZHENGLI As String = DBLSPACE & ""
    Const ALLCLEAR As String = DBLSPACE & "该工作簿中的工作表密码保护已全部解除!!" & DBLSPACE & "请记得另保存" _
        & DBLSPACE & "注意:不要用在不当地方,要尊重他人的劳动成果!"
    Const MSGNOPWORDS1 As String = "该文件工作表中没有加密"
    Const MSGNOPWORDS2 As String = "该文件工作表中没有加密2"
    Const MSGTAKETIME As String = "解密需花费一定时间,请耐心等候!" & DBLSPACE & "按确定开始破解!"
    Const MSGPWORDFOUND1 As String = "密码重新组合为:" & DBLSPACE & "$$" & DBLSPACE & _
        "如果该文件工作表有不同密码,将搜索下一组密码并修改清除"
    Const MSGPWORDFOUND2 As String = "密码重新组合为:" & DBLSPACE & "$$" & DBLSPACE & _
        "如果该文件工作表有不同密码,将搜索下一组密码并解除"
    Const MSGONLYONE As String = "确保为唯一的?"

    Dim w1 As Worksheet, w2 As Worksheet
    Dim i As Integer, j As Integer, k As Integer, l As Integer
    Dim m As Integer, n As Integer, i1 As Integer, i2 As Integer
    Dim i3 As Integer, i4 As Integer, i5 As Integer, i6 As Integer
    Dim PWord1 As String
    Dim ShTag As Boolean, WinTag As Boolean
    Application.ScreenUpdating = False
    With ActiveWorkbook
        WinTag = .ProtectStructure Or .ProtectWindows
    End With
    ShTag = False
    For Each w1 In Worksheets
        ShTag = ShTag Or w1.ProtectContents
    Next w1
    If Not ShTag And Not WinTag Then
        MsgBox MSGNOPWORDS1, vbInformation, HEADER
        Exit Sub
    End If
    MsgBox MSGTAKETIME, vbInformation, HEADER
    If Not WinTag Then
    Else
    On Error Resume Next
    Do 'dummy do loop
    For i = 65 To 66: For j = 65 To 66: For k = 65 To 66
    For l = 65 To 66: For m = 65 To 66: For i1 = 65 To 66
    For i2 = 65 To 66: For i3 = 65 To 66: For i4 = 65 To 66
    For i5 = 65 To 66: For i6 = 65 To 66: For n = 32 To 126
    With ActiveWorkbook
    .Unprotect Chr(i) & Chr(j) & Chr(k) & _
    Chr(l) & Chr(m) & Chr(i1) & Chr(i2) & _
    Chr(i3) & Chr(i4) & Chr(i5) & Chr(i6) & Chr(n)
    If .ProtectStructure = False And _
    .ProtectWindows = False Then
    PWord1 = Chr(i) & Chr(j) & Chr(k) & Chr(l) & _
    Chr(m) & Chr(i1) & Chr(i2) & Chr(i3) & _
    Chr(i4) & Chr(i5) & Chr(i6) & Chr(n)
    MsgBox Application.Substitute(MSGPWORDFOUND1, _
    "$$", PWord1), vbInformation, HEADER
    Exit Do 'Bypass all for...nexts
    End If
    End With
    Next: Next: Next: Next: Next: Next
    Next: Next: Next: Next: Next: Next
    Loop Until True
    On Error GoTo 0
    End If
    If WinTag And Not ShTag Then
    MsgBox MSGONLYONE, vbInformation, HEADER
    Exit Sub
    End If
    On Error Resume Next
    For Each w1 In Worksheets
    'Attempt clearance with PWord1
    w1.Unprotect PWord1
    Next w1
    On Error GoTo 0
    ShTag = False
    For Each w1 In Worksheets
    'Checks for all clear ShTag triggered to 1 if not.
    ShTag = ShTag Or w1.ProtectContents
    Next w1
    If ShTag Then
    For Each w1 In Worksheets
    With w1
    If .ProtectContents Then
    On Error Resume Next
    Do 'Dummy do loop
    For i = 65 To 66: For j = 65 To 66: For k = 65 To 66
    For l = 65 To 66: For m = 65 To 66: For i1 = 65 To 66
    For i2 = 65 To 66: For i3 = 65 To 66: For i4 = 65 To 66
    For i5 = 65 To 66: For i6 = 65 To 66: For n = 32 To 126
    .Unprotect Chr(i) & Chr(j) & Chr(k) & _
    Chr(l) & Chr(m) & Chr(i1) & Chr(i2) & Chr(i3) & _
    Chr(i4) & Chr(i5) & Chr(i6) & Chr(n)
    If Not .ProtectContents Then
    PWord1 = Chr(i) & Chr(j) & Chr(k) & Chr(l) & _
    Chr(m) & Chr(i1) & Chr(i2) & Chr(i3) & _
    Chr(i4) & Chr(i5) & Chr(i6) & Chr(n)
    MsgBox Application.Substitute(MSGPWORDFOUND2, _
    "$$", PWord1), vbInformation, HEADER
    'leverage finding Pword by trying on other sheets
    For Each w2 In Worksheets
    w2.Unprotect PWord1
    Next w2
    Exit Do 'Bypass all for...nexts
    End If
    Next: Next: Next: Next: Next: Next
    Next: Next: Next: Next: Next: Next
    Loop Until True
    On Error GoTo 0
    End If
    End With
    Next w1
    End If
    MsgBox ALLCLEAR & AUTHORS & VERSION & REPBACK & ZHENGLI, vbInformation, HEADER
End Sub