2024年9月

80后用菜刀,90后用蚁剑,95后用冰蝎和哥斯拉,以phpshell连接为例,本文主要是对这四款经典的webshell管理工具进行流量分析和检测。

什么是一句话木马?

1、定义
顾名思义就是执行恶意指令的木马,通过技术手段上传到指定服务器并可以正常访问,将我们需要服务器执行的命令上传并执行
2、特点
短小精悍,功能强大,隐蔽性非常好
3、
举例
php一句话木马用php语言编写的,运行在php环境中的php文件,例:
<?php @eval($_POST['pass']);?>
4、原理
以最为常见的php一句话木马为例,"
<?php ?>
"为php固定规范写法,"
@
"在php中含义为后面如果执行错误不会报错,"
eval()
"函数表示括号里的语句全做代码执行,"
$_POST['pass']
"表示从页面中以
post方式
获取变量pass的值

四、哥斯拉(Godzilla v3.0)

①全部类型的shell能绕过市面大部分的静态查杀

②流量加密能绕过过市面绝大部分的流量Waf

③Godzilla自带的插件是冰蝎、蚁剑不能比拟的

1、主要功能

它能实现的功能除了传统的命令执行、文件管理、数据库管理之外,根据shell类型的不同还包括了:

(1)MSF联动

(2)绕过OpenBasedir

(3)ZIP压缩 ZIP解压

(4)代码执行

(5)绕过 DisableFunctions

(6)Mimikatz

(7)读取服务器 FileZilla Navicat Sqlyog Winscp XMangager 的配置信息以及密码

(8)虚拟终端 可以用netcat连接

(9)Windows权限提升 (2012-2019烂土豆)

(10)读取服务器 谷歌 IE 火狐 浏览器保存的账号密码

(11)Windows权限提升烂土豆的C#版本 甜土豆

(12)支持 哥斯拉 冰蝎 菜刀 ReGeorg 的内存shell 并且支持卸载

(13)屏幕截图

(14)Servlet管理 Servlet卸载

(15)内存加载Jar 将Jar加载到 SystemClassLoader

2、基础配置

首先从使用最多的PHP_XOR_BASE64类型的加密shell说起,所用的shell主要配置如下:

①URL:
http://172.16.159.129/godzilla_shell.php

②密码:pass

③密钥:key

④有效载荷:PhpDynamicPayload

⑤加密器:PHP_XOR_BASE64

⑥哥斯拉的Shell配置包括基本配置和请求配置,其中基本配置主要设置shell地址、密码、密钥、加密器等信息

这里要注意密码和密钥的不同:

①密码:和蚁剑、菜刀一样,密码就是POST请求中的参数名称,本例中哥斯拉提交的每个请求都是pass=xxxxxxxx这种形式

②密钥:用于对请求数据进行加密,不过加密过程中并非直接使用密钥明文,而是计算密钥的md5值,然后取其

16位
用于加密过程

③哥斯拉shell的请求配置主要用于自定义HTTP请求头,以及在最终的请求数据左右再追加一些扰乱数据,进一步降低流量的特征

3、PHP_XOR_BASE64加密器

哥斯拉内置了3种Payload以及6种加密器,6种支持脚本后缀,20个内置插件,以下主要以
PHP_XOR_BASE64为例进行分析。

(1)加密原理

XOR运算

在逻辑运算之中,除了
AND

OR
,还有一种
XOR
运算,中文称为"异或运算"。
它的定义是:两个值相同时,返回false,否则返回true。也就是说,XOR可以用来判断两个值是否不同。

JavaScript
语言的二进制运算,有一个专门的 XOR 运算符,写作^。

上面代码中,如果两个二进制位相同,就返回0,表示false;否则返回1,表示true。

XOR加密

XOR运算有一个很奇妙的特点:如果对一个值连续做两次 XOR,会返回这个值本身。


上面代码中,原始信息是
message
,密钥是
key
,第一次 XOR会得到加密文本
cipherText
。对方拿到以后,再用
key
做一次XOR 运算,就会还原得到
message

如果每次的
key
都是
随机的
,那么产生的
CipherText
具有所有可能的值,而且是均匀分布,无法从
CipherText
看出
message
的任何特征。它具有最大的"信息熵",这被称为XOR 的
"
完美保密性
"
(perfect secrecy)。

XOR 的这个特点,使得它可以被用于信息的加密。

(2)客户端加密模块分析

哥斯拉的源码是通过反编译
Godzilla.jar
得到的,作者并未做代码混淆。

从代码中可以分析出,发送的payload内容先经过XOR加密后,再将密文进行base64编码,最后进行URL编码。

XOR加密的密钥来自用户提供的密钥经过MD5的32位摘要后,取前16位的值。

(3)Shell服务器端代码分析

