Mas0n
to be reverse engineer🐧
翻车鱼

吾爱破解【2023春节】解题领红包 WP

吾爱破解【2023春节】解题领红包 WP

参加活动的第二个年头,今年的题不管是数量还是范围都多了不少,这意味着今年更卷了😂

这篇题解就放在博客作个记录,希望明年还能有空参加

Windows 初级题 1/23

cpp逆向,简单移位

bytearray([i >> 2 for i in [0x00000198, 0x000001B0, 0x00000184, 0x0000019C, 0x000001EC, 0x000000D4, 0x000000C8, 0x00000140, 0x000001BC, 0x00000128, 0x000001A4, 0x00000194, 0x000000C8, 0x000000C0, 0x000000C8, 0x000000CC, 0x00000120, 0x00000184, 0x000001C0, 0x000001C0, 0x000001E4, 0x00000138, 0x00000194, 0x000001DC, 0x00000164, 0x00000194, 0x00000184, 0x000001C8, 0x000001F4]])

Android 初级题 1/24

Java逆向,简单位操作

bytearray([i - 2 for i in b"hnci}|jwfclkczkppkcpmwckng\x7f"])

Android 初级题 1/25

frida hook

objection -g com.zj.wuaipojie2023_1 explore
android hooking watch class_method com.zj.wuaipojie2023_1.C.cipher --dump-return
https://cdn.shi1011.cn/2023/02/ee7f1e73521f0f59332c3805a0aeb1b6.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

Windows 中级题 1/26

魔改upx,oep定位,dump&fix,关闭ASLR。

MFC程序,SEH异常处理,调用结构类似以下:

void DivException(int a1, int a2, int* a3) {
    __try {
        *a3 = a1 / a2;
    }
    __except (GetExceptionCode() == EXCEPTION_INT_DIVIDE_BY_ZERO) { }
}

void DbgBrk() {
    __try {
        DebugBreak();
    }
    __except (GetExceptionCode() == EXCEPTION_BREAKPOINT) {
        getUSER32BaseAddr(); // +0x2410
    }
}


int entry() {
    __try {
        RaiseException(1u, 0, 0, 0);
    }
    __except (filterLoadApiTable() /* +0x2DC0 */) {
        DbgBrk(); // +0x2FC0
    }
    __finally {
        DivException(1, 0, 0); // +0x2D80
    }

    divZero(); // +0x2FF0
    return 0;
}

这里注意到了divZero并没有对应注册SEH。

而是在_tmainCRTStartup初始化程序时,调用SetUnhandledExceptionFilter注册了UEH,具体位置在偏移0x100B上。

同时,由于SetUnhandledExceptionFilter内部调用了ntdll!NtQueryInformationProcess检测调试器,在这里结合divZero实现了反调试。

call divZero patch 为call TopLevelExceptionFilter 完成反反调试

流程分析中的几个点:

通过PEB获取USER32.dll基址,进而拿到函数表。

API表位于偏移0x17C90

https://cdn.shi1011.cn/2023/02/411cdea1b330570fcea16f3db52528f6.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

存储解密字符串表指针位于0x17D30,内容为宽字符形式

https://cdn.shi1011.cn/2023/02/4302b254599ca80f36b387cff2f3da57.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

粗略分析,疑似check两处:0x21100x2A50

tea算法两处

  • tea encrypt 0x1D70
  • tea decrypt 0x26E0

懒虫方案

