HGAME 2024 WEEK 1(复现)

HGAME 2024 WEEK 1 (crypto/re复现)

CRYPTO

ezRSA

解题分析:

leak1=p

leak2=q

(自己一顿复杂的推理得出来的,可能写的比较啰嗦)

考察的是费马小定理:如果p是一个质数,而整数a不是p的倍数,则有a^(p-1)≡1(mod p)

在这里插入图片描述

代码:

from Crypto.Util.number import *
from gmpy2 import *

leak1=149127170073611271968182576751290331559018441805725310426095412837589227670757540743929865853650399839102838431507200744724939659463200158012469676979987696419050900842798225665861812331113632892438742724202916416060266581590169063867688299288985734104127632232175657352697898383441323477450658179727728908669
leak2=116122992714670915381309916967490436489020001172880644167179915467021794892927977272080596641785569119134259037522388335198043152206150259103485574558816424740204736215551933482583941959994625356581201054534529395781744338631021423703171146456663432955843598548122593308782245220792018716508538497402576709461
c=10529481867532520034258056773864074017027019578041866245400647840230251661652999709715919620810933437191661180003295923273655675729588558899592524235622728816065501918076120812236580344991140980991532347991252705288633014913479970610056845543523591324177567061948922552275235486615514913932125436543991642607028689762693617305246716492783116813070355512606971626645594961850567586340389705821314842096465631886812281289843132258131809773797777049358789182212570606252509790830994263132020094153646296793522975632191912463919898988349282284972919932761952603379733234575351624039162440021940592552768579639977713099971
n=leak1*leak2
phi=(leak1-1)*(leak2-1)
e=0x10001
d=invert(e,phi)
m=powmod(c,d,n)
print(long_to_bytes(m))

答案:hgame{F3rmat_l1tt1e_the0rem_is_th3_bas1s}

ezMath

题目:

from Crypto.Util.number import *
from Crypto.Cipher import AES
import random,string
from secret import flag,y,x

def pad(x):
    return x+b'\x00'*(16-len(x)%16)#ZeroPadding填充
def encrypt(KEY):
    cipher= AES.new(KEY,AES.MODE_ECB)# AES的ECB模式 加密,创建了一个 cipher对象
    encrypted =cipher.encrypt(flag)#加密flag
    return encrypted
D = 114514
assert x**2 - D * y**2 == 1
flag=pad(flag)#flag进行填充,不足16位填充至16位
key=pad(long_to_bytes(y))[:16]#密钥取y的前16位,bytes型,同样不足16位进行填充
enc=encrypt(key)#AES加密
print(f'enc={enc}')
#enc=b"\xce\xf1\x94\x84\xe9m\x88\x04\xcb\x9ad\x9e\x08b\xbf\x8b\xd3\r\xe2\x81\x17g\x9c\xd7\x10\x19\x1a\xa6\xc3\x9d\xde\xe7\xe0h\xed/\x00\x95tz)1\\\t8:\xb1,U\xfe\xdec\xf2h\xab`\xe5'\x93\xf8\xde\xb2\x9a\x9a"

AES加密:对称加密(加密与解密使用的秘钥是一个)

考察的主要是求解pell方程,可以用连分数法求解

这里用sagemath进行求解(它里面有相关的集成运算很方便),注意num的取值,小了的话得不到答案

sagemath在线环境: https://sagecell.sagemath.org/

#sage
def solve_pell(d, num = 10000000):
    cf = continued_fraction(sqrt(d))
    for i in range(num):
        x = cf.numerator(i)#分子
        y = cf.denominator(i)#分母
        if x^2 - d * y^2 == 1:
            return x, y
    return None, None

D = 114514 
solve_pell(D)
#(3058389164815894335086675882217709431950420307140756009821362546111334285928768064662409120517323199,9037815138660369922198555785216162916412331641365948545459353586895717702576049626533527779108680)

有了x,y直接根据AES加密代码解密即可