PHP_XOR_BASE64
类型的加密shell的服务器端代码如下,其中定义了
encode
函数,用于加密或解密请求数据。由于是通过按位异或实现的加密,所以
encode
函数即可用于加密,同时也可用于解密。

整个shell的基本执行流程是:服务器接收到哥斯拉发送的第一个请求后,由于此时尚未建立session,所以将POST请求数据解密后(得到的内容为shell操作中所需要用到的相关php函数定义代码)存入session中,后续哥斯拉只会提交相关操作对应的
函数名称(如获取目录中的文件列表对应的函数为getFile)和相关参数,这样哥斯拉的相关操作就不需要发送大量的请求数据。

(4)数据包分析

这里从Shell Setting
对话框中的
测试连接
操作开始分析,在Shell Setting对话框中,一共会产生3个
POST
数据包,POST请求报文中参数名都是
pass
(即shell的连接密码),参数值都是加密数据。


第一个Request请求数据包

简单分析了一下payload的内容,包含run、bypass_open_basedir、formatParameter、evalFunc等二十多个功能函数,具备代码执行、文件操作、数据库操作等诸多功能。

②第一个Response响应数据包

该请求不含有任何Cookie
信息,服务器响应报文不含任何数据,但是会设置PHPSESSID
,后续请求都会自动带上该Cookie。

③第二个Request请求数据包