猜能省大量时间(

frida hook俩函数,修改返回值 & 打表

发现只有修改0x2A50函数返回值为4时,弹出Success,逻辑部分简单描述如下

  1. 十六进制转字符数组
  2. tea解密
  3. 与明文对比

调试拿到明文

https://cdn.shi1011.cn/2023/02/32336d30b16fa8fd5d69b912769f401a.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

frida hook拿到kdelta

const addr = Module.findBaseAddress("【2023春节】解题领红包之五_dump_SCY.exe");

function buf2hex(buffer) {
    return [...new Uint8Array(buffer)]
        .map(x => '0x'+x.toString(16).padStart(2, '0').toUpperCase())
        .join(' ');
}

Interceptor.attach(addr.add(0x26e0), {
    onEnter: function (args) {
        console.log("[+] onEnter");
        console.log("[+]     v: " + buf2hex(args[0].readByteArray(8)));
        console.log("[+]     k: " + buf2hex(args[1].readByteArray(16)));
        console.log("[+] delta: " + args[2]);
        console.log("[+]   sum: " + args[3]);

    },
    
    onLeave: function (retval) {
        console.log("[+] retval: " + retval);
    }

});

跑一遍标准tea,转成十六进制拿到flag

void tea_encrypt(uint32_t*a1, uint32_t *a2, uint32_t a3, uint32_t a4)
{
  unsigned int v5; // [rsp+0h] [rbp-28h]
  unsigned int v6; // [rsp+4h] [rbp-24h]
  unsigned int i; // [rsp+8h] [rbp-20h]

  v5 = *a1;
  v6 = a1[1];
  for ( i = 0; i < 0x20; ++i )
  {
    a4 += a3;
    v5 += (a2[1] + (v6 >> 5)) ^ (a4 + v6) ^ (*a2 + 16 * v6);
    v6 += (a2[3] + (v5 >> 5)) ^ (a4 + v5) ^ (a2[2] + 16 * v5);
   
  }
  *a1 = v5;
  a1[1] = v6;
 
}

int main() {
    char inp[] = "flag{!!!_HAPPY_NEW_YEAR_2023!!!}";
    auto *v = (uint32_t*) inp;
    uint32_t k[4] = {0x8C7BD7F8, 0x18F7AFF0, 0xA57387E8, 0x31EF5FE0};
    uint32_t delta = 0x8c7bd7f7;
    uint32_t sum = 0x8f7afee0;


    for (int i = 0; i < 4; ++i) {
        tea_encrypt(v+i*2, k, delta, 0);
        printf("%08X%08X", v[i*2], v[i*2+1]);
    }
    printf("\n");
}

正常方案

程序调用DialogBoxParamW创建对话框,0x11D0DialogFunc

WM_INITDIALOG初始化窗口

单击按钮,首先执行WM_CREATE处内容

https://cdn.shi1011.cn/2023/02/e7107fa97d8558315bdfa64eabf705ac.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

分析0x2110部分看似进行了tea encrypt并进行了check,实际上只是混淆视听,其真正的用途是传递sum

而后调用PostMessageW执行WM_CUT部分,此时lParam即为sum

https://cdn.shi1011.cn/2023/02/2e2a2ba52eee02de63f3d93f2a7a5da4.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

进入default分支,v6恒为0,能看到真正的check在0x2A50

https://cdn.shi1011.cn/2023/02/470ca75f878f675a642d07f7ec291409.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

check算法主要是根据uid,计算delta,以及k,而后tea解密,大致算法如下

v11 = 0x11111111;
for ( i = 0; i < 14; ++i )
    v11 += 0x11111111;

for ( delta = v11 + uid; delta >= 0; delta = 2 * delta + 9 )
    ;
  for ( n = 0; n < 4; ++n )
    k[n] = (n + 1) * (delta + 1);

tea_decrypt(&flag[m], k, delta, sum)

附keygen

from ctypes import c_int32
import struct


def calc_delta(uid):
    i = c_int32(uid - 1)
    while i.value >= 0:
        i.value = 2 * i.value + 9
    return i.value & 0xffffffff


def calc_key(delta):
    k = [0] * 4
    for i in range(4):
        k[i] = (i + 1) * (delta + 1)
        k[i] &= 0xffffffff
    return k

def tea_encrypt(v, k, delta):
    v0, v1 = v
    k0, k1, k2, k3 = k
    sums = 0
    for i in range(32):
        sums += delta
        sums &= 0xffffffff
        v0 += ((v1 << 4) + k0) ^ (v1 + sums) ^ ((v1 >> 5) + k1)
        v0 &= 0xffffffff
        v1 += ((v0 << 4) + k2) ^ (v0 + sums) ^ ((v0 >> 5) + k3)
        v1 &= 0xffffffff
    return v0, v1


def keygen(uid):
    FLAG = struct.unpack('<8L', b'flag{!!!_HAPPY_NEW_YEAR_2023!!!}')
    flag = [0] * 8
    delta = calc_delta(uid)
    k = calc_key(delta)
    for i in range(4):
        flag[i*2], flag[i*2+1] = tea_encrypt(FLAG[i*2:i*2+2], k, delta)
    return "".join(f"{x:08x}" for x in flag)


if __name__ == '__main__':
    print(keygen(1150835))
    

Android 中级题 1/27

ndk逆向,java层没啥用

流程完全没有串起来,需要脑洞

根据符号猜作者意图:找到RealKey解密assets/aes.png

算法为AES-128-ECB PKSC7 padding

JNI注册了函数get_RealKey,结尾调用的strcmp上有个不明显的hint:thisiskey,其意为执行getRealKey的算法得到RealKey

k = b'|wfkuqokj4548366'
xmmword_3030 = [0xFB, 0xFE, 0xFB, 0xFE, 0xFB, 0xFE, 0xFB, 0xFE, 0xFB, 0xFE, 0xFB, 0xFE, 0xFB, 0xFE, 0xFB, 0xFE]
v5 = [(i + j) & 0xff for i, j in zip(k, xmmword_3030)]
real = bytearray(v5)
print(real) # bytearray(b'wuaipojie2023114')

AES解出图片

import base64
from Crypto.Cipher import AES


def pad(s):
    return s + (16 - len(s) % 16) * chr(16 - len(s) % 16)

def unpad(s):
    return s[:-ord(s[len(s) - 1:])]


cipherText = open('com.zj.wuaipojie2023_2/assets/aes.png', 'rb').read()
cipherText = base64.urlsafe_b64decode(cipherText)

ctx = AES.new(key=b'wuaipojie2023114', mode=AES.MODE_ECB)
dec = unpad(ctx.decrypt(cipherText))
pic = bytearray.fromhex(dec.decode('utf-8'))
with open('aes.dec.png', 'wb') as f:
    f.write(pic)

然后misc行为:文件末尾塞了一个png图片

https://cdn.shi1011.cn/2023/02/95baba9f247acf6e42f2622acbd3bf06.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

扫码得到flag

Android 高级题 1/28

ndk逆向,JNI动态注册checkSn,ollvm sub fla bcf拉满,流程混淆的稀碎

我选择摆烂unidbg trace

贴个片段

    public static void main(String[] args) {
        emulator = AndroidEmulatorBuilder.for64Bit().setRootDir(new File("target/rootfs")).setProcessName("com.zj.wuaipojie2023_2").build();
        Memory memory = emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));
        Logger.getLogger("com.github.unidbg.linux.ARM32SyscallHandler").setLevel(Level.DEBUG);
        Logger.getLogger("com.github.unidbg.unix.UnixSyscallHandler").setLevel(Level.DEBUG);
        Logger.getLogger("com.github.unidbg.AbstractEmulator").setLevel(Level.DEBUG);
        Logger.getLogger("com.github.unidbg.linux.android.dvm.DalvikVM").setLevel(Level.DEBUG);
        Logger.getLogger("com.github.unidbg.linux.android.dvm.BaseVM").setLevel(Level.DEBUG);
        Logger.getLogger("com.github.unidbg.linux.android.dvm").setLevel(Level.DEBUG);

        vm = emulator.createDalvikVM();
        vm.setVerbose(true);

        new AndroidModule(emulator, vm).register(memory);

        DalvikModule dm = vm.loadLibrary(new File("lib52pojie.so"), false);

        vm.setJni(new MyJni());


        dm.callJNI_OnLoad(emulator);

        module = dm.getModule();
        
