zzre / DeadSecCTF-2024-FlagChecker

1 stars 0 forks source link

Need Help #1

Open WannaSS1eep opened 1 month ago

WannaSS1eep commented 1 month ago

i wonder if you can describe bytecode_to_pyc.py with more detail?I am confused by why you write script like that

zzre commented 1 month ago

sub_2B3C (checker.check)

__int64 sub_2B3C()
{
  __int64 v0; // rax
  int i; // [rsp+0h] [rbp-10h]
  signed int j; // [rsp+4h] [rbp-Ch]
  __int64 t; // [rsp+8h] [rbp-8h]

  sub_1F3B();                                   // initialize components of code object
  for ( i = 0; i <= 2; ++i )
    sub_1DBC();                                 // create code object and calls `emulate`
  if ( !(qword_66F0 ^ 0xFDF61CB53A00DAA8LL | qword_66E8 ^ 0x273AED9AEFD29A3CLL | qword_66E0 ^ 0x7AB48E39E26BE2A7LL | qword_66F8 ^ 0xFC796489FC8864EELL) )
  {
    t = GetAttrString(module_main, t_encoded);  // PyObject_GetAttrString(module_main, "t")
    for ( j = 0; (unsigned int)j <= 0x1D; ++j )
    {
      v0 = PyLong_FromUnsignedLongLong(byte_6410[j]);
      PyList_SetItem(t, j, v0);
    }
  }
  return sub_13CF(&Py_NoneStruct);
}

sub_1DBC

void __fastcall sub_1DBC()
{
  v15 = PyBytes_FromString(&unk_3011);
  v0 = sub_1491(&unk_3011);
  v1 = sub_1491(&unk_3011);
  v2 = PyTuple_New(0LL);
  v3 = PyTuple_New(0LL);
  v4 = PyTuple_New(0LL);
  v14 = qword_6760[round];
  v13 = qword_6740[round];
  v5 = PyBytes_FromStringAndSize(qword_6440[round], dword_6430[round]);
  v6 = PyCode_New(0LL, 0LL, 0LL, 16LL, 0LL, v5, v13, v14, v4, v3, v2, v1, v0, 0LL, v15);
  v11 = PyTuple_Pack(1LL, v6, v7, v8, v9, v10, v12, v13, v14, v15, v16, v6, v17, v18);
  PyObject_CallObject(qword_6788, v11);
  ++round; // round (dword_6790) starts from 1 because `PyInit_checker` increments it
}

Based on Python3.10 PyCode_New API documentation, we can construct like this:

v6 = PyCode_New(
    argcount=0LL, 
    kwonlyargcount=0LL, 
    nlocals=0LL, 
    stacksize=16LL,
    flags=0LL,
    code=v5,            // PyBytes_FromStringAndSize(qword_6440[round], dword_6430[round]) -> qword_6440 : bytecodes, dword_6430 : size of bytecode
    consts=v13,         // qword_6740[round] -> consts
    names=v14,          // qword_6760[round] -> names
    varnames=v4,        // PyTuple_New(0LL)
    freevars=v3,        // PyTuple_New(0LL)
    cellvars=v2,        // PyTuple_New(0LL)
    filename=v1,        // PyUnicode_FromString("")
    name=v0,            // PyUnicode_FromString("")
    firstlineno=0LL,
    lnotab=v15          // PyBytes_FromString("")
);
.data:0000000000006430 dword_6430      dd 0, 0B14h, 3C4h, 424h

You can extract bytecodes with size dword_6430[round] from qword_6440[round]

sub_1F3B

void sub_1F3B()
{
  ...
  v1 = PyLong_FromUnsignedLongLong(255LL);
  v2 = PyLong_FromUnsignedLongLong(0LL);
  v3 = PyLong_FromUnsignedLongLong(6LL);
  v4 = PyLong_FromUnsignedLongLong(5LL);
  v5 = PyLong_FromUnsignedLongLong(4LL);
  v36 = v67;
  qword_6748 = PyTuple_Pack(39LL, &Py_NoneStruct, v5, v4, v3, v2, v1, v0, v55, v56, v57, v58, v59, v60);
  ...
  qword_6750 = PyTuple_Pack(50LL, &Py_NoneStruct, v11, v10, v9, v8, v7, v6, v40, v43, v46, v49, v51, v53);
  ...
  qword_6758 = PyTuple_Pack(12LL, &Py_NoneStruct, v17, v61, v62, v63, v16, v15, v14, v13, v12, v65, v68, v36);
  ...
  qword_6768 = PyTuple_Pack(7LL, v23, v22, v21, v20, v69, v19, v18, v37, v40, v43, v46, v49, v51);
  ...
  qword_6770 = PyTuple_Pack(9LL, v29, v28, v64, v66, v70, v27, v26, v25, v24, v38, v41, v44, v47);
  ...
  result = PyTuple_Pack(5LL, v34, v33, v32, v31, v30, v39, v42, v45, v48, v50, v52, v53, v54);
  qword_6778 = result;
}

Based on Python3.10 PyTuple_Pack API documentation, first argument of PyTuple_Pack is size of tuple.

qword_6748~qword_6758 is qword_6740[round] (round(dword_6790) starts from 1 because PyInit_checker increments it)

