解密:最全面!一文让你看懂无侵入的微服务探针原理!!
优采云 发布时间: 2022-10-08 22:07解密:最全面!一文让你看懂无侵入的微服务探针原理!!
前言
随着微服务架构的兴起,应用行为的复杂性显着增加。为了提高服务的可观测性,分布式监控系统变得非常重要。
基于谷歌的Dapper论文,开发了很多知名的监控系统:Zipkin、Jaeger、Skywalking、OpenTelemetry,想要统一江湖。一群厂商和开源爱好者围绕采集、监控数据的采集、存储和展示做了很多优秀的设计。
如今,即使是个人开发者也可以依靠开源产品轻松构建完整的监控系统。但作为监控服务商,需要做好与业务的解绑工作,降低用户接入、版本更新、问题修复、业务止损等成本。因此,一个可插拔的、非侵入式的采集器成为了很多厂商的必备。
为了获取服务之间的调用链信息,采集器通常需要在方法前后进行埋藏。在Java生态中,常见的埋点方式有两种:依靠SDK手动埋点;使用Javaagent技术做无创跟踪。下面对无创埋点的技术和原理进行全面的介绍。
侵入式 采集器(探测)
在分布式监控系统中,模块可以分为:采集器(Instrument)、Transmitter(TransPort)、Collector(Collector)、Storage(Srotage)、Display(API&UI)。
zipkin的架构图示例
采集器将采集到的监控信息从应用端发送给采集器,采集器存储,最后提供给前端查询。
采集器采集信息,我们称之为Trace(调用链)。一条跟踪有一个唯一标识符 traceId,它由自上而下的树跨度组成。除了spanId,每个span还有traceId和父spanId,这样就可以恢复完整的调用链关系。
为了生成跨度,我们需要在方法调用前后放置埋点。比如对于一个http调用,我们可以在execute()方法前后添加埋点,得到完整的调用方法信息,生成一个span单元。
在Java生态中,常见的埋点方式有两种:依靠SDK手动埋点;使用Javaagent技术做无创跟踪。许多开发者在接触分布式监控系统时就开始使用 Zipkin。最经典的就是了解X-B3 trace协议,使用Brave SDK,手动埋点生成trace。但是,SDK中的埋点方式无疑是深深依赖于业务逻辑的。升级埋点时,必须进行代码更改。
那么如何将其与业务逻辑解绑呢?
Java还提供了另一种方式:依靠Javaagent技术修改目标方法的字节码,实现无创埋葬。这种使用Javaagent 的采集器 方式也称为探针。在应用启动时使用-javaagent,或者在运行时使用attach(pid)方法,可以将探针包导入应用,完成埋点的植入。以非侵入方式,可以实现无意义的热升级。用户无需了解深层原理即可使用完整的监控服务。目前很多开源监控产品都提供了丰富的java探针库,进一步降低了作为监控服务商的开发成本。
开发一个非侵入式探针,可以分为三个部分:Javaagent、字节码增强工具、跟踪生成逻辑。下面将介绍这些。
基本概念
在使用JavaAgent之前,让我们先了解一下Java相关的知识。
什么是字节码?
自 1994 年 Sun 发明类 C 语言 Java 以来,凭借“编译一次,到处运行”的特性,它迅速风靡全球。与 C++ 不同的是,Java 先将所有源代码编译成类(字节码)文件,然后依靠各种平台上的 JVM(虚拟机)来解释和执行字节码,从而与硬件解绑。class文件的结构是一个table表,由很多struct对象组成。
类型
姓名
阐明
长度
u4
魔法
幻数,识别Class文件格式
4字节
u2
次要版本
次要版本号
2 个字节
u2
主要版本
主要版本号
2 个字节
u2
常量池计数
常量池计算器
2 个字节
cp_info
常量池
常量池
n 字节
u2
访问标志
访问标志
2 个字节
u2
这节课
类索引
2 个字节
u2
超类
父索引
2 个字节
u2
接口数
接口计数器
2 个字节
u2
接口
接口索引集合
2 个字节
u2
字段数
字段数
2 个字节
字段信息
字段
字段集合
n 字节
u2
方法数
方法计数器
2 个字节
方法信息
方法
方法集合
n 字节
u2
属性计数
额外的物业柜台
2 个字节
属性信息
属性
附加属性集合
n 字节
字节码的字段属性
让我们编译一个简单的类 `Demo.java`
package com.httpserver;public class Demo { private int num = 1; public int add() { num = num + 2; return num; }}
16进制打开Demo.class文件,解析出来的字段也是由很多struct字段组成的:比如常量池、父类信息、方法信息等。
JDK自带的解析工具javap可以将class文件以人类可读的方式打印出来,结果和上面的一致
什么是JVM?
JVM(Java Virtual Machine),一种能够运行Java字节码的虚拟机,是Java架构的一部分。JVM有自己完整的硬件架构,如处理器、栈、寄存器等,也有相应的指令系统。JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需要生成运行在JVM上的目标代码(字节码),无需修改即可运行在各种平台上。这是“一次性编译”。,到处跑”的真正意思。
作为一种编程语言虚拟机,它不仅专用于Java语言,只要生成的编译文件符合JVM对加载和编译文件格式的要求,任何语言都可以被JVM编译运行。
同时,JVM技术规范并没有定义使用的垃圾回收算法和优化Java虚拟机指令的内部算法等,只是描述了应该提供的功能,主要是为了避免过多的麻烦和对实施者的限制。正是因为描述得当,才给厂商留下了展示的空间。
维基百科:现有 JVM 的比较
其中性能较好的HotSpot(Orcale)和OpenJ9(IBM)受到广大开发者的喜爱。
JVM的内存模型
JVM部署完成后,每一个Java应用启动,都会调用JVM的lib库申请资源,创建一个JVM实例。JVM 将内存划分为不同的区域。下面是JVM运行时的内存模型:
父委托加载机制
当 Java 应用程序启动并运行时,一个重要的操作是加载类定义并创建一个实例。这依赖于 JVM 自己的 ClassLoader 机制。
家长委托
一个类必须由一个ClassLoader加载,对应的ClassLoader和父ClassLoader,寻找一个类定义会从下往上搜索,这就是父委托模型。
JVM为了节省内存,并没有把所有的类定义都放到内存中,而是
这个设计提醒我们,如果可以在加载时或者直接替换加载的类定义,就可以完成神奇的增强。
JVM工具接口
晦涩难懂的 JVM 屏蔽了底层的复杂性,让开发人员可以专注于业务逻辑。除了启动时通过java -jar的内存参数外,其实还有一套专门提供给开发者的接口,即JVM工具接口。
JVM TI 是一个双向接口。JVM TI Client 也称为代理,基于事件事件机制。它接受事件并执行对 JVM 的控制,还可以响应事件。
它有一个重要的特性——Callback(回调函数)机制:JVM可以产生各种事件,面对各种事件,它提供了一个Callback数组。每个事件执行的时候都会调用Callback函数,所以写JVM TI Client的核心就是放置Callback函数。
正是这种机制允许我们向 JVM 发送指令以加载新的类定义。
Java代理
现在让我们试着想一想:如何神奇地改变应用程序中的方法定义?
这有点像把大象放在冰箱里,然后走几步:
根据字节码的规范生成一个新的类
使用 JVM TI,命令 JVM 将类加载到相应的内存中。
更换后,系统将使用我们的增强方法。
这并不容易,还好jdk为我们准备了这样一个上层接口指令包。它也很容易使用。我们将通过一个简单的agent例子来说明指令包的关键设计。
Javaagent的简单示例
javaagent有两种使用方式:
使用第一种方法的demo
public class PreMainTraceAgent { public static void premain(String agentArgs, Instrumentation inst) { inst.addTransformer(new DefineTransformer(), true); } static class DefineTransformer implements ClassFileTransformer{ @Override public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { System.out.println("premain load Class:" + className); return classfileBuffer; } }}
清单版本:1.0
可以重新定义类:真
可以重新转换类:真
Premain 类:PreMainTraceAgent
然后在resources目录下新建一个目录:META-INF,在这个目录下新建一个文件:MANIFREST.MF:
最后打包成agent.jar包
到了这里,你会发现增强字节码就是这么简单。
字节码生成工具
通过前面的理解,有一种感觉就是修改字节码就是这样^_^!!!但是我们要注意另一个问题,字节是如何产生的?
大佬:熟悉JVM规范,理解每个字节码的含义。我可以手动更改类文件,所以我为此编写了一个库。
专家:我知道客户端的框架,我修改源代码,重新编译,把二进制替换进去。
小白:我看不懂字节码。我可以使用大佬写的库。
下面将介绍几种常用的字节码生成工具
ASM
ASM 是一个纯字节码生成和分析框架。它具有完整的语法分析、语义分析,可用于动态生成类字节码。不过,这个工具还是太专业了。用户必须非常了解 JVM 规范,并且必须确切地知道应该在类文件中进行哪些更改以替换函数。ASM 提供了两组 API:
如果你对字节码和JVM内存模型有初步的了解,你可以根据官方文档简单的生成类。
ASM 十分强大,被应用于 <br /> 1. OpenJDK的 lambda语法 <br /> 2. Groovy 和 Koltin 的编译器 <br /> 3. 测试覆盖率统计工具 Cobertura 和 Jacoco <br /> 4. 单测 mock 工具,比如 Mockito 和 EasyMock <br /> 5. CGLIB ,ByteBuddy 这些动态类生成工具。
字节好友
ByteBuddy 是一款优秀的运行时字节码生成工具,基于 ASM 实现,提供更易用的 API。许多分布式监控项目(如 Skywalking、Datadog 等)使用它作为 Java 应用程序的探针以 采集 监控信息。
下面是与其他工具的性能比较。
在我们实际使用中,ByteBuddy的API真的很友好,基本满足了所有字节码增强需求:接口、类、方法、静态方法、构造方法、注解等的修改。另外,内置的Matcher接口支持模糊匹配,并且您可以根据名称匹配修改符合条件的类型。
但也有不足之处。官方文件比较陈旧,中文文件很少。很多重要的特性,比如切面等,没有详细介绍,经常需要阅读代码注释和测试用例才能理解真正的含义。如果你对ByteBuddy感兴趣,可以关注我们的公众号,下面文章将对ByteBuddy做专题分享。
跟踪数据的生成
通过字节码增强,我们可以实现非侵入式埋葬,那么与trace的生成逻辑的关联就可以看作是灵魂注入。下面我们用一个简单的例子来说明这样的组合是如何完成的。
示踪剂 API
这是一个用于生成跟踪消息的简单 API。
public class Tracer { public static Tracer newTracer() { return new Tracer(); } public Span newSpan() { return new Span(); } public static class Span { public void start() { System.out.println("start a span"); } public void end() { System.out.println("span finish"); // todo: save span in db } }}
只有一种方法 sayHello(String name) 目标类 Greeting
public class Greeting { public static void sayHello(String name) { System.out.println("Hi! " + name); }}
手动生成trace消息,需要在方法前后添加手动埋点
... public static void main(String[] args) { Tracer tracer = Tracer.newTracer(); // 生成新的span Tracer.Span span = tracer.newSpan(); // span 的开始与结束 span.start(); Greeting.sayHello("developer"); span.end();}...
无侵入埋点
字节增强允许我们不修改源代码。现在我们可以定义一个简单的aspect,将span生成逻辑放入aspect中,然后使用Bytebuddy植入埋点。
跟踪建议
将跟踪生成逻辑放入切面
public class TraceAdvice { public static Tracer.Span span = null; public static void getCurrentSpan() { if (span == null) { span = Tracer.newTracer().newSpan(); } } /** * @param target 目标类实例 * @param clazz 目标类class * @param method 目标方法 * @param args 目标方法参数 */ @Advice.OnMethodEnter public static void onMethodEnter(@Advice.This(optional = true) Object target, @Advice.Origin Class clazz, @Advice.Origin Method method, @Advice.AllArguments Object[] args) { getCurrentSpan(); span.start(); } /** * @param target 目标类实例 * @param clazz 目标类class * @param method 目标方法 * @param args 目标方法参数 * @param result 返回结果 */ @Advice.OnMethodExit(onThrowable = Throwable.class) public static void onMethodExit(@Advice.This(optional = true) Object target, @Advice.Origin Class clazz, @Advice.Origin Method method, @Advice.AllArguments Object[] args, @Advice.Return(typing = Assigner.Typing.DYNAMIC) Object result) { span.end(); span = null; }}
onMethodEnter:方法进入时调用。Bytebuddy 提供了一系列注解,带有@Advice.OnMethodExit 的静态方法,可以插入到方法开始的节点中。我们可以获取方法的详细信息,甚至可以修改传入的参数以跳过目标方法的执行。
OnMethodExit:方法结束时调用。类似于onMethodEnter,但可以捕获方法体抛出的异常并修改返回值。
植入建议
将 Javaagent 获得的 Instrumentation 句柄传递给 AgentBuilder(Bytebuddy 的 API)
public class PreMainTraceAgent { public static void premain(String agentArgs, Instrumentation inst) { // Bytebuddy 的 API 用来修改 AgentBuilder agentBuilder = new AgentBuilder.Default() .with(AgentBuilder.PoolStrategy.Default.EXTENDED) .with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE) .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION) .with(new WeaveListener()) .disableClassFormatChanges(); agentBuilder = agentBuilder // 匹配目标类的全类名 .type(ElementMatchers.named("baidu.bms.debug.Greeting")) .transform(new AgentBuilder.Transformer() { @Override public DynamicType.Builder transform(DynamicType.Builder builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) { return builder.visit( // 织入切面 Advice.to(TraceAdvice.class) // 匹配目标类的方法 .on(ElementMatchers.named("sayHello")) ); } }); agentBuilder.installOn(inst); } // 本地启动 public static void main(String[] args) throws Exception { ByteBuddyAgent.install(); Instrumentation inst = ByteBuddyAgent.getInstrumentation(); // 增强 premain(null, inst); // 调用 Class greetingType = Greeting.class. getClassLoader().loadClass(Greeting.class.getName()); Method sayHello = greetingType.getDeclaredMethod("sayHello", String.class); sayHello.invoke(null, "developer"); }
除了制作agent.jar,我们可以在本地调试的时候在main函数中启动,如上所示。本地调试
打印结果
WeaveListener onTransformation : baidu.bms.debug.Greetingstart a spanHi! developerspan finishDisconnected from the target VM, address: '127.0.0.1:61646', transport: 'socket'
如您所见,我们在目标方法之前和之后添加了跟踪生成逻辑。
在实际业务中,我们往往只需要捕获应用程序使用的帧,比如Spring的RestTemplate方法,就可以获取准确的Http方法调用信息。这种依靠这种字节码增强的方式,最大程度地实现了与业务的解耦。
还有什么?
在实际业务中,我们也积累了很多踩坑的经验:
1、有没有好的探针框架可以让我“哼哼哼”地写业务?
2、如何实现无意义的热升级,让用户在产品上轻松设置埋点?
3. ByteBuddy如何使用,切面的注解是什么意思?
4、Javaagent+Istio如何让Dubbo微服务治理框架毫无意义地迁移到ServiceMesh?
解密:渗透测试之信息收集
港口信息采集
端口作为服务器和客户端之间的接口,起着非常重要的作用。
一些常用端口标识服务器启用了哪些功能。常见的135、137、138、139、445,这些端口经常会爆发漏洞。以下是部分服务端口的漏洞。
扫描端口,可以使用 Nmap 和 masscan 进行扫描和检测。采集尽可能多的开放端口和对应的服务版本。得到准确的服务版本后,可以搜索对应版本的漏洞。
nmap 扫描的精度很高,但是扫描速度比较慢。
Masscan 扫描速度更快,但准确性较低。
Nmap -O 192.168.1.1
( )
侧站C段查询
侧站信息:侧站是与目标网站在同一服务器上的另一个网站。如果主站无法获取权限,可以将侧站作为攻击入口,然后想办法穿越服务器。到真正的目标站点目录。
C段:C段是与目标机器ip在同一C段的其他机器;
通过目标所在的C段中的任何其他机器,找到一种方法可以穿越到我们的目标机器。对于红蓝对抗和网保,C段扫描更有意义。
但是对于单独的 网站 渗透测试,C 段扫描几乎没有意义。
每个 IP 有四个段 ABCD。比如192.168.0.1,segment A是192,segment B是168,segment C是0,segment D是1,segment C嗅探就是拿下同C网段中的一台服务器,也就是,段 D 1-255 中的服务器,然后使用工具嗅探来关闭服务器。
目录扫描
由于发布网站时服务器配置问题,可以打开目录浏览器,造成信息泄露和安全隐患。
在信息采集过程中,需要采集的敏感目录/文件包括:
提到网站敏感目录时,要注意robots.txt文件。
robots.txt 文件是专门为搜索引擎机器人编写的纯文本文件。我们可以在 网站 中指定我们不想被该文件中的机器人访问的目录。
这样我们的网站内容可以部分或全部从搜索引擎收录中排除,或者搜索引擎只能收录指定内容。
因此,我们可以使用robots.txt来阻止Google的机器人访问我们网站上的重要文件,GoogleHack的威胁是不存在的。
如果 robots.txt 文件内容如下:
用户代理: *
禁止:/数据/
禁止:/db/
禁止:/admin/
禁止:/经理/
“Disallow”参数后面是禁止robot收录部分的路径,
比如我们想让机器人封禁收录网站目录下的“data”文件夹,
只需在 Disallow 参数后添加 /data/ 即可。
如果要添加其他目录,只需继续以这种格式添加即可。
文件写入后,将其上传到 网站 的根目录,以使 网站 远离 Google Hack。
尽管 robots 文件的目的是防止搜索蜘蛛想要爬取它们想要保护的页面,
但是如果我们知道robots文件的内容,就可以知道目标网站那些文件夹是不允许访问的。从侧面看,这些文件夹非常重要。
检测目标网站后端目录的工具:
网站指纹识别
在渗透测试中,对目标服务器进行指纹识别是非常有必要的,因为只有识别出对应的Web容器或者cms,才能找到相关的漏洞,然后才能进行对应的渗透操作。
cms又称全站系统。
常见的cms有:WordPress、Dedecms、Discuz、PhpWeb、PhpWind、Dvbbs、Phpcms、ECShop、、SiteWeaver、Aspcms、Empire、Z-Blog等待。
在线指纹网站:
内容敏感信息泄露
我们使用谷歌语法查找敏感信息
搜索文件
查找参数
找到登录点:
找到目录:
寻找重要的东西:
Github 信息泄露
Github 不仅可以托管代码,还可以搜索代码。在上传和发布代码时,一些敏感的配置信息文件会被公开。
Github 主要合集:
网站架构
但是要确定目标网站服务器的具体版本,可以使用nmap扫描,-O和-A参数可以扫描。
1、Access的全称是Microsoft Office Access,是微软公司发布的关联数据库管理系统。
对于小型数据库,当数据库达到 100M 左右时,性能会下降。数据库后缀:.mdb 一般是使用access数据库的asp网页文件
2、SQL Server是微软开发和推广的关系数据库管理系统(DBMS),是一个比较大的数据库。端口号为1433。数据库后缀.mdf
3.MySQL是关系型数据库管理系统,由瑞典MySQL AB公司开发,目前是Oracle的产品。
MySQL 是最流行的关系数据库管理系统。MySQL是WEB应用中最好的应用软件之一。大多数 MySQL 数据库都是 php 页面。默认端口为 3306
4.Oracle又称Oracle RDBMS,简称Oracle。
它是 Oracle Corporation 的关系数据库管理系统。通常用于较大的 网站。默认端口为 1521
首先是成本的区别,访问是免费的,mysql也是开源的,sql server一般要几千,Oracle要几万。
其次,处理能力,access支持千级以内的访问,sql server支持千级到几万级的访问,Oracle支持海量访问。
再次,从数据库规模来看,access是小型数据库,mysql是中小型数据库,sql server是中型数据库,oracle是大型数据库。
知道了这些信息后,我们需要知道 网站 使用的是什么类型的 web 服务器:Apache、Nginx、Tomcat 或 IIS。
在知道了web服务器是什么类型之后,我们还需要检测web服务器的具体版本。
比如Ngnix版本
1.可以根据网站URL判断
2. 站点:xxx 文件类型:php
3.可以根据火狐浏览器的插件来判断
教学具体工具 SubDomainsBrute subDomainsBrute 的特点 subDomainsBrute 安装
1.首先,你的电脑需要有python环境。如果没有,可以按照下面的链接下载。这里推荐使用python2.7.10。
python2.7.10下载地址
或者下载首页也可以下载python2.7.10按照以上提示步骤依次安装。
安装后,需要添加环境变量。
2.下载subDomainsBrute到python根目录。下载地址如下:
subDomainsBrute 下载地址:
3.检查python27文件夹下是否有Script文件夹
里面有一些easy_install相关的内容,直接安装setuptools可以自动生成Script文件夹。
下载 ez_setup.py 并在 cmd 中运行。
进入命令行,然后将目录切换到python安装目录下的Script文件夹,运行python ez_setup.py生成scripts文件夹。
4、在Script文件所在路径下输入cmd,在调用的命令行中安装需要的库。您可以使用 pip 直接安装它。命令是 pip install dnspython gevent
子域Brutez 使用
Usage: subDomainsBrute.py [options] target.com<br />Options:<br /> --version show program's version number and exit<br /> -h, --help show this help message and exit<br /> -f FILE File contains new line delimited subs, default is<br /> subnames.txt.<br /> --full Full scan, NAMES FILE subnames_full.txt will be used<br /> to brute<br /> -i, --ignore-intranet<br /> Ignore domains pointed to private IPs<br /> -t THREADS, --threads=THREADS<br /> Num of scan threads, 200 by default<br /> -p PROCESS, --process=PROCESS<br /> Num of scan Process, 6 by default<br /> -o OUTPUT, --output=OUTPUT<br /> Output file name. default is {target}.txt
层子域挖掘器
Layer子域矿机是一款域名查询工具,提供网站子域查询服务;
界面简洁,操作方式简单,支持服务接口、暴力搜索、同服挖矿三种模式,支持打开网站、复制域名、复制IP、复制CDN、导出域名、导出IP ,导出域名+IP,导出域名+IP+WEB服务器,导出生存网站!
使用说明
列出百度下的子域
网站使用后台扫描工具御剑
御剑是一款好用的网站后台扫描工具,带有图形化页面,易于使用。
我们使用Edgeworth Scanner,主要是扫描网站敏感目录,包括网站backends等。
扫描原理也是爆破,就是通过敏感目录的字典进行匹配。
在御剑后台扫描之前,爬虫会访问robots txt文件。
工具介绍
御剑安装使用
1.下载解压后双击打开软件。
2、打开后在域名输入框中输入要扫描的后台地址。
3. 在下面的选项中,您可以选择扫描线程、扫描超时和文件类型。
4.全部选好后,就可以点击开始扫描了。
5. 等待扫描后,下方会出现扫描结果。
御剑的使用非常简单,简单配置后即可进行扫描,但缺点是御剑无法导出扫描报告。
当然御剑也有很多版本,功能略有不同,比如指纹识别、后台扫描、获取真实IP、检测注入等。
使用 dirbuster
工具介绍
dirbuster的安装和使用
1. 下载最新版本的 DirBuster。解压后在Windows中双击DirBuster-0.12.jar,或者直接打开kali自带的DirBuster(命令:root@kali:~# dirbuster)。
2、启动DirBuser后,主界面如下:
注意:如果您的扫描目标是,
然后在URL中填写“/admin/{dir}”进行fuzz,
表示可以在“{dir}”前后拼接任何你想要的目录或后缀,
例如输入“:/admin/{dir}.php”表示扫描admin目录下的所有php文件。
回顾过去的内容
扫码获取卖淫视频+工具+群访问+靶场等资料
扫描二维码免费卖淫!
还有免费匹配的靶场和交流群!