def pad(x):
    return x+b'\x00'*(16-len(x)%16)#ZeroPadding填充
def decrypt(KEY):
    cipher= AES.new(KEY,AES.MODE_ECB)# AES的ECB模式 加密,创建了一个 cipher对象
    decrypted =cipher.decrypt(enc)#解密flag
    return decrypted

D = 114514
x=3058389164815894335086675882217709431950420307140756009821362546111334285928768064662409120517323199
y=9037815138660369922198555785216162916412331641365948545459353586895717702576049626533527779108680
enc=b"\xce\xf1\x94\x84\xe9m\x88\x04\xcb\x9ad\x9e\x08b\xbf\x8b\xd3\r\xe2\x81\x17g\x9c\xd7\x10\x19\x1a\xa6\xc3\x9d\xde\xe7\xe0h\xed/\x00\x95tz)1\\\t8:\xb1,U\xfe\xdec\xf2h\xab`\xe5'\x93\xf8\xde\xb2\x9a\x9a"

key=pad(long_to_bytes(y))[:16]#密钥取y的前16位,bytes型,同样不足16位进行填充
flag=decrypt(key)#AES解密
print(f'flag={flag}')

解密代码:(下面是直接通过连分数来求解x,y的,但是说实话还是不太理解)

from Crypto.Util.number import *
from Crypto.Cipher import AES
from gmpy2 import *
from math import *
import random,string
def solve_pell(d):#使用连分数求出最小整数解
    m=floor(sqrt(d))#a0
    r=sqrt(d)
    list_a = []
    list_a.append(m)#a的数组
    n0 = n1 = d - m * m
    m1 = m
    while 1:
        q, m2 = divmod(m1 + m, n1)
        list_a.append(q)
        m1 = -m2 + m
        n1 = (d - m1 * m1) // n1
        if m1 == m and n1 == n0:
            break
    # print(list_a)
    b = 1
    c = 0
    for i in range(len(list_a) - 2, -1, -1):
        b1 = c + b * list_a[i]
        c = b
        b = b1
    # print(b*b-d*c*c)
    # print(b,c)
    return b,c
def pad(x):
    return x+b'\x00'*(16-len(x)%16)#ZeroPadding填充
def decrypt(KEY):
    cipher= AES.new(KEY,AES.MODE_ECB)# AES的ECB模式 加密,创建了一个 cipher对象
    decrypted =cipher.decrypt(enc)#解密flag
    return decrypted

D = 114514
x,y=solve_pell(D)
# x=3058389164815894335086675882217709431950420307140756009821362546111334285928768064662409120517323199
# y=9037815138660369922198555785216162916412331641365948545459353586895717702576049626533527779108680
enc=b"\xce\xf1\x94\x84\xe9m\x88\x04\xcb\x9ad\x9e\x08b\xbf\x8b\xd3\r\xe2\x81\x17g\x9c\xd7\x10\x19\x1a\xa6\xc3\x9d\xde\xe7\xe0h\xed/\x00\x95tz)1\\\t8:\xb1,U\xfe\xdec\xf2h\xab`\xe5'\x93\xf8\xde\xb2\x9a\x9a"

key=pad(long_to_bytes(y))[:16]#密钥取y的前16位,bytes型,同样不足16位进行填充
flag=decrypt(key)#AES加密
print(f'flag={flag}')

代码来源:https://www.bilibili.com/read/cv24153490/

答案:hgame{G0od!_Yo3_k1ow_C0ntinued_Fra3ti0ns!!!}

参考文章:
AES加密原理: https://blog.csdn.net/chouzhou9701/article/details/122019967

pell方程: https://zhuanlan.zhihu.com/p/365860557
https://blog.csdn.net/TheWayForDream/article/details/113772229

连分数法解pell方程,非常具体: https://blog.csdn.net/wh2124335/article/details/8871535

