首页 > 其他分享 >Hgame2023 Reverse

Hgame2023 Reverse

时间:2024-11-15 21:19:26浏览次数:1  
标签:enc Reverse text 0x00 mov num Hgame2023 print

Hgame 2023

[HGAME 2023 week1]test your IDA

用ida打开即可

[HGAME 2023 week1]encode

查壳image-20241106135704303

32位windows

加密函数

image-20241106144724863

将输入的字节转高位和低位进行加密,后与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

查壳:

image-20241106145106892

64位linux无壳

找到主加密程序

image-20241106151208838

大概是个base64加密

点开sub_12EB

image-20241106151233417

直接点开里面的table表查看,按a换成字符串

image-20241106151251508

可以发现是替换了table表

但拿去解密是错的

那应该就有可能是base64加密本身逻辑不同,或者有别的table表

观察加密函数,没有问题,那应该就是有别的table表了

交叉引用a0cxwsoemvjq4zd

image-20241106151414391

可以看到确实有别的引用了这个table表

跟进

image-20241106152555619

便可以看到别的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表image-20241106152705919

[HGAME 2023 week1]easyasm

打开附件是一个txt文本,里面储存了一个用汇编代码写的加密程序

image-20241106153651967

可以看到这里有个异或和加密程序

其实可以直接猜是异或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查壳

image-20241106171829452

64位win

定位主程序

image-20241106183124094

对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

查壳

image-20241106183359746

64win无壳

main函数

image-20241106202555776

可以看到是接受参数Buf1和v10来加密然后与buf2进行比较

没看到哪里是v10的输入,但v10和buf1的地址相近,所以v10应该也是buf1的输入

tea加密

image-20241106203229555

魔改delta的tea加密,进行四轮

返回看函数接收,接受了一个m128i_i32地址

image-20241106203348716

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来拆包

image-20241107220021032

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程序写的异或加密

image-20241107221851175

首先在加密函数中把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

查壳:

image-20241107222041409

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文件

定位关键函数

这里看入口点

image-20241108211525781

image-20241108191928713

在资源处保存文件

image-20241108202017307

转换文档脚本

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反编译

image-20241109110631309

找到关键加密函数

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

找到主加密程序

image-20241109112054883

长度40的字节

uIntArr是密文

查看 m8encrypthkIa6DI函数

image-20241109160401129

我改了一下变量名称(有一说一java太丑了)

image-20241109160421623

可以看到这里有个很明显的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

image-20241111133632240

点进主要加密函数image-20241111133640284

这里就是虚拟机的opcode,a1就是ip

导入结构体(根据sub_140001000函数)

image-20241111165158083

struct vm
{
  _DWORD r[6];
  _DWORD ip;
  _DWORD esp;
  _BYTE zf;
};

分析vm指令

image-20241112121848666

mov

image-20241112121900440

有赋值操作,即mov

push

image-20241112122024679

点进stack,即可看到一堆未定义的空间

将寄存器的值赋值给这些空间,即可知道这是模拟压栈操作

pop

image-20241112122138416

与上面的类似,将stack中的值赋值给寄存器,模拟出栈操作(将上面的函数结合其实很容易看出来)

运算

image-20241112122234485

进行了一系列运算,分别是add, sub, mul, xor, shl, shr

cmp

image-20241112122345377

两个if值的比较,如果比较成功则zf为0不成功则为1,为后面的jz和jnz服务的

jmp

image-20241112122913255

跳转到下一条opcode指令处,将opcode下一条指令赋值给ip

jz

image-20241112122651008

JZ:jump if zero

如果if为假(即zf为0),则执行下一条指令

jnz

image-20241112123144319

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函数

image-20241112210625740

甚至都没翻到哪里对input进行加密了

那就先动调

下断点动调

既然都不知道input都有多长,那就先输入一个比较长的数

image-20241112210911330

一直f7步入

image-20241112211201797

进入到一个不知道干啥的函数

跳出去继续f7

image-20241112211259976

这次进入了一个疑似加密的函数,是一个异或函数

跳到异或部分image-20241112212129159

这里就是把eax和ecx进行异或,eax应该就是加密值,但我不知道为什么

使用ida脚本打印ecx的值

from idaapi import *
print(hex(get_reg_val("ecx")),end = ',')

image-20241112213437133

