huge commit
This commit is contained in:
1
2026/cscg/rev/go-backup/.python-version
Normal file
1
2026/cscg/rev/go-backup/.python-version
Normal file
@@ -0,0 +1 @@
|
||||
3.13
|
||||
0
2026/cscg/rev/go-backup/GoBackup.gpr
Normal file
0
2026/cscg/rev/go-backup/GoBackup.gpr
Normal file
11
2026/cscg/rev/go-backup/GoBackup.rep/idata/00/00000000.prp
Normal file
11
2026/cscg/rev/go-backup/GoBackup.rep/idata/00/00000000.prp
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<FILE_INFO>
|
||||
<BASIC_INFO>
|
||||
<STATE NAME="CONTENT_TYPE" TYPE="string" VALUE="Program" />
|
||||
<STATE NAME="PARENT" TYPE="string" VALUE="/" />
|
||||
<STATE NAME="FILE_ID" TYPE="string" VALUE="c0a803683f527947006418077" />
|
||||
<STATE NAME="FILE_TYPE" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="READ_ONLY" TYPE="boolean" VALUE="false" />
|
||||
<STATE NAME="NAME" TYPE="string" VALUE="backup" />
|
||||
</BASIC_INFO>
|
||||
</FILE_INFO>
|
||||
Binary file not shown.
4
2026/cscg/rev/go-backup/GoBackup.rep/idata/~index.bak
Normal file
4
2026/cscg/rev/go-backup/GoBackup.rep/idata/~index.bak
Normal file
@@ -0,0 +1,4 @@
|
||||
VERSION=1
|
||||
/
|
||||
NEXT-ID:0
|
||||
MD5:d41d8cd98f00b204e9800998ecf8427e
|
||||
5
2026/cscg/rev/go-backup/GoBackup.rep/idata/~index.dat
Normal file
5
2026/cscg/rev/go-backup/GoBackup.rep/idata/~index.dat
Normal file
@@ -0,0 +1,5 @@
|
||||
VERSION=1
|
||||
/
|
||||
00000000:backup:c0a803683f527947006418077
|
||||
NEXT-ID:1
|
||||
MD5:d41d8cd98f00b204e9800998ecf8427e
|
||||
2
2026/cscg/rev/go-backup/GoBackup.rep/idata/~journal.bak
Normal file
2
2026/cscg/rev/go-backup/GoBackup.rep/idata/~journal.bak
Normal file
@@ -0,0 +1,2 @@
|
||||
IADD:00000000:/backup
|
||||
IDSET:/backup:c0a803683f527947006418077
|
||||
6
2026/cscg/rev/go-backup/GoBackup.rep/project.prp
Normal file
6
2026/cscg/rev/go-backup/GoBackup.rep/project.prp
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<FILE_INFO>
|
||||
<BASIC_INFO>
|
||||
<STATE NAME="OWNER" TYPE="string" VALUE="cato" />
|
||||
</BASIC_INFO>
|
||||
</FILE_INFO>
|
||||
15
2026/cscg/rev/go-backup/GoBackup.rep/projectState
Normal file
15
2026/cscg/rev/go-backup/GoBackup.rep/projectState
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<PROJECT>
|
||||
<PROJECT_DATA_XML_NAME NAME="DISPLAY_DATA">
|
||||
<SAVE_STATE>
|
||||
<ARRAY NAME="EXPANDED_PATHS" TYPE="string">
|
||||
<A VALUE="GoBackup:" />
|
||||
</ARRAY>
|
||||
<STATE NAME="SHOW_TABLE" TYPE="boolean" VALUE="false" />
|
||||
</SAVE_STATE>
|
||||
</PROJECT_DATA_XML_NAME>
|
||||
<TOOL_MANAGER ACTIVE_WORKSPACE="Workspace">
|
||||
<WORKSPACE NAME="Workspace" ACTIVE="true" />
|
||||
</TOOL_MANAGER>
|
||||
</PROJECT>
|
||||
|
||||
11
2026/cscg/rev/go-backup/GoBackup.rep/user/00/00000000.prp
Normal file
11
2026/cscg/rev/go-backup/GoBackup.rep/user/00/00000000.prp
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<FILE_INFO>
|
||||
<BASIC_INFO>
|
||||
<STATE NAME="CONTENT_TYPE" TYPE="string" VALUE="ProgramUserData" />
|
||||
<STATE NAME="PARENT" TYPE="string" VALUE="/" />
|
||||
<STATE NAME="FILE_ID" TYPE="string" VALUE="c0a8036965928179814879296" />
|
||||
<STATE NAME="FILE_TYPE" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="READ_ONLY" TYPE="boolean" VALUE="false" />
|
||||
<STATE NAME="NAME" TYPE="string" VALUE="udf_c0a803683f527947006418077" />
|
||||
</BASIC_INFO>
|
||||
</FILE_INFO>
|
||||
Binary file not shown.
4
2026/cscg/rev/go-backup/GoBackup.rep/user/~index.dat
Normal file
4
2026/cscg/rev/go-backup/GoBackup.rep/user/~index.dat
Normal file
@@ -0,0 +1,4 @@
|
||||
VERSION=1
|
||||
/
|
||||
NEXT-ID:0
|
||||
MD5:d41d8cd98f00b204e9800998ecf8427e
|
||||
2
2026/cscg/rev/go-backup/GoBackup.rep/user/~journal.dat
Normal file
2
2026/cscg/rev/go-backup/GoBackup.rep/user/~journal.dat
Normal file
@@ -0,0 +1,2 @@
|
||||
IADD:00000000:/udf_c0a803683f527947006418077
|
||||
IDSET:/udf_c0a803683f527947006418077:c0a8036965928179814879296
|
||||
@@ -0,0 +1,4 @@
|
||||
VERSION=1
|
||||
/
|
||||
NEXT-ID:0
|
||||
MD5:d41d8cd98f00b204e9800998ecf8427e
|
||||
@@ -0,0 +1,4 @@
|
||||
VERSION=1
|
||||
/
|
||||
NEXT-ID:0
|
||||
MD5:d41d8cd98f00b204e9800998ecf8427e
|
||||
0
2026/cscg/rev/go-backup/README.md
Normal file
0
2026/cscg/rev/go-backup/README.md
Normal file
BIN
2026/cscg/rev/go-backup/backup
Executable file
BIN
2026/cscg/rev/go-backup/backup
Executable file
Binary file not shown.
BIN
2026/cscg/rev/go-backup/backup.i64
Normal file
BIN
2026/cscg/rev/go-backup/backup.i64
Normal file
Binary file not shown.
BIN
2026/cscg/rev/go-backup/backup.pdf
Normal file
BIN
2026/cscg/rev/go-backup/backup.pdf
Normal file
Binary file not shown.
11
2026/cscg/rev/go-backup/data.md
Normal file
11
2026/cscg/rev/go-backup/data.md
Normal file
@@ -0,0 +1,11 @@
|
||||
EANs: [4932413071222 9215337181228 4055572232713 3177777536937 5194353145886 8022631001531 2177559451930 9375914288310]
|
||||
ISBNs:
|
||||
ISBN 0-024-00310-7
|
||||
ISBN 0-238-30610-0
|
||||
ISBN 3-505-52102-7
|
||||
ISBN 0-022-11111-5
|
||||
ISBN 6-052-75101-0
|
||||
ISBN 8-323-17020-7
|
||||
ISBN 6-422-29100-0
|
||||
ISBN 5-112-73112-5
|
||||
Credit Cards: [5217804766545209 4885373827486781 5839899598496843 9587981988999895 8837998849894933 8986989999966281 9999992947693935 99999489398999945]
|
||||
71
2026/cscg/rev/go-backup/decode.py
Normal file
71
2026/cscg/rev/go-backup/decode.py
Normal file
@@ -0,0 +1,71 @@
|
||||
EANS = [
|
||||
"4932413071222",
|
||||
"9215337181228",
|
||||
"4055572232713",
|
||||
"3177777536937",
|
||||
"5194353145886",
|
||||
"8022631001531",
|
||||
"2177559451930",
|
||||
"9375914288310",
|
||||
]
|
||||
|
||||
ISBNS = [
|
||||
"0-024-00310-7",
|
||||
"0-238-30610-0",
|
||||
"3-505-52102-7",
|
||||
"0-022-11111-5",
|
||||
"6-052-75101-0",
|
||||
"8-323-17020-7",
|
||||
"6-422-29100-0",
|
||||
"5-112-73112-5",
|
||||
]
|
||||
|
||||
cards = [
|
||||
"5217804766545209",
|
||||
"4885373827486781",
|
||||
"5839899598496843",
|
||||
"9587981988999895",
|
||||
"8837998849894933",
|
||||
"8986989999966281",
|
||||
"9999992947693935",
|
||||
"9999489398999945",
|
||||
]
|
||||
|
||||
|
||||
def decode_ean(ean):
|
||||
digits = [int(c) for c in ean[:12]]
|
||||
total = 0
|
||||
for i, d in enumerate(digits):
|
||||
if i % 2 != 0:
|
||||
total += d * 3
|
||||
else:
|
||||
total += d
|
||||
return chr(total)
|
||||
|
||||
|
||||
def decode_isbn(isbn):
|
||||
digits = [int(c) for c in isbn.replace("-", "")[:9]]
|
||||
total = sum((i + 1) * d for i, d in enumerate(digits))
|
||||
return chr(total)
|
||||
|
||||
|
||||
def decode_credit_card(cc):
|
||||
digits = [int(c) for c in cc[:15]]
|
||||
total = 0
|
||||
for idx in range(14, -1, -1):
|
||||
d = digits[14 - idx]
|
||||
weight = 14 - idx
|
||||
if weight % 2 != 0:
|
||||
total += d
|
||||
else:
|
||||
doubled = 2 * d
|
||||
total += doubled - 9 if doubled > 9 else doubled
|
||||
return chr(total)
|
||||
|
||||
|
||||
part1 = "".join(decode_ean(i) for i in EANS)
|
||||
part2 = "".join(decode_isbn(i) for i in ISBNS)
|
||||
part3 = "".join(decode_credit_card(c) for c in cards)
|
||||
|
||||
|
||||
print(part1 + part2 + part3)
|
||||
81
2026/cscg/rev/go-backup/go_types.h
Normal file
81
2026/cscg/rev/go-backup/go_types.h
Normal file
@@ -0,0 +1,81 @@
|
||||
// ============================================
|
||||
// GO RUNTIME TYPES FOR IDA PRO
|
||||
// ============================================
|
||||
|
||||
// --------------------------------------------
|
||||
// BASIC TYPES
|
||||
// --------------------------------------------
|
||||
typedef unsigned char uint8;
|
||||
typedef unsigned short uint16;
|
||||
typedef unsigned int uint32;
|
||||
typedef unsigned long long uint64;
|
||||
typedef signed char int8;
|
||||
typedef short int16;
|
||||
typedef int int32;
|
||||
typedef long long int64;
|
||||
typedef int32 GoRune; // Unicode code point
|
||||
|
||||
// --------------------------------------------
|
||||
// STRING
|
||||
// A string is just (ptr, len) - immutable
|
||||
// --------------------------------------------
|
||||
typedef struct {
|
||||
void *ptr; // pointer to bytes
|
||||
int64 len; // number of bytes
|
||||
} GoString;
|
||||
|
||||
// --------------------------------------------
|
||||
// SLICES
|
||||
// Every slice is (ptr, len, cap)
|
||||
// --------------------------------------------
|
||||
typedef struct {
|
||||
void *ptr;
|
||||
int64 len;
|
||||
int64 cap;
|
||||
} GoSlice;
|
||||
|
||||
// Typed slice variants for readability
|
||||
typedef struct {
|
||||
int32 *ptr;
|
||||
int64 len;
|
||||
int64 cap;
|
||||
} GoSlice_rune; // []rune / []int32
|
||||
|
||||
typedef struct {
|
||||
GoString *ptr;
|
||||
int64 len;
|
||||
int64 cap;
|
||||
} GoSlice_string; // []string
|
||||
|
||||
typedef struct {
|
||||
int64 *ptr;
|
||||
int64 len;
|
||||
int64 cap;
|
||||
} GoSlice_int64; // []int64
|
||||
|
||||
// --------------------------------------------
|
||||
// TIME
|
||||
// --------------------------------------------
|
||||
typedef struct {
|
||||
uint64 wall; // [63]=hasMonotonic [62:30]=secs_since_1885 [29:0]=nanosecs
|
||||
int64 ext; // monotonic ns since process start, OR full wall seconds
|
||||
void *loc; // *Location, NULL = UTC
|
||||
} GoTime;
|
||||
|
||||
// --------------------------------------------
|
||||
// OS.ARGS (lives in .bss)
|
||||
// --------------------------------------------
|
||||
typedef struct {
|
||||
GoString *ptr; // pointer to []string data
|
||||
int64 len; // number of arguments
|
||||
int64 cap; // capacity
|
||||
} GoArgs;
|
||||
|
||||
// --------------------------------------------
|
||||
// INTERFACE
|
||||
// Every interface is (type_ptr, data_ptr)
|
||||
// --------------------------------------------
|
||||
typedef struct {
|
||||
void *type; // *_type (runtime type descriptor)
|
||||
void *data; // pointer to actual value
|
||||
} GoInterface;
|
||||
192
2026/cscg/rev/go-backup/goresym_rename.py
Normal file
192
2026/cscg/rev/go-backup/goresym_rename.py
Normal file
@@ -0,0 +1,192 @@
|
||||
# Copyright (C) 2022 Mandiant, Inc. All Rights Reserved.
|
||||
import atexit
|
||||
import json
|
||||
import os
|
||||
CLI_AVAILABLE = True
|
||||
try:
|
||||
import idapro
|
||||
except ImportError:
|
||||
CLI_AVAILABLE = False
|
||||
import idaapi
|
||||
import ida_bytes
|
||||
import ida_funcs
|
||||
import ida_name
|
||||
import ida_typeinf
|
||||
import ida_kernwin
|
||||
|
||||
|
||||
def iterable(obj):
|
||||
if obj is None:
|
||||
return False
|
||||
|
||||
try:
|
||||
iter(obj)
|
||||
except Exception:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
# https://gist.github.com/NyaMisty/693db2ce2e75c230f36b628fd7610852
|
||||
# 'Synchonize to idb' right click equivalent
|
||||
def resync_local_types():
|
||||
def is_autosync(name, tif):
|
||||
return idaapi.get_ordinal_from_idb_type(name, tif.get_decltype().to_bytes(1, "little")) != -1
|
||||
|
||||
for ord in range(1, idaapi.get_ordinal_qty(None)):
|
||||
t = idaapi.tinfo_t()
|
||||
t.get_numbered_type(None, ord)
|
||||
typename = t.get_type_name()
|
||||
if typename.startswith("#"):
|
||||
continue
|
||||
|
||||
autosync = is_autosync(typename, t)
|
||||
# print('Processing struct %d: %s%s' % (ord, typename, ' (autosync) ' if autosync else ''))
|
||||
idaapi.import_type(None, -1, typename, idaapi.IMPTYPE_OVERRIDE)
|
||||
if autosync:
|
||||
continue
|
||||
struc = idaapi.get_struc(idaapi.get_struc_id(typename))
|
||||
if not struc:
|
||||
continue
|
||||
struc.ordinal = -1
|
||||
idaapi.save_struc(struc, False)
|
||||
|
||||
|
||||
def get_type_by_name(name):
|
||||
t = idaapi.tinfo_t()
|
||||
t.get_named_type(None, name)
|
||||
return t
|
||||
|
||||
|
||||
def set_function_signature(ea, typedef):
|
||||
idaapi.apply_type(ea, ida_typeinf.parse_decl(typedef, ida_typeinf.PT_SIL), idaapi.TINFO_DEFINITE)
|
||||
|
||||
|
||||
def import_primitives():
|
||||
type_map = {
|
||||
"BUILTIN_STRING": "string",
|
||||
"uint8_t": "uint8",
|
||||
"uint16_t": "uint16",
|
||||
"uint32_t": "uint32",
|
||||
"uint64_t": "uint64",
|
||||
"int8_t": "int8",
|
||||
"int16_t": "int16",
|
||||
"int32_t": "int32",
|
||||
"double": "float64",
|
||||
"float": "float32",
|
||||
"complex64_t": "complex64",
|
||||
"complex128_t": "complex128",
|
||||
"void*": "uintptr", # should be uint64 or uint32 depending on ptr size, but this works too
|
||||
"uint8": "byte",
|
||||
"int32": "rune",
|
||||
"int": "void*", # int in GO depends on architecture size
|
||||
}
|
||||
|
||||
ida_typeinf.idc_parse_types("struct BUILTIN_INTERFACE{void *tab;void *data;};", ida_typeinf.HTI_PAKDEF | ida_typeinf.HTI_DCL)
|
||||
ida_typeinf.idc_parse_types("struct BUILTIN_STRING{char *ptr;size_t len;};", ida_typeinf.HTI_PAKDEF | ida_typeinf.HTI_DCL)
|
||||
|
||||
ida_typeinf.idc_parse_types("struct complex64_t{float real;float imag;};", ida_typeinf.HTI_PAKDEF | ida_typeinf.HTI_DCL)
|
||||
ida_typeinf.idc_parse_types("struct complex128_t{double real;double imag;};", ida_typeinf.HTI_PAKDEF | ida_typeinf.HTI_DCL)
|
||||
|
||||
for ida_type, gotype in type_map.items():
|
||||
ida_typeinf.idc_parse_types(f"typedef {ida_type} {gotype};", ida_typeinf.HTI_PAKDEF | ida_typeinf.HTI_DCL)
|
||||
|
||||
|
||||
def forward_declare_structs(types):
|
||||
for typ in types:
|
||||
if typ["Kind"] == "Struct":
|
||||
ida_typeinf.idc_parse_types(f"struct {typ['CStr']};", ida_typeinf.HTI_PAKDEF | ida_typeinf.HTI_DCL)
|
||||
|
||||
|
||||
def main(json_file):
|
||||
with open(json_file, "r", encoding="utf-8") as rp:
|
||||
buf = rp.read()
|
||||
|
||||
hints = json.loads(buf)
|
||||
if iterable(hints["UserFunctions"]):
|
||||
for func in hints["UserFunctions"]:
|
||||
ida_bytes.del_items(func["Start"])
|
||||
ida_funcs.add_func(func["Start"])
|
||||
print("Renaming %s to %s" % (hex(func["Start"]), func["FullName"]))
|
||||
idaapi.add_func(func["Start"], func["End"])
|
||||
idaapi.set_name(func["Start"], func["FullName"], idaapi.SN_NOWARN | idaapi.SN_NOCHECK | ida_name.SN_FORCE)
|
||||
|
||||
if iterable(hints["StdFunctions"]):
|
||||
for func in hints["StdFunctions"]:
|
||||
print("Renaming %s to %s" % (hex(func["Start"]), func["FullName"]))
|
||||
ida_bytes.del_items(func["Start"])
|
||||
ida_funcs.add_func(func["Start"])
|
||||
idaapi.add_func(func["Start"], func["End"])
|
||||
idaapi.set_name(func["Start"], func["FullName"], idaapi.SN_NOWARN | idaapi.SN_NOCHECK | ida_name.SN_FORCE)
|
||||
|
||||
if iterable(hints["Types"]):
|
||||
import_primitives()
|
||||
|
||||
# we must do this to prevent IDA from creating an invalid struct of type int when we import things like typedef <class>* <newname>.
|
||||
# it would have made typedef struct <class> int; without a forward declaration. That would then break importing the class later with redefinition error.
|
||||
forward_declare_structs(hints["Types"])
|
||||
|
||||
for typ in hints["Types"][::-1]:
|
||||
if typ.get("CReconstructed"):
|
||||
errors = ida_typeinf.idc_parse_types(typ["CReconstructed"] + ";", ida_typeinf.HTI_PAKDEF | ida_typeinf.HTI_DCL)
|
||||
if errors > 0:
|
||||
print(typ["CReconstructed"], "failed to import")
|
||||
|
||||
# just for precation
|
||||
# resync_local_types()
|
||||
|
||||
for typ in hints["Types"]:
|
||||
print("Renaming %s to %s" % (hex(typ["VA"]), typ["Str"]))
|
||||
idaapi.set_name(typ["VA"], typ["Str"], idaapi.SN_NOWARN | idaapi.SN_NOCHECK | ida_name.SN_FORCE)
|
||||
|
||||
# IDA often thinks these are string pointers, lets undefine that, then set the type correctly
|
||||
ida_bytes.del_items(typ["VA"], 0, 4)
|
||||
abi_typ = get_type_by_name("abi_Type")
|
||||
idaapi.apply_tinfo(typ["VA"], abi_typ, idaapi.TINFO_DEFINITE)
|
||||
|
||||
if iterable(hints["Interfaces"]):
|
||||
for typ in hints["Interfaces"]:
|
||||
print("Renaming %s to %s" % (hex(typ["VA"]), typ["Str"]))
|
||||
idaapi.set_name(typ["VA"], typ["Str"], idaapi.SN_NOWARN | idaapi.SN_NOCHECK | ida_name.SN_FORCE)
|
||||
|
||||
# IDA often thinks these are string pointers, lets undefine that, then set the type correctly
|
||||
ida_bytes.del_items(typ["VA"], 0, 4)
|
||||
abi_typ = get_type_by_name("abi_Type")
|
||||
idaapi.apply_tinfo(typ["VA"], abi_typ, idaapi.TINFO_DEFINITE)
|
||||
|
||||
if hints["TabMeta"] is not None:
|
||||
tabmeta = hints["TabMeta"]
|
||||
va = tabmeta["VA"]
|
||||
if va is not None and va != 0:
|
||||
idaapi.set_name(va, "runtime_pclntab", idaapi.SN_NOWARN | idaapi.SN_NOCHECK | ida_name.SN_FORCE)
|
||||
|
||||
if hints["ModuleMeta"] is not None:
|
||||
modmeta = hints["ModuleMeta"]
|
||||
va = modmeta["VA"]
|
||||
if va is not None and va != 0:
|
||||
idaapi.set_name(va, "runtime_firstmoduledata", idaapi.SN_NOWARN | idaapi.SN_NOCHECK | ida_name.SN_FORCE)
|
||||
|
||||
|
||||
def getargs() -> str:
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description="Apply GoReSym renaming and type hints to an IDA database.")
|
||||
parser.add_argument("binary", help="Path to the Go binary file.")
|
||||
parser.add_argument("json_file", help="Path to the GoReSym output JSON file.")
|
||||
args = parser.parse_args()
|
||||
assert os.path.isfile(args.json_file)
|
||||
assert os.path.isfile(args.binary)
|
||||
idapro.open_database(args.binary, run_auto_analysis=True)
|
||||
atexit.register(idapro.close_database, True)
|
||||
return args.json_file
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
json_path = None
|
||||
if CLI_AVAILABLE and ida_kernwin.is_ida_library(None, 0, None):
|
||||
json_path = getargs()
|
||||
else:
|
||||
json_path = ida_kernwin.ask_file(0, "*.json", "GoReSym output file")
|
||||
assert json_path is not None and os.path.isfile(json_path)
|
||||
|
||||
main(json_path)
|
||||
58367
2026/cscg/rev/go-backup/gosym.json
Normal file
58367
2026/cscg/rev/go-backup/gosym.json
Normal file
File diff suppressed because it is too large
Load Diff
6
2026/cscg/rev/go-backup/main.py
Normal file
6
2026/cscg/rev/go-backup/main.py
Normal file
@@ -0,0 +1,6 @@
|
||||
def main():
|
||||
print("Hello from go-backup!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
7
2026/cscg/rev/go-backup/pyproject.toml
Normal file
7
2026/cscg/rev/go-backup/pyproject.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[project]
|
||||
name = "go-backup"
|
||||
version = "0.1.0"
|
||||
description = "Add your description here"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.13"
|
||||
dependencies = []
|
||||
Reference in New Issue
Block a user