https://blog.csdn.net/u011815404/article/details/88717125?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-1-88717125-blog-8871535.235%5Ev43%5Epc_blog_bottom_relevance_base1&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-1-88717125-blog-8871535.235%5Ev43%5Epc_blog_bottom_relevance_base1&utm_relevant_index=2

ezPRNG

位运算:>>和 <<都是位运算,对二进制数进行移位操作。
<< 是左移,末位补0,类比十进制数在末尾添0相当于原数乘以10,x<<1是将x的二进制表示左移一位,相当于原数x乘2。比如整数4在二进制下是100,4<<1左移1位变成1000(二进制),结果是8。
是右移,右移1位相当于除以2。
而>>=和<<=,就是对变量进行位运算移位之后的结果再赋值给原来的变量,可以类比赋值运算符+=和-=可以理解。
比如x>>=2, 就是把变量x右移2位,再保留x操作后的值。

异或相同为0,不同为1(a ^ 0 = a)

参考的wp,写的真的好详细: https://blog.csdn.net/jayq1/article/details/136063602

题目:

from Crypto.Util.number import *
import uuid
def PRNG(R,mask):
    nextR = (R << 1) & 0xffffffff#左移一位,即去掉了高一位,并补上一个0
    i=(R&mask)&0xffffffff#只有mask中为1的位置有效(表示R中相应位置是1或者0),其他位置为0
    nextbit=0
    while i!=0:
        nextbit^=(i%2)#nextbit与i的最低位异或
        i=i//2#i舍弃最后一位,即nextbit与i的每一位进行了异或
    nextR^=nextbit #nextR的最后一位为nextbit
    return (nextR,nextbit)

R=str(uuid.uuid4())#生成的随机数,然后转字符串
flag='hgame{'+R+'}'
print(flag)
R=R.replace('-','')#把-删除了
Rlist=[int(R[i*8:i*8+8],16) for i in range(4)]#将R切片,8个为一组转为十进制放入列表中,共32个,列表有4个元素

mask=0b10001001000010000100010010001001
output=[]
for i in range(4):
    R=Rlist[i]
    out=''
    for _ in range(1000):#加密操作
        (R,nextbit)=PRNG(R,mask)
        out+=str(nextbit)
    output.append(out)

print(f'output={output}')
#output=['1111110110111011110000101011010001000111111001111110100101000011110111111100010000111110110111100001001000101101011110111100010010100000011111101101110101011010111000000011110000100011101111011011000100101100110100101110001010001101101110000010001000111100101010010110110111101110011011001011111011010101011000011011000111011011111001101010111100101100110001011010010101110011101001100111000011110111000001101110000001111100000100000101111100010110111001110011010000011011110110011000001101011111111010110011010111010101001000010011110110011110110101011110111010011010010110111111010011101000110101111101111000110011111110010110000100100100101101010101110010101001101010101011110111010011101110000100101111010110101111110001111111110010000000001110011100100001011111110100111011000101001101001110010010001100011000001101000111010010000101101111101011000000101000001110001011001010010001000011000000100010010010010111010011111111011100100100100101111111001110000111110110001111001111100101001001100010', '0010000000001010111100001100011101111101111000100100111010101110010110011001011110101100011101010000001100000110000000011000000110101111111011100100110111011010000100011111000111001000101001110010110010001000110010101011110011101000011111101101011000011110001101011111000110111000011000110011100100101100111100000100100101111001011101110001011011111111011010100010111011000010010101110110100000110100000100010101000010111101001000011000000000111010010101010111101101011111011001000101000100011001100101010110110001010010001010110111011011111101011100111001101111111111010011101111010010011110011111110100110011111110110001000111100010111000101111000011011011111101110101110100111000011100001010110111100011001011010011010111000110101100110100011101101011101000111011000100110110001100110101010110010011011110000111110100111101110000100010000111100010111000010000010001111110110100001000110110100100110110010110111010011111101011110000011101010100110101011110000110101110111011010110110000010000110001', '1110110110010001011100111110111110111001111101010011001111100100001000111001101011010100010111110101110101111010111100101100010011001001011101000101011000110111000010000101001000100111010110001010000111110110111000011001100010001101000010001111111100000101111000100101000000001001001001101110000100111001110001001011010111111010111101101101001110111010111110110011001000010001010100010010110110101011100000101111100100110011110001001001111100101111001111011011010111001001111010001100110001100001100000110000011111010100101111000000101011111010000111110000101111100010000010010111010110100101010101001111100101011100011001001011000101010101001101100010110000010001110011110011100111000110101010111010011010000001100001011000011101101000000011111000101111101011110011000011011000100100110111010011001111101100101100011000101001110101111001000010110010111101110110010101101000000101001011000000001110001110000100000001001111100011010011000000011011101111101001111110001011101100000010001001010011000001', '0001101010101010100001001001100010000101010100001010001000100011101100110001001100001001110000110100010101111010110111001101011011101110000011001000100100101000011011101000111001001010011100010001010110111011100100111110111001010010111010100000100111110101110010010110100001000010010001101111001110100010001011101100111011101011101100100101011010101000101001000101110011011111110110011111111100000000011100000010011000110001000110101010001011000010101000110000101001110101010111011010010111011001010011100010101001100110000110101100010000100110101110100001101001011011110011100110011001010110100101010111110110111100000111010001111101110000000000111011011101000011001010010111001110111000100111011110100101000100011011101100011111000101110110110111111001111000000011100011000010000101001011001101110101000010101001000100110010000101001111100101000001011011010011110001101000001101111010100101001100010100000111000011110101010100011011001110001011110111010111011010101101100000110000001010010101111011']