第二个请求报文发送很少数据(实际内容为测试连接命令
test
),返回少量数据(即
ok


第二个Response响应数据包

服务器响应数据解密过程并不复杂,先调用
findStr
函数删除服务器响应数据左右附加的混淆字符串(对于
PHP_XOR_BASE64
加密方式来说,前后各附加了16位的混淆字符),然后将得到的数据进行
base64
解码,最后再和shell连接密钥md5值的前16位按位异或,即完成响应数据的解密。

⑤第三个Request请求数据包

运行哥斯拉命令执行代码中的getBasicsInfo函数得到的系统基本信息。


第三个Response响应数据包

4、PHP_EVAL_XOR_BASE64加密器

哥斯拉不同的加密器发送请求的过程都是一样的,不同之处在于加密/解密的实现方式不同。
PHP_EVAL_XOR_BASE64加密shell的特点如下:
Ø请求数据加密得到的密文形式:pass=evalContent&key=XXXXXXXX,其中
pass是shell密码,
key是shell密钥
Ø每个请求中的pass=evalContent都是
相同的,evalContent是将shells/cryptions/phpXor/template/base64.bin文件内容
经过编码得到的(先删除第1行的<?php,再将其中的{pass}替换为shell密码,将{secretKey}替换为shell密钥)
Ø每个请求中的key=XXXXXXXX才是
实际执行的shell操作,加密方法和PHP_XOR_BASE64加密shell的方式
相同
evalContent的加密过程如下:
①提取src/shells/cryptions/phpXor/template/base64.bin文件内容
②将base64.bin文件内容进行
base64编码
③将第2步中编码得到的字符串
逆序排列
④将第3步中得到的字符串进行
URL编码
⑤将第4步中得到的字符串拼接到
eval(base64_decode(strrev(urldecode('第4步中得到的字符串'))));中,即为最终的evalContent

5、
PHP_XOR_RAW
加密器

PHP_XOR_RAW加密shell的加解过程只是将原始数据与shell密钥(本例中为key)md5值的前16位
按位异或,然后将得到的
二进制字节码直接发送给服务器;服务器返回的响应数据也是
二进制字节码,左右不再追加任何数据。

6、规则落地

alert http any any -> any any (msg:"哥斯拉/Godzilla PHP Base64 连接成功";
flow:established,from_server;
flowbits:txisset,Godzilla_webshell_request_match;
http.server;
content:
"Rising", negate;
http.response_body;
bsize:
36;
pcre:
"/^([0-9A-F]{16}|[0-9a-f]{16})/";
pcre:
"/([0-9A-F]{16}|[0-9a-f]{16})$/";
pcre:
"/^[\s\S]{16}(.*)[\s\S]{16}$/";
pcrexform:
"^[\s\S]{16}(.*)[\s\S]{16}$";
pcre:
"/[\s\S]/";
base64;
flowbits:unset,Godzilla_webshell_request_match;
)
  • alert http any any -> any any 表示对任何源IP和目的IP之间的HTTP流量生成告警。
  • msg:"哥斯拉/Godzilla PHP Base64 连接成功" 是告警的描述信息。
  • flow:from server,established 表示只对来自服务器端的已建立连接的流量进行检测。
  • flowbits:isset,Godzilla webshell request match 检查名为"Godzilla webshell request match"的流状态位是否已被设置。(注意这里使用了isset而不是txisset)
  • http.server 表示只对HTTP服务器响应进行检测。
  • content:!"Rising" 表示检测HTTP响应正文中不包含字符串"Rising"。
  • http.response body 表示对HTTP响应的正文部分进行检测。
  • bsize:36 指定只检测响应正文的前36个字节。
  • pcre:"/^([0-9A-F]{16}|[0-9a-f]{16})/"和pcre:"/([0-9A-F]{16}|[0-9a-f]{16})$/" 使用Perl兼容正则表达式(PCRE)检测响应正文是否以16个十六进制字符开头和结尾。
  • pcre:"/^[\s\S]{16}(.*)[\s\S]{16}$/"和pcrexform:"^[\s\S]{16}(.*)[\s\S]{16}$" 使用PCRE检测响应正文是否符合特定的模式,即以16个任意字符开头,任意字符串为中间部分,再以16个任意字符结尾。
  • pcre:"/[\s\S]/" 匹配响应正文中的单个被方括号包围的任意字符。
  • isbase64:3 表示对匹配到的内容进行Base64解码,并检查解码后的字节数是否是3的倍数。
  • flowbits: unset,Godzilla webshell request match 清除名为"Godzilla webshell request match"的流状态位。

原来有这么多时间

六月的那么一天,天气比以往时候都更凉爽,媳妇边收拾桌子,边漫不经心的对我说:你最近好像都没怎么阅读了。 正刷着新闻我,如同被一记响亮的晴空霹雳击中一般,不知所措。是了,最近几月诸事凑一起,加之两大项目接踵而至,确实有些许糟心,于是总是在空闲的时间泡在新闻里聊以解忧,再回首,隐隐有些恍如隔世之感。于是收拾好心情,翻开了躺在书架良久的整洁三步曲。也许是太久没有阅读了, 一口气,Bob大叔 Clean 系列三本都读完了,重点推荐Clear Architecture,部分章节建议重复读,比如第5部分-软件架构,可以让你有真正的提升,对代码,对编程,对软件都会有不一样的认识。

Clean Code 次之,基本写了一些常见的规约,大部分也是大家熟知,数据结构与面向对象的看法,是少有的让我 哇喔的点,如果真是在码路上摸跋滚打过的,快速翻阅即可。
The Clean Coder 对个人而言可能作用最小。 确实写人最难,无法聚焦。讲了很多,但是感觉都不深入,或者作者是在写自己,很难映射到自己身上。 当然,第二章说不,与第14章辅导,学徒与技艺,还是值得一看的。

阅读技术书之余,又战战兢兢的翻开了敬畏已久的朱生豪先生翻译的《莎士比亚》, 不看则已,因为看了根本停不来。其华丽的辞职,幽默的比喻,真的会让人情不自禁的开怀朗读起来。

。。。

再看从6月到现在,电子书阅读时间超过120小时,平均每天原来有1个多小时的空余时间,简直超乎想像。



看了整洁架构一书,就想写代码,于是有了这篇文章。

灵魂拷问 - 宕机怎么办

为了解决系统中大量规则配置的问题,与同事一起构建了一个可视化表达式引擎 RuleLink
《非全自研可视化表达引擎-RuleLinK》
,解决了公司内部几乎所有配置问题。尤为重要的一点,所有配置业务同学即可自助完成。随着业务深入又增加了一些自定义函数,增加了公式及计算功能,增加组件无缝嵌入其他业务...我一度以为现在的功能已经可以满足绝大部分场景了。真到Wsin强同学说了一句:业财项目是
深度依赖
RuleLink的,流水打标,关联科目。。。我知道他看了数据,10分RuleLink执行了5万+次。这也就意味着,如果RuleLink宕机了,业财服务也就宕机了,也就意味着巨大的事故。这却是是一个问题,公司业务确实属于非常低频,架不住财务数据这么多。如果才能让RuleLink更稳定成了当前的首要问题。


高可用VS少依赖

要提升服务的可用性,增加服务的实例是最快的方式。 但是考虑到我们自己的业务属性,以及业财只是在每天固定的几个时间点短时高频调用。 增加节点似乎不是最经济的方式。看 Bob大叔的《Clear Architecture》书中,对架构的稳定性有这样一个公式:不稳定性,I=Fan-out/(Fan-in+Fan-out)

Fan-in:入向依赖,这个指标指代了组件外部类依赖于组件内部类的数量。

Fan-out:出向依赖,这个指标指代了组件内部类依赖于组件外部类的数量。

这个想法,对于各个微服务的稳定性同时适用,少一个外部依赖,稳定性就增加一些。站在业财系统来说,如果我能减少调用次数,其稳定性就在提升,批量接口可以一定程度上减少依赖,但并未解决根本问题。那么调用次数减少到极限会是什么样的呢?答案是:
一次。
如果规则不变的话,我只需要启动时加载远程规则,并在本地容器执行规则的解析。如果有变动,我们只需要监听变化即可。这样极大减少了业财对RuleLink的依赖,也不用增RuleLink的节点。实际上大部分配置中心都是这样的设计的,比如apollo,nacos。 当然,本文的实现方式也有非常多借鉴(copy)了apollo的思想与实现。

服务端设计

模型比较比较简单,应用订阅场景,场景及其规则变化时,或者订阅关系变化时,生成应用与场景变更记录。类似于生成者-消费都模型,使用DB做存储。

”推送”原理

整体逻辑参考apollo实现方式。 服务端启动后 创建Bean ReleaseMessageScanner 注入变更监听器 NotificationController。
ReleaseMessageScanner 一个线程定时扫码变更,如果有变化 通知到所有监听器。

NotificationController在得知有配置发布后是如何通知到客户端的呢?
实现方式如下:
1,客户端会发起一个Http请求到RuleLink的接口,NotificationController
2,NotificationController不会立即返回结果,而是通过Spring DeferredResult把请求挂起
3,如果在60秒内没有该客户端关心的配置发布,那么会返回Http状态码304给客户端
4,如果有该客户端关心的配置发布,NotificationController会调用DeferredResult的setResult方法,传入有变化的场景列表,同时该请求会立即返回。客户端从返回的结果中获取到有变化的场景后,会直接更新缓存中场景,并更新刷新时间

ReleaseMessageScanner 比较简单,如下。NotificationController 代码也简单,就是收到更新消息,setResult返回(如果有请求正在等待的话)

public class ReleaseMessageScanner implementsInitializingBean {private static final Logger logger = LoggerFactory.getLogger(ReleaseMessageScanner.class);private finalAppSceneChangeLogRepository changeLogRepository;private intdatabaseScanInterval;private final List<ReleaseMessageListener>listeners;private finalScheduledExecutorService executorService;public ReleaseMessageScanner(finalAppSceneChangeLogRepository changeLogRepository) {this.changeLogRepository =changeLogRepository;
databaseScanInterval
= 5000;
listeners
=Lists.newCopyOnWriteArrayList();
executorService
= Executors.newScheduledThreadPool(1, RuleThreadFactory
.create(
"ReleaseMessageScanner", true));
}

@Override
public void afterPropertiesSet() throwsException {
executorService.scheduleWithFixedDelay(()
->{try{
scanMessages();
}
catch(Throwable ex) {
logger.error(
"Scan and send message failed", ex);
}
finally{

}
}, databaseScanInterval, databaseScanInterval, TimeUnit.MILLISECONDS);

}
/*** add message listeners for release message
*
@paramlistener*/ public voidaddMessageListener(ReleaseMessageListener listener) {if (!listeners.contains(listener)) {
listeners.add(listener);
}
}
/*** Scan messages, continue scanning until there is no more messages*/ private voidscanMessages() {boolean hasMoreMessages = true;while (hasMoreMessages && !Thread.currentThread().isInterrupted()) {
hasMoreMessages
=scanAndSendMessages();
}
}
/*** scan messages and send
*
*
@returnwhether there are more messages*/ private booleanscanAndSendMessages() {//current batch is 500 List<AppSceneChangeLogEntity> releaseMessages =changeLogRepository.findUnSyncAppList();if(CollectionUtils.isEmpty(releaseMessages)) {return false;
}
fireMessageScanned(releaseMessages);
return false;
}
/*** Notify listeners with messages loaded
*
@parammessages*/ private void fireMessageScanned(Iterable<AppSceneChangeLogEntity>messages) {for(AppSceneChangeLogEntity message : messages) {for(ReleaseMessageListener listener : listeners) {try{
listener.handleMessage(message.getAppId(),
"");
}
catch(Throwable ex) {
logger.error(
"Failed to invoke message listener {}", listener.getClass(), ex);
}
}
}
}
}