0x4037a04e,0xfdda0246,0x3c6efa21,0xcf9cd9af,0x673347b9,0xdec4ee0,0x1380c4d1,0x3ab2a932,0x25d50a7,0x834a3982,0xcb6ea25f,0xa26ba4ab,0xa1c42135,0xd1063eba,0x2397fefc,0x55c7d126,0xabababab

进入比较函数

image-20241112213532565

image-20241112213553284

还有两位在汇编界面看

image-20241112213633737

这就是一个比较函数

可以看到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也没看懂

标签:enc,Reverse,text,0x00,mov,num,Hgame2023,print
From: https://www.cnblogs.com/murasame520/p/18548674

相关文章

  • 关于在Reverse函数中不能使用L=s而是*L=*s的原因分析
    完整代码地址:https://blog.csdn.net/2301_76819732/article/details/143807340?spm=1001.2014.3001.5502如果使用L=s;的话,当输出结果时,会发现内容为空。我感到很奇怪,按照我的设想,L=s;会把s指向的地址赋给L。但现在这个情况肯定是失败的了。我随便试了一试,发现如果......
  • Reverse and Add UVA - 10018
    //ReverseandAddUVA-10018.cpp:此文件包含"main"函数。程序执行将在此处开始并结束。///*https://vjudge.net/problem/UVA-10018#author=md_bayazidThe“reverseandadd”methodissimple:chooseanumber,reverseitsdigitsandaddittotheoriginal.I......
  • reverse学习总结(3)
    一,luck_guy先查壳,没有特殊情况,进入ida分析猜测是输入v4,让后对v4进行修改(查了一下单词,patch_me相当于加密等操作吧)我们跟进让后进入get_flag函数发现有一个生成随机数的函数,并且只有特定的数才可以得到flag,与标题:luck_guy相吻合,所以我们直接分析代码就可以,随机......
  • reverse2
    题目链接:reverse2。下载附件后,使用IDA进行反编译,定位到main函数,如下。main函数中主要需要分析的地方如下。for(i=0;i<=strlen(&flag);++i){if(*(&flag+i)=='i'||*(&flag+i)=='r')*(&flag+i)='1';......
  • reverse3
    题目链接:reverse3。下载附件后,使用IDA打开,进行反编译,定位到main函数如下。通过分析main函数流程,可以发现主要是对用户输入的字符串进行了两次操作,第一次是函数"sub_4110BE",第二次是对函数"sub_4110BE"处理后得到的字符串进行简单的加减运算,随后就与目标字符串进行比对......
  • E. Reverse the Rivers(二分)CF984
    题意:给定n个国家,k个地区,aij为第i个国家第j个地区,bij=a1j|a2j|---aij为第i个国家第j个地区的更新值,给出q个问题,每个问题包含m项要求,国家i必须满足m项要求:如果o=='<'必须满足bir<c否则bir>c,输出满足所有条件的最小序号的国家分析:如果o是小于号,用二分找到右区间,如果o是大于号,用二......
  • 2024网鼎杯线上赛REVERSE02(超详细)
    进入主函数分析代码发现了四段加密,一层一层进行解密第一步:打开进入main函数,然后分析代码第一个加密对dest的八个字节做了乘2加密,密文是s2伪代码下看不全在汇编下看第二步:第二块数据进行了异或加密异或key是XorrLord,然后写脚本进行解密拿到了第一段和第二......
  • [强网拟态 2024 初赛] Reverse赛题复现
    队内的爹做的太快了,完全跟不上......
  • CF1458D Flip and Reverse 题解
    思路由于它要求\(\text{01}\)数量相等,我们可以考虑站在前缀和的角度看待这个问题。我们将\(0\)看作负一,\(1\)看作一。可以把它化成一个折线图(方便观察)。观察一下它的操作实际上在干什么。容易发现,在折线图上,我们把操作的\([l,r]\)的整段折线reverse了一遍。同样的,......
  • 【有啥问啥】逆向工程(Reverse Engineering,RE):深度解析与技术方法
    逆向工程(ReverseEngineering,RE):深度解析与技术方法引言逆向工程(ReverseEngineering,简称RE),作为现代科技领域中的一项重要技术,其影响力已远远超越了传统的硬件拆解范畴。在软件安全、产品设计优化、知识产权保护,以及教育与研究等多个领域,逆向工程都展现出了其独特的价值和......