解题过程:(靠我自己根本写不出来,看了wp之后真的懂了)

首先R是由uuid4生成的随机数,然后把R进行了切片,最后也是最重要的就是加密过程了

def PRNG(R,mask):
    nextR = (R << 1) & 0xffffffff#左移一位,即去掉了高一位,并补上一个0
    i=(R&mask)&0xffffffff#只有mask中为1的位置有效(表示R中相应位置是1或者0),其他位置为0
    nextbit=0
    while i!=0:
        nextbit^=(i%2)#nextbit与i的最低位异或
        i=i//2#i舍弃最后一位,即nextbit与i的每一位进行了异或
    nextR^=nextbit #nextR的最后一位为nextbit
    return (nextR,nextbit)

R左移一位(即丢弃了最高位,最低位补0),& 0xffffffff作用是限制位数,保证位数一直是32位(可以自己编写代码简单测试一下)

R&mark那么结果中只有mark为1的位置是根据R的值来确定的(即有效位),而mark为0的位置一定为0

而nextbit的值是i的每一位进行异或的结果,即i上的mark有效位的值的异或(a^0=a,异或不分顺序,所以0的异或可以忽略不计)

最后nextR=nextR^nextbit即把nextR的最后一位变为nextbit的值

根据上面的分析可以知道

在这里插入图片描述

我们对R的第32位进行猜测(总共就两种可能,不是0就是1),然后根据R’的有效位的值异或结果与检验位(即nextbit的后一位)进行比较,相等则说明猜测正确,否则为另一个值

根据分析写出代码即可,其中得到答案fbbbee823f434f919337907880e4191a,但这不是最后的答案,题目中对于原始的R还进行了一个替换‘_’被删除了

这就要提到uuid4所生成的随机数有一定的格式,这里我尝试生成了10次

import uuid
for i in range(10):
    print(uuid.uuid4())
#e0b6e3e8-000a-44b1-a3d4-7612fe75760c
#20c3b641-eaa4-4bfb-9360-a2ecd2be7835
#a3486b4a-995d-436a-b1f1-c3130c10f818
#c011a1f4-3565-4c0b-ac30-5c1fe81fa595
#40413971-bcc1-4d5c-94dc-249eb2fe67c6
#fc5fa159-cecf-4f08-a79c-2801f5ac349e
#41b0adc6-599c-478b-b7d8-eaf154915ff4
#001989d8-0222-480f-88a6-8aaeaaca80b5
#2806efef-26c4-477d-bb55-8be19f8d5cb3
#c06b061b-8a8e-4dbf-8ad8-75717cd9be88