客户端设计

上图简要描述了客户端的实现原理:

  • 客户端和服务端保持了一个长连接,从而能第一时间获得配置更新的推送。(通过Http Long Polling实现)
  • 客户端还会定时从RuleLink配置中心服务端拉取应用的最新配置。
    • 这是一个fallback机制,为了防止推送机制失效导致配置不更新
    • 客户端定时拉取会上报本地版本,所以一般情况下,对于定时拉取的操作,服务端都会返回304 - Not Modified
    • 定时频率默认为每5分钟拉取一次,客户端也可以通过在运行时指定配置项: rule.refreshInterval来覆盖,单位为分钟。
  • 客户端从RuleLink配置中心服务端获取到应用的最新配置后,会写入内存保存到SceneHolder中,
  • 可以通过RuleLinkMonitor 查看client 配置刷新时间,以及内存中的规则是否远端相同

客户端工程

客户端以starter的形式,通过注解EnableRuleLinkClient 开始初始化。

1 /**
2 *@authorJJ3  */
4 @Retention(RetentionPolicy.RUNTIME)5 @Target(ElementType.TYPE)6 @Documented7 @Import({EnableRuleLinkClientImportSelector.class})8 public @interfaceEnableRuleLinkClient {9 
10   /**
11 * The order of the client config, default is {@linkOrdered#LOWEST_PRECEDENCE}, which is Integer.MAX_VALUE.12 *@return
13    */
14   int order() defaultOrdered.LOWEST_PRECEDENCE;15 }