//        PrintStream printStream = null;
//        try {
//            printStream = new PrintStream(new FileOutputStream("log.txt"));
//        } catch (FileNotFoundException e) {
//            throw new RuntimeException(e);
//        }
//
//        emulator.traceCode().setRedirect(printStream);
        
        String uid = "01150835";
        String flag = "MWYxODIxOThmYWFmM2ZlYjYxM2Q5ZDVlZTExMzg4MTM=";

        
        List<Object> list = new ArrayList<>();
        list.add(vm.getJNIEnv());
        list.add(0);
        list.add(vm.addLocalObject(new StringObject(vm, uid)));


        list.add(vm.addLocalObject(new StringObject(vm, flag)));

        Debugger dbg = emulator.attach();
//        dbg.addBreakPoint(0x4001064c);
        Number number = module.callFunction(emulator, 0xe830, list.toArray());
        System.out.println("result: " + number.intValue());

        
    }

分析下jni注册,checkSn偏移E830

https://cdn.shi1011.cn/2023/02/1d7199bffb21adf8362b98aaf022436d.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

调用一次checkSn大概86w行,ida+trace日志+unidbg调试分析,改变同一位置字节、不同位置字节

对比两者trace差异,简单贴几个关键偏移

0x184B8 -> md5_transform
0x1B32C -> 0x17834 -> md5(const uint8_t* uid)
0x1ECD8 -> zuc_F(pzuc_context context)
0x1D454 -> zuc_init(pzuc_context context, const uint8_t* key, const uint8_t* iv)
0x10668 -> md5_hexdigests(uid) xor zuc_encrypt(base64_decode(flag)) == 0

md5、base64为标准算法

zuc算法为流加密,参考代码 https://github.com/zhiyuan-lin/zuc/blob/master/c/zuc.c

key与时间戳相关,iv来自偏移4D22B,具体组成如下

char key[16]={};
char iv[16]={};

char buf[6]={};
int t = timestamp/20000000; // 0x185f69b0a37 / 20000000 == 83743 
sprintf(buf, "%d", t);

