Hgame2023 Reverse
Hgame 2023
[HGAME 2023 week1]test your IDA
用ida打开即可
[HGAME 2023 week1]encode
查壳
32位windows
加密函数
将输入的字节转高位和低位进行加密,后与byte_403000进行比较
解密脚本:
这里采取了爆破
enc = [8, 6, 7, 6, 1, 6, 13, 6, 5, 6, 11, 7, 5, 6, 14, 6, 3, 6, 15, 6, 4, 6, 5, 6, 15, 5, 9, 6, 3, 7, 15, 5, 5, 6, 1, 6, 3, 7, 9, 7, 15, 5, 6, 6, 15, 6, 2, 7, 15, 5, 1, 6, 15, 5, 2, 7, 5, 6, 6, 7, 5, 6, 2, 7, 3, 7, 5, 6, 15, 5, 5, 6, 14, 6, 7, 6, 9, 6, 14, 6, 5, 6, 5, 6, 2, 7, 13, 7]
flag = ''
for j in range(0, len(enc), 2):
for i in range(32, 126):
if i & 0xF == enc[j] and (i >> 4) & 0xF == enc[j+1]:
flag += chr(i)
print(flag)
[HGAME 2023 week2]before_main
查壳:
64位linux无壳
找到主加密程序
大概是个base64加密
点开sub_12EB
直接点开里面的table表查看,按a换成字符串
可以发现是替换了table表
但拿去解密是错的
那应该就有可能是base64加密本身逻辑不同,或者有别的table表
观察加密函数,没有问题,那应该就是有别的table表了
交叉引用a0cxwsoemvjq4zd
可以看到确实有别的引用了这个table表
跟进
便可以看到别的table表,拿去解密即可
从网上抄的映射脚本:
import base64 # 导入base64模块用于解密
s1 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' # 标准表
s2 = 'qaCpwYM2tO/RP0XeSZv8kLd6nfA7UHJ1No4gF5zr3VsBQbl9juhEGymc+WTxIiDK' # base64换表
en_text = 'AMHo7dLxUEabf6Z3PdWr6cOy75i4fdfeUzL17kaV7rG=' # 密文
map_text = '' # 用于存放密文通过换表映射回标准表的字符
for i in en_text:
if (i != '='): # 注意密文中存在等号的情况下,不需要替换!
idx = s2.index(i) # 获取每个密文的字符在换表中的索引
map_text += s1[idx] # 取出标准表中的该索引的字符,就是正常base64加密的密文
else:
map_text += i
print(map_text) # 可以先看看标准表base64加密的密文
print(base64.b64decode(map_text)) # 直接使用提供的base64解密函数,获得明文,就是flag
疑问
为什么会是调用这个table表,可以看到程序运行的时候同步运行了init函数,我们跟进init函数即可找到刚刚的table表
[HGAME 2023 week1]easyasm
打开附件是一个txt文本,里面储存了一个用汇编代码写的加密程序
可以看到这里有个异或和加密程序
其实可以直接猜是异或0x33
enc = [0x5b,0x54,0x52,0x5e,0x56,0x48,0x44,0x56,0x5f,0x50,0x3,0x5e,0x56,0x6c,0x47,0x3,0x6c,0x41,0x56,0x6c,0x44,0x5c,0x41,0x2,0x57,0x12,0x4e]
flag = ''
for i in enc:
flag += chr(i ^ 0x33)
print(flag)
得到flag
但还是得学着看汇编的hh
我自己的理解:
; void __cdecl enc(char *p)
.text:00401160 _enc proc near ; CODE XREF: _main+1B↑p //定义函数enc
.text:00401160
.text:00401160 i = dword ptr -4 //定义变量
.text:00401160 Str = dword ptr 8
.text:00401160
.text:00401160 push ebp
.text:00401161 mov ebp, esp //将基址指针ebp
.text:00401163 push ecx
.text:00401164 mov [ebp+i], 0
.text:0040116B jmp short loc_401176 //跳转到loc_401176 开始循环
.text:0040116D ; ---------------------------------------------------------------------------
.text:0040116D
.text:0040116D loc_40116D: ; CODE XREF: _enc+3B↓j
.text:0040116D mov eax, [ebp+i] //将i加载到eax中
.text:00401170 add eax, 1 //将i值加1
.text:00401173 mov [ebp+i], eax //更新i的值
.text:00401176
.text:00401176 loc_401176: ; CODE XREF: _enc+B↑j
.text:00401176 mov ecx, [ebp+Str] //将str移动给ecx
.text:00401179 push ecx ; Str //将str压栈
.text:0040117A call _strlen //用strlen获取刚刚压栈的str长度
.text:0040117F add esp, 4
.text:00401182 cmp [ebp+i], eax //比较i 与 eax大小
.text:00401185 jge short loc_40119D //如果i大于eax则跳转loc_40119D
.text:00401187 mov edx, [ebp+Str] //将str地址赋值edx
.text:0040118A add edx, [ebp+i] //将str的地址+i的地址,即可实现对str数组元素的获取
.text:0040118D movsx eax, byte ptr [edx] //将edx地址中的元素转byte赋值到eax寄存器
.text:00401190 xor eax, 33h //将eax与0x33异或,将异或结果储存到了eax寄存器中的低地址al中,他只是对eax中的内存进行操作
.text:00401193 mov ecx, [ebp+Str] //将异或后的值赋值给ecx
.text:00401196 add ecx, [ebp+i] //再次加载str的第i个数
.text:00401199 mov [ecx], al //将加密后的结果移动到ecx中
.text:0040119B jmp short loc_40116D //跳转到loc_40116D函数
.text:0040119D ; ---------------------------------------------------------------------------
.text:0040119D
.text:0040119D loc_40119D: ; CODE XREF: _enc+25↑j
.text:0040119D mov esp, ebp //回复栈指针
.text:0040119F pop ebp //恢复基址指针
.text:004011A0 retn //结束函数
.text:004011A0 _enc endp
Input: your flag
Encrypted result: 0x5b,0x54,0x52,0x5e,0x56,0x48,0x44,0x56,0x5f,0x50,0x3,0x5e,0x56,0x6c,0x47,0x3,0x6c,0x41,0x56,0x6c,0x44,0x5c,0x41,0x2,0x57,0x12,0x4e
[HGAME 2023 week1]easyenc
die查壳
64位win
定位主程序
对v7数组以byte类型进行操作
编写脚本
#include<stdio.h>
void main() {
int v8[10], v3, v5;
v8[0] = 167640836;
v8[1] = 11596545;
v8[2] = -1376779008;
v8[3] = 85394951;
v8[4] = 402462699;
v8[5] = 32375274;
v8[6] = -100290070;
v8[7] = -1407778552;
v8[8] = -34995732;
v8[9] = 101123568;
for (int i = 0; i < 41; i++)
{
v5 = (*((char*)v8 + i) + 86) ^ 0x32;
printf("%c", v5);
}
}
[HGAME 2023 week1]a_cup_of_tea
查壳
64win无壳
main函数
可以看到是接受参数Buf1和v10来加密然后与buf2进行比较
没看到哪里是v10的输入,但v10和buf1的地址相近,所以v10应该也是buf1的输入
tea加密
魔改delta的tea加密,进行四轮
返回看函数接收,接受了一个m128i_i32地址
smmword表示一个十六字节字符串
小端序储存,因此可以推测这就是tea加密的key,当然也可以用动调来看tea加密中变量的赋值
编写脚本
#include <iostream>
using namespace std;
int main()
{
unsigned int enc[8] = { 778273437,-1051836401, -1690714183,1512016660,1636330974,1701168847,-1626976412,0x236A43F6 };
unsigned int key[4] = { 0x12345678, 0x23456789, 0x34567890, 0x45678901 };
int i, j;
for (i = 0;i < 8;i += 2) {
unsigned int delta = 0x543210DD;
int sum = -0x543210DD * 32;
for (j = 0; j < 32; j++) {
enc[i + 1] -= ((enc[i] << 4) + key[2]) ^ (enc[i] + sum) ^ ((enc[i] >> 5) + key[3]);
enc[i] -= ((enc[i + 1] << 4) + key[0]) ^ (enc[i + 1] + sum) ^ ((enc[i + 1] >> 5) + key[1]);
sum += delta;
}
}
printf("%s", enc);
return 0;
}
用python的ctypes库
from ctypes import *
from binascii import *
from Crypto.Util import *
key = [0x12345678, 0x23456789, 0x34567890, 0x45678901]
enc = [0x2E63829D, 0xC14E400F, 0x9B39BFB9, 0x5A1F8B14, 0x61886DDE, 0x6565C6CF, 0x9F064F64, 0x236A43F6]
for i in range(0, len(enc), 2):
v0 = c_uint32(enc[i])
v1 = c_uint32(enc[i+1])
delta = 0x543210DD
sum = c_uint32(-delta * 32)
r = 32
for j in range(r):
v1.value -= (sum.value + v0.value) ^ (key[2] + 16 * v0.value) ^ (key[3] + (v0.value >> 5))
v0.value -= (sum.value + v1.value) ^ (key[0] + 16 * v1.value) ^ (key[1] + (v1.value >> 5))
sum.value += delta
enc[i] = v0.value
enc[i+1] = v1.value
print(a2b_hex(hex(enc[i])[2:].encode()).decode()[::-1],end='')
print(a2b_hex(hex(enc[i+1])[2:].encode()).decode()[::-1], end='')
# print(number.long_to_bytes(enc[i]).decode()[::-1],end="")
# print(number.long_to_bytes(enc[i+1]).decode()[::-1],end="")
#也可以用Crypto.Util库的long_to_bytes打印
[HGAME 2023 week2]stream
python编译的程序,使用pyinstxtractor-master来拆包
stream.pyc反编译
import base64
def gen(key):
s = list(range(256))
j = 0
for i in range(256):
j = (j + s[i] + ord(key[i % len(key)])) % 256
tmp = s[i]
s[i] = s[j]
s[j] = tmp
i = j = 0
data = []
for _ in range(50):
i = (i + 1) % 256
j = (j + s[i]) % 256
tmp = s[i]
s[i] = s[j]
s[j] = tmp
data.append(s[(s[i] + s[j]) % 256])
return data
def encrypt(text, key):
result = ''
for c, k in zip(text, gen(key)):
result += chr(ord(c) ^ k)
result = base64.b64encode(result.encode()).decode()
return result
text = input('Flag: ')
key = 'As_we_do_as_you_know'
enc = encrypt(text, key)
if enc == 'wr3ClVcSw7nCmMOcHcKgacOtMkvDjxZ6asKWw4nChMK8IsK7KMOOasOrdgbDlx3DqcKqwr0hw701Ly57w63CtcOl':
print('yes!')
return None
None('try again...')
很简单的python程序写的异或加密
首先在加密函数中把k打印出来
再编写脚本
解密脚本:
from base64 import *
key = [213, 242, 54, 127, 156, 227, 172, 100, 212, 1, 130, 92, 20, 189, 115, 12, 15, 228, 186, 225, 227, 75, 200, 119, 171, 11, 152, 15, 89, 160, 116, 157, 194, 226, 72, 147, 65, 74, 92, 21, 136, 193, 152, 94, 17, 178, 205, 195, 87, 145]
enc = 'wr3ClVcSw7nCmMOcHcKgacOtMkvDjxZ6asKWw4nChMK8IsK7KMOOasOrdgbDlx3DqcKqwr0hw701Ly57w63CtcOl'
tmp = b64decode(enc.encode()).decode()
flag = ''
for i in range(len(tmp)):
flag += chr(ord(tmp[i]) ^ key[i])
print(flag)
[HGAME 2023 week2]math
查壳:
64位linux无壳
分析
是一个简单的矩阵相乘方程
from z3 import *
v12 = [63998, 33111, 67762, 54789, 61979, 69619, 37190, 70162, 53110, 68678, 63339, 30687, 66494, 50936, 60810, 48784, 30188, 60104, 44599, 52265, 43048, 23660, 43850, 33646, 44270]
v10 = [126, 225, 62, 40, 216, 253, 20, 124, 232, 122, 62, 23, 100, 161, 36, 118, 21, 184, 26, 142, 59, 31, 186, 82, 79]
x = [Int(f"x{i}") for i in range(25)]
s = Solver()
for i in range(5):
for j in range(5):
tmp = 0
for k in range(5):
tmp += x[5 * i + k] * v10[5 * k + j]
s.add(v12[5 * i + j] == tmp)
if s.check() == sat:
result = s.model()
print(result)
[HGAME 2023 week3]kunmusic
用dnspy打开dll文件
定位关键函数
这里看入口点
在资源处保存文件
转换文档脚本
with open(r'D:\CTF\Reverse\Hgame\Hgame2023\[HGAME 2023 week3]kunmusic\kmusic\data', 'rb') as file:
str = file.read()
result = [0] * len(str)
for i in range(len(str)):
result[i] = (str[i] ^ 104)
output = bytes(result)
with open(r'D:\CTF\Reverse\Hgame\Hgame2023\[HGAME 2023 week3]kunmusic\kmusic\output.txt', 'wb') as file:
file.write(output)
丢入dnspy反编译
找到关键加密函数
z3求解
from z3 import *
s = Solver()
num = [BitVec(f'num{i}',8) for i in range(13)]
array = [132, 47, 180, 7, 216, 45, 68, 6, 39, 246, 124, 2, 243, 137, 58, 172, 53, 200, 99, 91, 83, 13, 171, 80, 108, 235, 179, 58, 176, 28, 216, 36, 11, 80, 39, 162, 97, 58, 236, 130, 123, 176, 24, 212, 56, 89, 72]
s.add(num[0] + 52296 + num[1] - 26211 + num[2] - 11754 + (num[3] ^ 0xA114) + num[4] * 63747 + num[5] - 52714 + num[6] - 10512 + num[7] * 12972 + num[8] + 45505 + num[9] - 21713 + num[10] - 59122 + num[11] - 12840 + (num[12] ^ 0x525F) == 12702282 )
s.add( num[0] - 25228 + (num[1] ^ 0x50DB) + (num[2] ^ 0x1FDE) + num[3] - 65307 + num[4] * 30701 + num[5] * 47555 + num[6] - 2557 + (num[7] ^ 0xBF9F) + num[8] - 7992 + (num[9] ^ 0xE079) + (num[10] ^ 0xE052) + num[11] + 13299 + num[12] - 50966 == 9946829 )
s.add( num[0] - 64801 + num[1] - 60698 + num[2] - 40853 + num[3] - 54907 + num[4] + 29882 + (num[5] ^ 0x3506) + (num[6] ^ 0x533E) + num[7] + 47366 + num[8] + 41784 + (num[9] ^ 0xD1BA) + num[10] * 58436 + num[11] * 15590 + num[12] + 58225 == 2372055 )
s.add( num[0] + 61538 + num[1] - 17121 + num[2] - 58124 + num[3] + 8186 + num[4] + 21253 + num[5] - 38524 + num[6] - 48323 + num[7] - 20556 + num[8] * 56056 + num[9] + 18568 + num[10] + 12995 + (num[11] ^ 0x995C) + num[12] + 25329 == 6732474 )
s.add( num[0] - 42567 + num[1] - 17743 + num[2] * 47827 + num[3] - 10246 + (num[4] ^ 0x3F9C) + num[5] + 39390 + num[6] * 11803 + num[7] * 60332 + (num[8] ^ 0x483B) + (num[9] ^ 0x12BB) + num[10] - 25636 + num[11] - 16780 + num[12] - 62345 == 14020739 )
s.add( num[0] - 10968 + num[1] - 31780 + (num[2] ^ 0x7C71) + num[3] - 61983 + num[4] * 31048 + num[5] * 20189 + num[6] + 12337 + num[7] * 25945 + (num[8] ^ 0x1B98) + num[9] - 25369 + num[10] - 54893 + num[11] * 59949 + (num[12] ^ 0x3099) == 14434062 )
s.add( num[0] + 16689 + num[1] - 10279 + num[2] - 32918 + num[3] - 57155 + num[4] * 26571 + num[5] * 15086 + (num[6] ^ 0x59CA) + (num[7] ^ 0x5B35) + (num[8] ^ 0x3FFD) + (num[9] ^ 0x5A85) + num[10] - 40224 + num[11] + 31751 + num[12] * 8421 == 7433598 )
s.add( num[0] + 28740 + num[1] - 64696 + num[2] + 60470 + num[3] - 14752 + (num[4] ^ 0x507) + (num[5] ^ 0x89C8) + num[6] + 49467 + num[7] - 33788 + num[8] + 20606 + (num[9] ^ 0xAF4A) + num[10] * 19764 + num[11] + 48342 + num[12] * 56511 == 7989404 )
s.add( (num[0] ^ 0x7132) + num[1] + 23120 + num[2] + 22802 + num[3] * 31533 + (num[4] ^ 0x9977) + num[5] - 48576 + (num[6] ^ 0x6F7E) + num[7] - 43265 + num[8] + 22365 + num[9] + 61108 + num[10] * 2823 + num[11] - 30343 + num[12] + 14780 == 3504803 )
s.add( num[0] * 22466 + (num[1] ^ 0xDABF) + num[2] - 53658 + (num[3] ^ 0xB838) + (num[4] ^ 0x30DF) + num[5] * 59807 + num[6] + 46242 + num[7] + 3052 + (num[8] ^ 0x62BF) + num[9] + 30202 + num[10] * 22698 + num[11] + 33480 + (num[12] ^ 0x4175) == 11003580 )
s.add( num[0] * 57492 + (num[1] ^ 0x346D) + num[2] - 13941 + (num[3] ^ 0xBBDC) + num[4] * 38310 + num[5] + 9884 + num[6] - 45500 + num[7] - 19233 + num[8] + 58274 + num[9] + 36175 + (num[10] ^ 0x4888) + num[11] * 49694 + (num[12] ^ 0x2501) == 25546210 )
s.add( num[0] - 23355 + num[1] * 50164 + (num[2] ^ 0x873A) + num[3] + 52703 + num[4] + 36245 + num[5] * 46648 + (num[6] ^ 0x12FA) + (num[7] ^ 0xA376) + num[8] * 27122 + (num[9] ^ 0xA44A) + num[10] * 15676 + num[11] - 31863 + num[12] + 62510 == 11333836 )
s.add( num[0] * 30523 + (num[1] ^ 0x1F36) + num[2] + 39058 + num[3] * 57549 + (num[4] ^ 0xD0C0) + num[5] * 4275 + num[6] - 48863 + (num[7] ^ 0xD88C) + (num[8] ^ 0xA40) + (num[9] ^ 0x3554) + num[10] + 62231 + num[11] + 19456 + num[12] - 13195 == 13863722)
s.add(num[0]==ord('h')^array[0])
s.add(num[1]==ord('g')^array[1])
s.add(num[2]==ord('a')^array[2])
s.add(num[3]==ord('m')^array[3])
s.add(num[4]==ord('e')^array[4])
s.add(num[5]==ord('{')^array[5]) #z3多解,猜测求解
if s.check() == sat:
result = s.model()
num_values = [result[num[i]].as_long() for i in range(13)]
flag = ''
for i in range(len(array)):
flag += chr((array[i] ^ num_values[i % len(num_values)] ) & 0x7f )
print(flag)
[HGAME 2023 week2]VidarCamera
找到主加密程序
长度40的字节
uIntArr是密文
查看 m8encrypthkIa6DI函数
我改了一下变量名称(有一说一java太丑了)
可以看到这里有个很明显的xtea加密
修改了delta,round,加密过程
(func1和func2点进去查看是什么意思,其实就是简单的索引)
解密函数:
from ctypes import *
from Crypto.Util import *
enc = [0x260202FA, 0x1B451064, 0x867B61F1, 0x228033C5, 0xF15D82DC, 0x9D8430B1, 0x19F2B1E7, 0x2BBA859C, 0x2A08291D, 0xDC707918]
key = [2233, 4455, 6677, 8899]
for i in range(len(enc) - 2, -1, -1):
v0 = c_uint32(enc[i])
v1 = c_uint32(enc[i+1])
delta = 878077251
r = 33
sum = c_uint32(r * delta)
for j in range(r):
sum.value -= delta
v1.value -= (key[(sum.value >> 11) & 3] + sum.value) ^ (v0.value + ((v0.value >> 5) ^ (v0.value << 4)))
v0.value -= (key[sum.value & 3] + sum.value) ^ (v1.value + ((v1.value >> 5) ^ (v1.value << 4))) ^ sum.value
enc[i] = v0.value
enc[i+1] = v1.value
for i in range(10):
print(number.long_to_bytes(enc[i]).decode()[::-1], end='')
[HGAME 2023 week4]vm
点进主要加密函数
这里就是虚拟机的opcode,a1就是ip
导入结构体(根据sub_140001000函数)
struct vm
{
_DWORD r[6];
_DWORD ip;
_DWORD esp;
_BYTE zf;
};
分析vm指令
mov
有赋值操作,即mov
push
点进stack,即可看到一堆未定义的空间
将寄存器的值赋值给这些空间,即可知道这是模拟压栈操作
pop
与上面的类似,将stack中的值赋值给寄存器,模拟出栈操作(将上面的函数结合其实很容易看出来)
运算
进行了一系列运算,分别是add, sub, mul, xor, shl, shr
cmp
两个if值的比较,如果比较成功则zf为0不成功则为1,为后面的jz和jnz服务的
jmp
跳转到下一条opcode指令处,将opcode下一条指令赋值给ip
jz
JZ:jump if zero
如果if为假(即zf为0),则执行下一条指令
jnz
JNZ:jump if not zero
如果if为真(即zf不为0),则执行下一条指令
编写脚本
opcode = [ 0x00, 0x03, 0x02, 0x00, 0x03, 0x00, 0x02, 0x03, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x03, 0x02, 0x32,
0x03, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00,
0x01, 0x00, 0x00, 0x03, 0x02, 0x64, 0x03, 0x00, 0x02, 0x03,
0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x01, 0x00, 0x00, 0x03,
0x00, 0x08, 0x00, 0x02, 0x02, 0x01, 0x03, 0x04, 0x01, 0x00,
0x03, 0x05, 0x02, 0x00, 0x03, 0x00, 0x01, 0x02, 0x00, 0x02,
0x00, 0x01, 0x01, 0x00, 0x00, 0x03, 0x00, 0x01, 0x03, 0x00,
0x03, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x03, 0x01, 0x28,
0x04, 0x06, 0x5F, 0x05, 0x00, 0x00, 0x03, 0x03, 0x00, 0x02,
0x01, 0x00, 0x03, 0x02, 0x96, 0x03, 0x00, 0x02, 0x03, 0x00,
0x00, 0x00, 0x00, 0x04, 0x07, 0x88, 0x00, 0x03, 0x00, 0x01,
0x03, 0x00, 0x03, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x03,
0x01, 0x28, 0x04, 0x07, 0x63, 0xFF, 0xFF]
i = 0
for j in range(10000):
if opcode[i] == 0xff:
break
match opcode[i]:
case 0: #mov
print(f'({i})', end=' ') #地址
match (opcode[i + 1]):
case 1:
print('mov flag[r[2]], r[1]')
case 2:
print(f'mov r[{opcode[i + 2]}], r[{opcode[i + 3]}]')
case 3:
print(f'mov r[{opcode[i + 2]}], {opcode[i + 3]}')
case _:
print('mov r[0], flag[r[2]] ')
i += 4
case 1: #push
print(f'({i})', end=' ')
match (opcode[i + 1]):
case 1:
print('push r[0]')
case 2:
print('push r[2]')
case 3:
print('case r[3]')
case _:
print('push r[0]')
i += 2
case 2: #pop
print(f'({i})', end=' ')
match (opcode[i + 1]):
case 1:
print('pop r[1]')
case 2:
print('pop r[2]')
case 3:
print('pop r[3]')
case _:
print('pop r[0]')
i += 2
case 3: #运算
print(f'({i})', end=' ')
match (opcode[i + 1]):
case 0:
print(f'add r{[opcode[i + 2]]} r{[opcode[i + 3]]}')
case 1:
print(f'sub r{[opcode[i + 2]]} r{[opcode[i + 3]]}')
case 2:
print(f'mul r{[opcode[i + 2]]} r{[opcode[i + 3]]}')
case 3:
print(f'xor r{[opcode[i + 2]]} r{[opcode[i + 3]]}')
case 4:
print(f'shl r{[opcode[i + 2]]} r{[opcode[i + 3]]}')
case 5:
print(f'shr r{[opcode[i + 2]]} r{[opcode[i + 3]]}')
i += 4
case 4: #cmp
print(f'({i})', end=' ')
print('cmp r[0] r[1]')
i += 1
case 5: #jmp
print(f'({i})', end=' ')
print(f'jmp {opcode[i + 1]}')
i += 2
case 6: #jz
print(f'({i})', end=' ')
print(f'jz {opcode[i + 1]}')
i += 2
case 7: #jnz
print(f'({i})', end=' ')
print(f'jnz {opcode[i + 1]}')
i += 2
打印出来的汇编指令(我给了一些注释)
(0) mov r[2], 0
(4) add r[2] r[3] //r[3]就是i
(8) mov r[0], flag[r[2]] //这里的flag[r[2]]就是输入的input
(12) mov r[1], r[0]
(16) mov r[2], 50
(20) add r[2] r[3]
(24) mov r[0], flag[r[2]] //将flag内存处偏移50 + i的值存入r[0]
(28) add r[1] r[0] //将input 与 偏移50+i的值相加
(32) mov r[2], 100
(36) add r[2] r[3]
(40) mov r[0], flag[r[2]] //将flag内存处偏移100 + i的值存入r[0]
(44) xor r[1] r[0] //将input 与 偏移100+i的值异或,将值存入v[1]
(48) mov r[0], 8 //赋值8给r[0]
(52) mov r[2], r[1]
(56) shl r[1] r[0]
(60) shr r[2] r[0]
(64) add r[1] r[2] //将异或后的值左移八位右移八位相加存入r[1](也就是低八位和高八位换位)
(68) mov r[0], r[1] //将运算结果保存在r[0]中
(72) push r[0] //将运算结果压入栈中
(74) mov r[0], 1
(78) add r[3] r[0]
(82) mov r[0], r[3]
(86) mov r[1], 40
(90) cmp r[0] r[1] //判断循环结束
(91) jz 95
(93) jmp 0
(95) mov r[3], 0
(99) pop r[1] //将结果出栈保存到r[1]中,所以最后一个结果先与第一个比较
(101) mov r[2], 150
(105) add r[2] r[3]
(109) mov r[0], flag[r[2]]
(113) cmp r[0] r[1] //将运算结果与偏移150位的数据比较
(114) jnz 136
(116) mov r[0], 1
(120) add r[3] r[0]
(124) mov r[0], r[3]
(128) mov r[1], 40
(132) cmp r[0] r[1] //继续循环
(133) jnz 99
将输入与input内存偏移50处的值相加,后与偏移100的值异或,最后与偏移150处的值逆序(出栈压栈操作导致的)比较
编写解密脚本
enc = [18432, 61696, 16384, 8448, 13569, 25600, 30721, 63744, 6145, 20992, 9472, 23809, 18176, 64768, 26881, 23552, 44801, 45568, 60417, 20993
, 20225, 6657, 20480, 34049, 52480, 8960, 63488, 3072, 52992, 15617, 17665, 33280, 53761, 10497, 54529, 1537, 41473, 56832, 42497, 51713]
enc = enc[::-1]
key2 =[155, 168, 2, 188, 172, 156, 206, 250, 2, 185, 255, 58, 116, 72, 25, 105, 232, 3, 203, 201, 255, 252, 128, 214, 141, 215, 114, 0, 167, 29, 61, 153, 136
, 153, 191, 232, 150, 46, 93, 87]
key1 = [201, 169, 189, 139, 23, 194, 110, 248, 245, 110, 99, 99, 213, 70, 93, 22, 152, 56, 48, 115, 56, 193, 94, 237, 176,
41, 90, 24, 64, 167, 253, 10, 30, 120, 139, 98, 219, 15, 143, 156]
flag = ''
for i in range(len(enc)):
tmp1 = (enc[i] & 0xFF << 8) + ((enc[i] >> 8) & 0xFF)
tmp2 = tmp1 ^ key1[i]
tmp3 = tmp2 - key2[i]
flag += chr(tmp3 & 0xff)
print(flag)
flag : hgame{y0ur_rever5e_sk1ll_i5_very_g0od!!}
高低位互换操作:
b = 18432
low_byte = b & 0xFF # 低八位 (masking with 0xFF)
high_byte = (b >> 8) & 0xFF # 高八位 (shift right and mask with 0xFF)
swapped_value = (low_byte << 8) + high_byte
[HGAME 2023 week3]cpp
全靠大佬wp成就TAT
进入丑的一b的main函数
甚至都没翻到哪里对input进行加密了
那就先动调
下断点动调
既然都不知道input都有多长,那就先输入一个比较长的数
一直f7步入
进入到一个不知道干啥的函数
跳出去继续f7
这次进入了一个疑似加密的函数,是一个异或函数
跳到异或部分
这里就是把eax和ecx进行异或,eax应该就是加密值,但我不知道为什么
使用ida脚本打印ecx的值
from idaapi import *
print(hex(get_reg_val("ecx")),end = ',')
0x4037a04e,0xfdda0246,0x3c6efa21,0xcf9cd9af,0x673347b9,0xdec4ee0,0x1380c4d1,0x3ab2a932,0x25d50a7,0x834a3982,0xcb6ea25f,0xa26ba4ab,0xa1c42135,0xd1063eba,0x2397fefc,0x55c7d126,0xabababab
进入比较函数
还有两位在汇编界面看
这就是一个比较函数
可以看到enc长度是40,那key应该也是40
编写汇编脚本
from struct import *
key = [0x4037a04e,0xfdda0246,0x3c6efa21,0xcf9cd9af,0x673347b9,0xdec4ee0,0x1380c4d1,0x3ab2a932,0x25d50a7,0x834a3982]
enc = [0x28, 0x50, 0xC1, 0x23, 0x98, 0xA1, 0x41, 0x36, 0x4C, 0x31, 0xCB, 0x52, 0x90, 0xF1, 0xAC, 0xCC, 0xF, 0x6C, 0x2A, 0x89, 0x7F, 0xDF, 0x11, 0x84, 0x7F, 0xE6, 0xA2, 0xE0, 0x59, 0xC7, 0xC5, 0x46, 0x5D, 0x29, 0x38, 0x93, 0xED, 0x15, 0x7A, 0xFF,]
key_LE = []
flag = []
for i in key:
key_LE.extend((pack('>I', i)))
for i in range(len(enc)):
print(chr(key_LE[i] ^ enc[i]), end='')
貌似是需要把enc进行小端序转换的,我这边直接把key大端序打印了,方便点
hgame{Cpp_1s_much_m0r3_dlff1cult_th4n_C}
只能说不懂的多的一b,哎哎,看wp也没看懂