可以看出是有固定格式的,所以根据格式可以直接解出flag

python的一些函数

join()函数

语法: ‘sep’.join(seq)

参数说明
sep:分隔符。可以为空
seq:要连接的元素序列、字符串、元组、字典
上面的语法即:以sep作为分隔符,将seq所有的元素合并成一个新的字符串

返回值:返回一个以分隔符sep连接各个元素后生成的字符串

insert()函数用于将指定对象插入列表的指定位置

list.insert(index, obj)

  • index:对象obj需要插入的索引位置
  • obj:要插入列表中的对象

解密代码:

from Crypto.Util.number import *
# 首先针对mask提取出相与有效位1
# mask=0b10001001000010000100010010001001
mask='10001001000010000100010010001001'
for i in range(len(mask)):
    if mask[i]=='1':
        print(i,end=',')
# 0,4,7,12,17,21,24,28,31,有效位

#然后对R进行恢复
output=['1111110110111011110000101011010001000111111001111110100101000011110111111100010000111110110111100001001000101101011110111100010010100000011111101101110101011010111000000011110000100011101111011011000100101100110100101110001010001101101110000010001000111100101010010110110111101110011011001011111011010101011000011011000111011011111001101010111100101100110001011010010101110011101001100111000011110111000001101110000001111100000100000101111100010110111001110011010000011011110110011000001101011111111010110011010111010101001000010011110110011110110101011110111010011010010110111111010011101000110101111101111000110011111110010110000100100100101101010101110010101001101010101011110111010011101110000100101111010110101111110001111111110010000000001110011100100001011111110100111011000101001101001110010010001100011000001101000111010010000101101111101011000000101000001110001011001010010001000011000000100010010010010111010011111111011100100100100101111111001110000111110110001111001111100101001001100010', '0010000000001010111100001100011101111101111000100100111010101110010110011001011110101100011101010000001100000110000000011000000110101111111011100100110111011010000100011111000111001000101001110010110010001000110010101011110011101000011111101101011000011110001101011111000110111000011000110011100100101100111100000100100101111001011101110001011011111111011010100010111011000010010101110110100000110100000100010101000010111101001000011000000000111010010101010111101101011111011001000101000100011001100101010110110001010010001010110111011011111101011100111001101111111111010011101111010010011110011111110100110011111110110001000111100010111000101111000011011011111101110101110100111000011100001010110111100011001011010011010111000110101100110100011101101011101000111011000100110110001100110101010110010011011110000111110100111101110000100010000111100010111000010000010001111110110100001000110110100100110110010110111010011111101011110000011101010100110101011110000110101110111011010110110000010000110001', '1110110110010001011100111110111110111001111101010011001111100100001000111001101011010100010111110101110101111010111100101100010011001001011101000101011000110111000010000101001000100111010110001010000111110110111000011001100010001101000010001111111100000101111000100101000000001001001001101110000100111001110001001011010111111010111101101101001110111010111110110011001000010001010100010010110110101011100000101111100100110011110001001001111100101111001111011011010111001001111010001100110001100001100000110000011111010100101111000000101011111010000111110000101111100010000010010111010110100101010101001111100101011100011001001011000101010101001101100010110000010001110011110011100111000110101010111010011010000001100001011000011101101000000011111000101111101011110011000011011000100100110111010011001111101100101100011000101001110101111001000010110010111101110110010101101000000101001011000000001110001110000100000001001111100011010011000000011011101111101001111110001011101100000010001001010011000001', '0001101010101010100001001001100010000101010100001010001000100011101100110001001100001001110000110100010101111010110111001101011011101110000011001000100100101000011011101000111001001010011100010001010110111011100100111110111001010010111010100000100111110101110010010110100001000010010001101111001110100010001011101100111011101011101100100101011010101000101001000101110011011111110110011111111100000000011100000010011000110001000110101010001011000010101000110000101001110101010111011010010111011001010011100010101001100110000110101100010000100110101110100001101001011011110011100110011001010110100101010111110110111100000111010001111101110000000000111011011101000011001010010111001110111000100111011110100101000100011011101100011111000101110110110111111001111000000011100011000010000101001011001101110101000010101001000100110010000101001111100101000001011011010011110001101000001101111010100101001100010100000111000011110101010100011011001110001011110111010111011010101101100000110000001010010101111011']