memcpy(key, 0x4D220, 0xA); // 0x3d,0x99,0x40,0x0e,0x05,0x50,0x7c,0x81,0x2e,0x3f
memcpy(key+0xA, buf, 0x5); // 0x38,0x33,0x37,0x34,0x35,0x00

memcpy(iv, 0x4D22B, 0x5); // 0xf2,0x3d,0xa0,0x96,0x02,0x2b,0x26,0x63,0xe1,0x5f,0xc1,0x28,0x6a,0x33,0x70,0x25

流加密,即crypt(crypt(inp)) == inp

直接偷懒

uid、flag输入格式

uid # 8位,不足补0
flag = base64.b64encode(md5(uid).hexdigest()) # 44 bytes after base64 encode

unidbg跑起来dump

	final byte[] res = new byte[32];
    final int[] i = {0};
    dbg.addBreakPoint(0x40010668, new BreakPointCallback() {
        @Override
        public boolean onHit(Emulator<?> emulator, long address) {
            res[i[0]++] = (byte) emulator.getContext().getLongByReg(Arm64Const.UC_ARM64_REG_X15);
            return true;
        }
    });
    

然后转成base64,输回去就成了

https://cdn.shi1011.cn/2023/02/971043a1729a076b2554316940cf618a.jpg?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5

至于keygen,分析这一坨太费时间,略

Web 初/中/高

应该改名misc+web+古典crypto套娃题

直接挂个草稿吧(不完全)

2023challenge.52pojie.cn adg dns重定向 52pojie.cn

https://cdn.shi1011.cn/2023/02/f4863c97c7de5f25fe832a63f34ebcd0.png?imageMogr2/format/webp/interlace/0/quality/90|watermark/2/text/wqlNYXMwbg/font/bXN5aGJkLnR0Zg/fontsize/14/fill/IzMzMzMzMw/dissolve/80/gravity/southeast/dx/5/dy/5
flag1
放视频
flag1{52pojiehappynewyear}
----------------------------------------------------------------------------
flag2
视频二维码
https://2023challenge.52pojie.cn/?flag=flag2{878a48f2}
flag2{878a48f2}
----------------------------------------------------------------------------
flag3
视频右下角水印 iodj3(06i95dig) 凯撒 offset 3
flag3(06f95afd)
----------------------------------------------------------------------------

----------------------------------------------------------------------------
flag5
morse code
flag5{eait}
----------------------------------------------------------------------------
flag6

flag6{590124}
视频开头 拨号盘
https://blog.xhyeax.com/2019/02/03/friend-ctf/
---------------------------------------------------------------------------
flag7
网页 二进制
flag7{5d06be63}
----------------------------------------------------------------------------
flag8
音频频谱
flag8{c394d7}
----------------------------------------------------------------------------
flag9
倒着放音频
flag9{21c5f8}
----------------------------------------------------------------------------

----------------------------------------------------------------------------
flag11
网页下方brainfuck
flag11{63418de7}
----------------------------------------------------------------------------
flagA
curl "https://2023challenge.52pojie.cn/?uid=1150835" \
  -H "X-52PoJie-Uid: 1150835" \
  -v
----------------------------------------------------------------------------
flagB
dns解析
dig 2023challenge.52pojie.cn @8.8.8.8 -t txt

2023challenge.52pojie.cn. 600	IN	TXT	"\"_52pojie_2023_happy_new_year=flagB{substr(md5(uid+\\\"_happy_new_year_\\\"+floor(timestamp/600)),0,8)}\""
----------------------------------------------------------------------------
flagC
jwt伪造
eyJ1aWQiOiIxMTUwODM1Iiwicm9sZSI6ImFkbWluIn0

curl 'https://2023challenge.52pojie.cn/home' \
  -H 'Cookie: 2023_challenge_jwt_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxMTUwODM1Iiwicm9sZSI6ImFkbWluIn0.U2Yd0PGhcJq_D0QAicnA8xTb1menooqWBaLCt65y1Cw' \
  -v

Refer

本文链接:https://blog.shi1011.cn/rev/android/2454
本文采用 CC BY-NC-SA 4.0 Unported 协议进行许可

Mas0n

文章作者

推荐文章

发表回复

textsms
account_circle
email

翻车鱼

吾爱破解【2023春节】解题领红包 WP
参加活动的第二个年头,今年的题不管是数量还是范围都多了不少,这意味着今年更卷了😂 这篇题解就放在博客作个记录,希望明年还能有空参加 Windows 初级题 1/23 cpp逆向,简…
扫描二维码继续阅读
2023-02-08