在最需求的地方应用起来

花了大概3个周的业余时间,搭建了client工程,经过一番斗争后,决定直接用到了最迫切的项目 - 业财。当然,也做了完全准备,可以随时切换到RPC版本。 得益于DeferredResult的应用,变更总会在60s内同步,也有兜底方案:每300s主动查询变更,即便是启动后RuleLink宕机了,也不影响其运行。这样的准备之下,上线后几乎没有任何波澜。当然,也就没有人会担心宕机了。这真可以算得上一次愉快的编程之旅。

成为一名优秀的程序员!

在摄影中,光线起着至关重要的作用,它对图像的整体质量和氛围有着显著的影响。您可以使用光线来增强主题,创造深度和维度,传达情感,以及突出重要细节。

在这篇文章中,我会告诉你如何在stable diffussion中控制生成图片的光线。

软件

我们将使用 AUTOMATIC1111 Stable Diffusion GUI 来创建图像。

使用光线关键词

最简单的控制光线的方法就是在提示中添加
光线关键词

我将使用以下基础提示和负面提示来说明效果。

正向提示词:

masterpiece,best quality,masterpiece,best quality,official art,extremely detailed CG unity 8k wallpaper,a beautiful woman,

负向提示词:

lowers,monochrome,grayscales,skin spots,acnes,skin blemishes,age spot,6 more fingers on one hand,deformity,bad legs,error legs,bad feet,malformed limbs,extra limbs,

模型:majicmixRealistic_v7

宽度:512

高度:768

CFG 刻度:7

下面是使用基础提示词生成的图片,他们看起来还不错,但是光线就不怎么样了。

image-20240703143858781

Volumetric lighting
是在图像上明显的光束。它在摄影中用于增加体积感。

在提示中添加关键词
Volumetric lighting

image-20240703144120928

rim lighting
为主题添加了明亮的轮廓。它可能会使主题变暗。您可以与其他光线术语结合使用以照亮主题。

在提示中添加关键词
rim lighting

image-20240703144310934

Sunlight
为图像添加了阳光。它倾向于呈现自然背景。

在提示中添加关键词
Sunlight

image-20240703144429961

Backlight
将光源置于主题之后。通过添加这个关键词,您可以产生一些时尚的效果。

在提示中添加
Backlight

image-20240703144516763

众所周知,Stable Diffusion 在没有引导的情况下不会产生黑暗的图像。

解决这个问题的方法有很多,包括使用模型和 LoRA。但更简单的方法是添加一些昏暗的光线关键词。

在提示中添加
dimly lit

image-20240703144626131

Crepuscular rays
在云层中添加了光线穿透的光线。它可以创造出令人惊叹的视觉效果。

这个提示和肖像宽高比通常呈现全身图像,添加
Crepuscular rays
会放大。

image-20240703144742215

技巧:

  • 如果您没有看到效果,请增加关键词的权重。

  • 这些光线关键词并不总是有效。一次生成几张图像进行测试。

  • 在提示生成器中找到更多的光线关键词。

控制特定区域的光线

提示中的光线关键词适用于整个图像。这里我会告诉你如何控制特定区域的光线。

这里你需要安装一个插件叫做regional Prompter。

下载地址如下:
https://github.com/hako-mikan/sd-webui-regional-prompter.git

安装好之后,可以在工作区的下方发现这个Regional Prompter的区域。

在这个例子中,我们将对图像的上部和下部应用不同的光线。


txt2img
页面上,展开
regional Prompter
部分。

image-20240703150427848

按我上面的选择进行设置。

基本上含义就是把图片按2:3的比例分割成两部分,来分别进行promot设置。

regional Prompter是一个非常强大的工具,可以产出非常惊艳的效果。我会在后续的文章中详细介绍regional Prompter。

这里只是作为一个使用场景。

我们改下输入提示:

正向提示词:

masterpiece,best quality,masterpiece,best quality,official art,extremely detailed CG unity 8k wallpaper,a beautiful woman,
BREAK
( hard light:1.2),(volumetric:1.2),well-lit,
BREAK
(dimly lit:1.4),

负面提示词保持不变。

这样我们的到了一个上面光亮,下面昏暗的图片。

image-20240703150710842

现在尝试交换光线分配。

masterpiece,best quality,masterpiece,best quality,official art,extremely detailed CG unity 8k wallpaper,a beautiful woman,
BREAK
(dimly lit:1.4),
BREAK
( hard light:1.2),(volumetric:1.2),well-lit,

image-20240703150837199

光线相应地交换。