And qword_6768~qword_6778 is qword_6760[round].

So, you can get all co_code, co_consts and co_names as I wrote in bytecode_to_pyc.py.

WannaSS1eep commented 1 month ago

thxxxxxx it help a lot

---Original--- From: "Hyeongcheol @.> Date: Mon, Jul 29, 2024 23:35 PM To: @.>; Cc: @.**@.>; Subject: Re: [zzre/DeadSecCTF-2024-FlagChecker] Need Help (Issue #1)

sub_2B3C (checker.check) __int64 sub_2B3C() { int64 v0; // rax int i; // [rsp+0h] [rbp-10h] signed int j; // [rsp+4h] [rbp-Ch] int64 t; // [rsp+8h] [rbp-8h] sub_1F3B(); // initialize components of code object for ( i = 0; i <= 2; ++i ) sub_1DBC(); // create code object and calls emulate if ( !(qword_66F0 ^ 0xFDF61CB53A00DAA8LL | qword_66E8 ^ 0x273AED9AEFD29A3CLL | qword_66E0 ^ 0x7AB48E39E26BE2A7LL | qword_66F8 ^ 0xFC796489FC8864EELL) ) { t = GetAttrString(module_main, t_encoded); // PyObject_GetAttrString(module_main, "t") for ( j = 0; (unsigned int)j <= 0x1D; ++j ) { v0 = PyLong_FromUnsignedLongLong(byte_6410[j]); PyList_SetItem(t, j, v0); } } return sub_13CF(&Py_NoneStruct); }

sub_1DBC void __fastcall sub_1DBC() { v15 = PyBytes_FromString(&unk_3011); v0 = sub_1491(&unk_3011); v1 = sub_1491(&unk_3011); v2 = PyTuple_New(0LL); v3 = PyTuple_New(0LL); v4 = PyTuple_New(0LL); v14 = qword_6760[round]; v13 = qword_6740[round]; v5 = PyBytes_FromStringAndSize(qword_6440[round], dword_6430[round]); v6 = PyCode_New(0LL, 0LL, 0LL, 16LL, 0LL, v5, v13, v14, v4, v3, v2, v1, v0, 0LL, v15); v11 = PyTuple_Pack(1LL, v6, v7, v8, v9, v10, v12, v13, v14, v15, v16, v6, v17, v18); PyObject_CallObject(qword_6788, v11); ++round; // round (dword_6790) starts from 1 because PyInit }

Based on Python3.10 PyCode_New API documentation, we can construct like this: v6 = PyCode_New( argcount=0LL, kwonlyargcount=0LL, nlocals=0LL, stacksize=16LL, flags=0LL, code=v5, // PyBytes_FromStringAndSize(qword_6440[round], dword_6430[round]) -> qword_6440 : bytecodes, dword_6430 : size of bytecode consts=v13, // qword_6740[round] -> consts names=v14, // qword_6760[round] -> names varnames=v4, // PyTuple_New(0LL) freevars=v3, // PyTuple_New(0LL) cellvars=v2, // PyTuple_New(0LL) filename=v1, // PyUnicode_FromString("") name=v0, // PyUnicode_FromString("") firstlineno=0LL, lnotab=v15 // PyBytes_FromString("") ); .data:0000000000006430 dword_6430 dd 0, 0B14h, 3C4h, 424h
You can extract bytecodes with size dword_6430[round] from qword_6440[round]

sub_1F3B void sub_1F3B() { ... v1 = PyLong_FromUnsignedLongLong(255LL); v2 = PyLong_FromUnsignedLongLong(0LL); v3 = PyLong_FromUnsignedLongLong(6LL); v4 = PyLong_FromUnsignedLongLong(5LL); v5 = PyLong_FromUnsignedLongLong(4LL); v36 = v67; qword_6748 = PyTuple_Pack(39LL, &Py_NoneStruct, v5, v4, v3, v2, v1, v0, v55, v56, v57, v58, v59, v60); ... qword_6750 = PyTuple_Pack(50LL, &Py_NoneStruct, v11, v10, v9, v8, v7, v6, v40, v43, v46, v49, v51, v53); ... qword_6758 = PyTuple_Pack(12LL, &Py_NoneStruct, v17, v61, v62, v63, v16, v15, v14, v13, v12, v65, v68, v36); ... qword_6768 = PyTuple_Pack(7LL, v23, v22, v21, v20, v69, v19, v18, v37, v40, v43, v46, v49, v51); ... qword_6770 = PyTuple_Pack(9LL, v29, v28, v64, v66, v70, v27, v26, v25, v24, v38, v41, v44, v47); ... result = PyTuple_Pack(5LL, v34, v33, v32, v31, v30, v39, v42, v45, v48, v50, v52, v53, v54); qword_6778 = result; }

Based on Python3.10 PyTuple_Pack API documentation, first argument of PyTuple_Pack is size of tuple.

qword_6748~qword_6758 is qword_6740[round] (round(dword_6790) starts from 1 because PyInit_checker increments it)

And qword_6768~qword_6778 is qword_6760[round].

So, you can get all co_code, co_consts and co_names as I wrote in bytecode_to_pyc.py.

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you authored the thread.Message ID: @.***>