flag=''
for i in range(4):
    nextbit=output[i]
    R = []#列表方便插入,因为我们需要从头部插入,推结果的时候是倒着推的
    for _ in range(32):#每次求解1bit,一共32bit 因为与0xffffffff 为限制位数的作用
        temp='0'+''.join(R)+nextbit[:32-1-len(R)]#总共32位,第一个是猜测位为0,第二部分是已知R位(方便推导前面的bit),第三部分是nextbit填充位
        #猜测检验
        if (int(temp[0])^int(temp[4])^int(temp[7])^int(temp[12])^int(temp[17])^int(temp[21])^int(temp[24])^int(temp[28])^int(temp[31])
                == int(nextbit[32-1-len(R)])):#nextbit的值(str型)要转为int型,有效位的异或会等于nextbits校验位的值
            R.insert(0,'0')#猜测正确在首位填充0
            #insert(a,b) a为索引位置,b为插入对象
        else:
            R.insert(0,'1')#否则填充1
    R=''.join(R)#第一段求完转为字符串
    R=hex(int(R,2))[2:] # 二进制转十进制 转16进制,[2:] 是因为十六进制有0x前缀,所以从索引位置为2的地方开始
    flag+=R
print(flag)
# fbbbee823f434f919337907880e4191a

答案:hgame{fbbbee82-3f43-4f91-9337-907880e4191a}

奇怪的图片

题目:

import time

from PIL import Image, ImageDraw, ImageFont
import threading
import random
import secrets


flag = "hgame{fake_flag}"


def generate_random_image(width, height):#生成指定宽度和高度的随机颜色图像
    image = Image.new("RGB", (width, height), "white")
    pixels = image.load()#获取像素数据,然后可以使用pixels[x, y]来访问和操作图像中的单个像素
    for x in range(width):
        for y in range(height):
            red = random.randint(0, 255)
            green = random.randint(0, 255)
            blue = random.randint(0, 255)
            pixels[x, y] = (red, green, blue)
    return image


def draw_text(image, width, height, token):#在给定图像上随机位置绘制一个文本标志
    font_size = random.randint(16, 40)#字体大小
    font = ImageFont.truetype("arial.ttf", font_size)#字体类型
    text_color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))#文本颜色
    x = random.randint(0, width - font_size * len(token))
    y = random.randint(0, height - font_size)
    draw = ImageDraw.Draw(image)# 创建一个ImageDraw对象,用于在图像上绘制
    draw.text((x, y), token, font=font, fill=text_color)#在图像上的 (x, y) 坐标处绘制给定的文本标志 token,使用指定的字体、颜色进行填充。 同时x和y的操作 保证随机坐标一定在图像内部
    return image


def xor_images(image1, image2):#图像异或
    if image1.size != image2.size:#大小要一致
        raise ValueError("Images must have the same dimensions.")
    xor_image = Image.new("RGB", image1.size)
    pixels1 = image1.load()
    pixels2 = image2.load()
    xor_pixels = xor_image.load()
    for x in range(image1.size[0]):
        for y in range(image1.size[1]):
            r1, g1, b1 = pixels1[x, y]
            r2, g2, b2 = pixels2[x, y]
            xor_pixels[x, y] = (r1 ^ r2, g1 ^ g2, b1 ^ b2)#三个值分别异或
    return xor_image