技巧:

  • 如果您没有看到效果,请调整关键词的权重。

  • 区域提示并不总是100%有效。可以多尝试一些图片看看效果。

使用 ControlNet 控制光线

除了上面的提示词和regional Prompter来控制光线之外。我们还可以使用controlNet来对图片的光线进行更加精确的控制。

controlNet是一个单独的插件,所以你需要先安装它。

Txt2img 设置

安装好controlNet之后,在
txt2img
页面上,像平常一样生成图像。

image-20240703151405473

点击发送到
img2img

这个操作会把所有的提示,负面提示,图像大小和种子值拷贝到 img2img 页面。

Img2img 设置


img2img
页面上,导航到 ControlNet 部分。

将您刚刚保存的图像上传到
ControlNet 单元 0

image-20240703173952451

大家可以使用我的配置选项。

这里我们需要选择Depth模型,在preprocessor中选择depth_zoe,model选择control_xxxx_depth。

向上滚动到
img2img 画布
。删除图像。

然后使用画图工具绘制一个黑白的模板图。

白色代表光线。

如下所示:

image-20240703174500514

把这个图像上传到
img2img 画布


调整大小模式
设置为仅调整大小。


去噪强度
设置为 0.9。

点击
生成

您应该得到带有横向光源的图像。

image-20240703174546141

如果你不想创建自己的光源,那么可以baidu一下黑白光源图片:

image-20240703174814660

比如第一张光源图片,我们可以得到下面的图片:

image-20240703174921267

备注

不一定必须使用深度控制模型。其他模型,如 canny 和lineart模型,也可以工作。你可以尝试使用预处理器,看看哪一个适合你。

如果您看到不自然的颜色,请减少
Controlnet 权重

调整去噪强度并观察效果。
点我查看更多精彩内容:www.flydean.com

我不在的大半年,大数据服务基本没问题,只过来维护过一两次

2024年大半年,大数据服务都比较稳定,我也只过来维护过一两次。8月份我又过来了,交接完离职同事的工作,本来没什么事情。

StatHub页面服务状态不刷新

StatHub是一个集群管理/应用编排/进程守护工具,可以用来高效的管理服务集群。具有节点进程管理和应用管理功能。
在这工作的另一家公司的大数据研发说,StatHub页面服务状态不刷新。我说你的服务是正常的吗?他说是正常的。我说不用管它,等哪天有空我再看看。
StatHub包括master和agent两个部分:
stat-server,即master,提供服务编排界面。
stat-agent,运行在工作节点,守护工作进程。
StatHub源代码地址:
https://github.com/rm-vrf/stat

完蛋了,删了不该删的文件夹

闲下来之后,我就尝试解决StatHub的问题。其实以前是有解决方案的,就是查找各服务器节点上的.stat_agent文件夹中的app和proc文件夹中的大小为0的文件并删除,就可以了。
但是我一时半会没想起来这个解决方案,于是想着通过重启解决,我重启了不正常节点的stat-agent,又多次重启了stat-server,都不行。
我想,是不是什么缓存造成的,.stat_server这个文件夹最开始部署StatHub的时候肯定是不存在的,它应该是自动生成的,我先停了stat-server,再把它删了,然后重启试试。于是,我就这样删除了.stat-server,重启StatHub成功,.stat-server文件夹又自动重启生成了。但是很快我就发现了一个严重问题,StatHub页面上的那100多个服务全没了!页面空了!
跑路吧,要失业了,卧槽!
虽然100多个服务脱离管理了,但服务应该都还是在正常运行的,只要服务不挂,一时半会是没有问题的。
怎么办?恢复数据?那个服务器很重要,上面跑了不少重要的服务,万一搞坏了,就真的完了。

找到方法,慢慢恢复StatHub页面的服务管理

好在,我发现stat-agent所在的20多台服务器上的.stat_agent文件夹中的proc文件夹中的各服务的进程信息都在,那里面有服务的名称和启动命令,可以用来在StatHub页面中重新录入服务信息,主要是启动参数,因为有些java和spark服务的启动参数比较复杂。于是我把20多台服务器上的proc文件夹中的服务名称和启动命令做了备份。然后,先恢复了2、3个服务的管理,但是服务状态刷新不出来,也无法正常停止和启动服务,我只能到服务所在机器上,敲Linux命令查看服务运行状态。

修改StatHub源码,解决服务状态不正常的问题

打开StatHub的源码,发现遍历各节点信息时,加了try catch,但只catch了ResourceAccessException异常,其它异常会导致for循环挂了,所有节点和进程信息都获取失败了。所以我修改了代码,加了一个catch (Exception e),并打印日志,提交,重新发布启动stat-server,查看stat-server日志,确定了异常节点,把异常节点服务器上的大小为0的文件删除,服务状态就正常了。

又出现新情况,StatHub页面节点列表中162这台机器的节点信息不见了

因为某原因重启162节点上的stat-agent后,StatHub页面节点列表中162这台机器的节点信息不见了。最后发现是服务器出了问题,mount命令,卡一会,一堆挂载,不知为何。df -hl命令也会卡一会才出来信息,这个问题导致stat-agent遍历磁盘信息时,卡住了。

ClickHouse也出问题了,一个服务插入数据时频繁报Too many parts异常

之前解决过一次,思路就是增加每次批量插入的数据量,以减少插入次数。当时服务暂时稳定了,我以为解决了,其实并没有解决。服务消费的kafka的topic共有78个分区,rdd.foreachPartition的并行度是78,太大了,怎么减少并行度呢?当时我并不知道怎么解决。这次,我把代码改成了rdd.coalesce(1).foreachPartition,coalesce的作用是减少分区,这样就可以减少数据插入ClickHouse的并行度,我把并行度设置为1。按理说问题应该解决了,但还是报Too many parts异常,数据插入成功几次失败几次。

重启ClickHouse

没有什么是重启解决不了的,如果不行,就再重启一次。
于是我就决定重启4个节点的ClickHouse服务。
重启第3个节点时,服务器突然失联,我就重启个ClickHouse就把服务器搞挂了?好在有惊无险,过了一会,又连上了。
重启第4个节点时,发现起不来了啊!查看监控页面,发现所有写入ClickHouse的服务,都报红了!我又重启了依赖的zookeeper服务,又多次重启了ClickHouse,都不行。
部分报错信息:DB::Exception: The local set of parts of table 'xxx' doesn't look like the set of parts in ZooKeeper: xxx billion rows of xxx billion total rows in filesystem are suspicious. ... Cannot attach table 'xxx' from metadata file /var/lib/clickhouse/metadata/xxx/xxx.sql from query ATTACH TABLE ...
百度搜到一个类似问题
https://support.huaweicloud.com/intl/en-us/trouble-mrs/mrs_03_0281.html
,步骤太多,没太看明白,不敢操作。

解决问题,重启ClickHouse成功

我注意到报错信息中的metadata file,心生一计,把错误日志中提到的那两个.sql文件改名成xxx.sql.bak备份一下,然后重启ClickHouse,成功了!然后把那两个文件又改名回来。然后观察那些写入ClickHouse的服务,全都正常了,部分服务失败了没有自动重启就手动重启了一下。然后发现Too many parts的问题也解决了。

162服务器也正常了

另一家公司的大数据研发,经过准备工作,重启了这台机器解决了问题。

StatHub页面的服务管理恢复了大半

经过这几天的手动录入,StatHub页面的服务管理恢复了大半。
我把stat-server所在服务器上的.stat_server文件夹中的app和choreo文件夹做了备份。以前没想到这个文件夹如此重要,也没想过会被删,从来没有备份过。
剩下的服务,慢慢录入,或者等服务出问题需要重启的时候再录入也行。

这一个多星期的工作是无中生有吗?

也不全是

  1. StatHub页面服务状态不正常,还是需要处理的。但是我犯了错误,把不该删的文件夹删除了。经过这次教训,我做了备份。
  2. ClickHouse出问题是迟早的,因为之前写的spark服务,始终没有优化好,数据插入并行度太大。
  3. 162服务器早就有问题了,但只要不重启stat-agent就没事。

问题处理的差不多了

还有一个问题,StatHub页面的100多个服务,只恢复了大半。恢复服务管理,是需要重启服务的,很多服务并不是我写的,也不是我部署的,我不熟悉,万一起不来,影响了业务,就会造成不必要的麻烦。但服务脱离管理,万一哪天挂了,又不知道,也会给排查问题造成麻烦。

前言

前面一篇文章已经介绍过,ComfyUI 和 Stable Diffusion 的关系。不清楚的朋友,看传送门
Stable Diffusion 小白的入坑铺垫

WebUI 以及 ComfyUI 不等于 Stable Diffusion,可以简单粗暴一点的理解为方便运行某些大模型的工具。由于本人在接触过 ComfyUI 之后,就基本放弃 WebUI 了,本文开始,接下来会有一个系列的入门文章来介绍 ComfyUI。不论是 ComfyUI 还是 WebUI,基础工作原理都是需要理解清楚,才能更好地利用大模型以及一些插件,来生成我们想要的效果。本文主要介绍 ComfyUI 的本地安装部署。

一、官方版本安装

ComfyUI 官方地址如下:
https://github.com/comfyanonymous/ComfyUI

安装步骤,官方文档写的比较清楚,这里就不再赘述。

安装官方版本需要有一定的编程基础,首先懂得 git 的使用,其次要有一定的 Python 基础,基本的环境管理、包安装等。如果你不会魔法冲浪,还需要懂的换源。
另外安装官方版本,有很大概率,在安装过程中会出现一些报错,需要自己挨个处理。

二、秋葉整合包