def generate_unique_strings(n, length):#生成一定数量的指定长度的唯一随机字符串
    unique_strings = set()#集合对象
    while len(unique_strings) < n:
        random_string = secrets.token_hex(length // 2)
        unique_strings.add(random_string)
    return list(unique_strings)

#给图像命名 图片个数就是flag的字符数
random_strings = generate_unique_strings(len(flag), 8)


current_image = generate_random_image(120, 80)
key_image = generate_random_image(120, 80)

def random_time(image, name):
    time.sleep(random.random())
    image.save(".\\png_out\\{}.png".format(name))

for i in range(len(flag)):
    current_image = draw_text(current_image, 120, 80, flag[i])#flag分别写入了每个图片中
    threading.Thread(target=random_time, args=(xor_images(current_image, key_image), random_strings[i])).start()#当调用start()方法时,新线程将开始执行random_time函数,并以xor_images(current_image, key_image)和random_strings[i]作为参数。

根据对代码的分析:首先生成了current_image和key_image,然后循环依次写入flag前i个字符进current_image,然后再把current_image和key_image进行异或,最后输出了得到的图片

总共21张图片,说明flag的长度为21位,我们直接取一张图片和其他图片异或,然后就可以得到写入了flag的21张图片,手动排序即可得到答案。

我们在使用第一张图片与其他图片异或后得到的结果如下:

在这里插入图片描述

我们可以看到含有{的图片有5个,说明原图片上的字符正好是hgame{,只有这个与其他异或能够得到5个含{的图片,剩下的我们直接手动排序即可(含有{的图片可以不去看,里面包含的信息我们已知为hgame{,此外需要注意的是因为图片的异或,相同的相素点会消失)
在这里插入图片描述

解题代码:

from PIL import Image
import os

def xor_images(image1, image2):#图像异或
    if image1.size != image2.size:#大小要一致
        raise ValueError("Images must have the same dimensions.")
    xor_image = Image.new("RGB", image1.size)
    pixels1 = image1.load()
    pixels2 = image2.load()
    xor_pixels = xor_image.load()
    for x in range(image1.size[0]):
        for y in range(image1.size[1]):
            r1, g1, b1 = pixels1[x, y]
            r2, g2, b2 = pixels2[x, y]
            xor_pixels[x, y] = (r1 ^ r2, g1 ^ g2, b1 ^ b2)#三个值分别异或
    return xor_image
def list_files(path):
    a=[]
    for root, dirs, files in os.walk(path):#文件夹路径,文件夹名字,文件名
        for i in range(len(files)):
            a.append(os.path.join(root,files[i]))
    return a
path='E:\Crypto\png_out'#要提取的文件地址
png_list=list_files(path)
print(len(png_list))
c1=Image.open(png_list[0])
for i in range(len(png_list)):
    c2=Image.open(png_list[i])
    xor_images(c1,c2).save("E:\Crypto\png_in\\image_{}.png".format(i))

答案:hgame{1adf_17eb_803c}

参考wp: https://blog.csdn.net/qq_42880719/article/details/136053063

Reverse

ezUPX

解题分析:
根据题目可以知道是upx加壳,直接解壳,分析代码即可(代码比较简单,异或)
解题代码:

#include<stdio.h>
int main()
{
	int data[] =
{
  100, 123, 118, 115,  96,  73, 101,  93,  69,  19, 
  107,   2,  71, 109,  89,  92,   2,  69, 109,   6, 
  109,  94,   3,  70,  70,  94,   1, 109,   2,  84, 
  109, 103,  98, 106,  19,  79,  50
};
	int  flag[37];
	for ( int i = 0;i<=37; ++i ){
  		flag[i]=(data[i] ^ 0x32);
		printf("%c",flag[i]);
  	}
  	
    return 0;
}

答案:VIDAR{Wow!Y0u_kn0w_4_l1ttl3_0f_UPX!}

ezIDA

ida打开就看到答案了

答案:hgame{W3lc0me_T0_Th3_World_of_Rev3rse!}

ezASM

解题分析:
主要考的应该是汇编语言的掌握,但是我学的不咋地,直接扔给了chat GPT分析知道了大致操作,写出代码(会去恶补汇编知识的,要命)

代码:

#include<stdio.h>
int main()
{
	int data[] =
{
  74, 69, 67, 79, 71, 89, 99, 113, 111, 125, 107, 81, 125, 107, 79, 82, 18, 80, 86, 22, 76, 86, 125, 22, 125, 112, 71, 84, 17, 80, 81, 17, 95, 34
};
	int  flag[33];
	for ( int i = 0;i<33; ++i ){
  		flag[i]=(data[i] ^ 0x22);
		printf("%c",flag[i]);
  	}
  	
    return 0;
}

答案:hgame{ASM_Is_Imp0rt4nt_4_Rev3rs3}

ezPYC

这个我看了wp,但是下载了pyinstxtractor.py这个没办法从exe文件中获取pyc文件,不知道为啥,先放一下,我后面补上


这里是分界线(我回来了,终于知道为啥了,是因为我的python版本和给的exe文件python版本不同,所以不行,这里需要改一下python版本)

根据题目可以知道是python逆向,但是给的是exe文件,需要先从里面提取出pyc文件,通过pyinstxtractor.py进行提取即可

下载地址: extremecoders-re/pyinstxtractor: PyInstaller Extractor (github.com)

但是但是,这里我把exPYC.exe和pyinstxtractor.py文件置于同一文件夹内,在该路径内打开cmd

然后输入命令:python pyinstxtractor.py exPYC.exe没反应,离谱了,上网查也没查到什么(困惑了好久,不明白为啥自己不行),突然想起之前学长叫我们python逆向最好下载一个Anaconda进行python环境的匹配(和给的exe文件python版本一致),于是跑去下载了

Anaconda下载和安装: https://blog.csdn.net/fan18317517352/article/details/123035625

不过下载的时候最好下载新一点的版本,我因为下载的版本太老了,也不行,于是又删掉了重新下(说多了都是泪)

下载完之后先检测一下自己配置好了没
在这里插入图片描述

这样子就是配置好了,此外如果版本不太行的话可以升级一下

conda update conda  更新至最新版本,也会更新其它相关包
conda update --all  更新所有包
conda create -n xxx python=3.7 创建一个python3.7 名为xxx的虚拟环境
conda remove -n xxx --all 移除环境
conda info --env 或者 conda env list 查看当前有几个环境
conda activate python35 切换环境

然后我们创建一个匹配exe文件的python环境(我这是已经创建好了的)

在这里插入图片描述

然后切换到需要的环境即可(下面是切换成功的)

在这里插入图片描述

然后就正常操作即可,输入命令:python pyinstxtractor.py ezPYC.exe

在这里插入图片描述

然后在ezPYC.exe_extracted文件夹中找到ezPYC.pyc文件

得到了pyc文件还没结束,我们需要反编译pyc文件获取源码,这里直接在cmd中输入pycdc ezPYC.pyc是不行的,我们需要下载pycdc项目

指路一个关于python反编译的博客,嘎嘎详细

那我这边的话因为没下载,偷个懒直接在线网站反编译了

pyc反编译

源码是一个很简单的异或加密,对应写出解密代码即可

解密代码:

flag = [
    87,
    75,
    71,
    69,
    83,
    121,
    83,
    125,
    117,
    106,
    108,
    106,
    94,
    80,
    48,
    114,
    100,
    112,
    112,
    55,
    94,
    51,
    112,
    91,
    48,
    108,
    119,
    97,
    115,
    49,
    112,
    112,
    48,
    108,
    100,
    37,
    124,
    2]
c = [
    1,
    2,
    3,
    4]
for i in range(0, 36, 1):
    print(chr(flag[i]^ c[i % 4]),end='')

答案:VIDAR{Python_R3vers3_1s_1nter3st1ng!}

  • 28
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值