如果只有官方版本,那估计要劝退一大半的人,难道不懂编程就不能使用 AI 绘画了?广大设计师们表示心有不甘。不急,相信开源的力量,除了官方版本以外,有很多大神自发制作了一键启动的整合包,只需要下载下来整合包,解压,然后就可以一键启动。在众多版本的整合包中,当属 B站
@秋葉aaaki
大佬的绘事启动器最广为人知。

2.1 整合包下载安装

秋叶 ComfyUI 整合包官方发布地址:
https://www.bilibili.com/video/BV1Ew411776J/

网盘下载:
https://pan.quark.cn/s/64b808baa960

如果需要其它网盘的下载地址,可以到视频评论区去找。热心网友已经上传,并分享出来了。

2.2 整合包使用说明

整合包下载下来,解压,然后成功启动后的界面应该像下面这样:

一般来说,首次启动,在使用前,最好先更新一下内核版本,以及更新插件。
在更新之前,点击左边菜单栏中的最下面的设置,找到网络设置:

如果你没有魔法,请确保圈起来的这些开关全部打开。
接下来,点击版本管理,执行更新:

依次刷新内核版本,一键更新,刷新扩展版本,一键更新即可。

最后,回到一键启动页面,点击一键启动,然后启动器界面会自动跳转到控制台页面,等待一会,看到如下信息,就代表启动成功了。

此时,正常情况下,你的浏览器,应该打开了如下页面,并加载了一个默认工作流。

地址栏地址应该和控制台信息中显示的地址一致
http://127.0.0.1:8188/

如果你的浏览器没有自动打开该页面,可以手动打开浏览器,输入上面的地址打开。

有可能你的默认界面显示的不是中文,如需要设置语言,点击左下角的小齿轮,进入设置界面,找到语言,然后选择。

还可能存在一种情况,你安装的不是最新版本的整合包,也没有更新内核就启动了,有可能你看到的是旧的悬浮面板样式,

此时的小齿轮在悬浮面板右上角,点击进入设置进行更改语言即可。建议更新到新版本,使用新的界面,看起来更简洁。

回到主页面,点击页面中右上角
执行队列
,则开始执行该工作流,稍等一会,能看到生成的图片。

这个默认工作流是最简单的工作流,生成图片速度很快,具体花费时间,取决于你的电脑配置,主要是显卡。

OK, 到这里,就表明本地 ComfyUI 环境安装部署成功了。

三、整合包插件安装(自定义节点)

插件,也叫自定义节点。不论是官方版本还是整合包,安装成功后,都已经自带了很多常用的插件,但这远远不够,实际使用过程中要经常安装插件,整合包安装插件的方式有很多,下面逐一介绍。

3.1 通过 ComfyUI 节点管理器安装

ComfyUI 节点管理器,本身也是一个插件,叫
ComfyUI-Manager
,在成功安装 ComfyUI 时也一并安装了。
点击菜单栏上的
Manager
,代开 Manager 界面

安装成功之后,需要重启 ComfyUI 启动器生效。

3.2 通过 Git URL 安装

一般自定义节点都会在某个 git 仓库中,找到对应的地址。
比如 EchoMimic 插件,打开它的仓库地址页面,点击 Code, 即可查看到地址,点击复制即可。

在你 ComfyUI 安装路径下找到 custom_nodes ,比如我的是
D:\AI\StableDiffusion\ComfyUI\custom_nodes

打开命令行窗口,执行命令

git clone https://github.com/sharpcj/EchoMimic.git

3.3 下载插件包安装

还是在 git 仓库地址中,点击下面的
Download ZIP
下载下来,解压到
custom_nodes
目录中即可。

该方法不能直接进行插件更新,不推荐使用。

3.4 启动器插件管理

这个是整合包特有的安装方式,打开启动器,选到版本管理菜单,安装新扩展,然后搜索需要安装的插件,点击安装即可。

四、工作流的加载与保存

ComfyUI 工作流的形式有两种,一种是 json 文件。记录了工作流的节点信息,连接信息等等。另一种是通过 ComfyUI 工作流生成的图片,默认带有生成该图片的工作流信息。

4.1 加载工作流

直接将工作流 json 文件 或者 带有工作流信息的 图片拖进 ComfyUI 操作界面就行了。
注意:只有通过 ComfyUI 工作流生成的,并且没有经过去去除信息处理的图片才可以。

4.2 保存工作流

同理,保存工作流的形式有两种,一种是生成的图片,另一种是通过菜单,点击保存,生成 json 文件。

结束语

本问主要讲了如何在本地安装部署 ComfyUI 秋葉整合包,以及如何安装插件,加载保存工作流的知识。
更多菜单功能,可以在后续掌握了一定 ComfyUI 的知识后,自行探索。
接下来一片文章,会通过最简单的文生图工作流,来介绍 ComfyUI 工作流的核心常用节点,敬请关注。