首页
社区
课程
招聘
[原创]SCTF low_re出题思路
2022-1-4 19:43 13514

[原创]SCTF low_re出题思路

2022-1-4 19:43
13514

SCTF low_re

前言

这个题其实算是逆向题, 考虑到这个题不是常规逆向思路, 并且有点谜语, 所以我把这个题放misc里面了.

 

出题人比较菜, 如果给师傅们造成了不好的体验, 轻点骂, orz.

 

这个题目的预期解是通过pintool之类的插桩工具进行指令计数来进行爆破, 但是我也看到有一些师傅使用钩子来获得信息的一些思路, 甚至于做了一些vmp的逆向, 师傅们tql.

出题思路

首先python源代码

这里是单字节加密(所以可以使用侧信道爆破)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import hashlib
import sys
 
def _rc4_crypt(key, data, dataLen):
    out = []
    s_box = list(range(256))
    j = 0
    for i in range(256):
        j = (j + s_box[i] + ord(key[i % len(key)])) % 256
        s_box[i], s_box[j] = s_box[j], s_box[i]
 
    for x in range(dataLen):
        i = (i + 1) % 256
        j = (j + s_box[i]) % 256
        t = s_box[i]
        s_box[i] = s_box[j]
        s_box[j] = t
        ti = (s_box[i] + (s_box[j] % 256)) % 256
        t = s_box[ti]
        while (len(out) < x + 1):
            out.append(0)
        out[x] = data[x] ^ t
    return out
 
def linux_srand(seed):
    if seed == 0:
        seed = 1
    word = seed
    seed = seed & 0xffffffff
    global linux_status
    global linux_r
    linux_status = 0
    linux_r = [0] * (344 + linux_status)
    linux_r[0] = seed
    for i in range(1, 31):
        if (word < 0):
            hi = (-word) // 127773
            hi = -hi
            lo = (-word) % 127773
            lo = -lo
        else:
            hi = word // 127773
            lo = word % 127773
        word = ((16807 * lo)) - ((2836 * hi))
        if word < 0:
            word = (2147483647 + word) & 0xffffffff
        linux_r[i] = word
    for i in range(31, 34):
        linux_r[i] = linux_r[i - 31]
    for i in range(34, 344):
        linux_r[i] = (((linux_r[i - 31] + linux_r[i - 3]) & 0xffffffff) % (1 << 32)) & 0xffffffff
 
def linux_rand():
    global linux_status
    global linux_r
    linux_r.append(0)
    linux_r[344 + linux_status] = (((linux_r[344 + linux_status - 31] + linux_r[344 + linux_status - 3]) & 0xffffffff) % (1 << 32)) & 0xffffffff
    linux_status += 1
    return linux_r[344 + linux_status - 1] >> 1
print("hello challanger")
flag = input("please input your flag:\n")
if (type(flag) != str):
    print("error")
    sys.exit()
if (len(flag) != 17):
    sys.exit()
flag = list(bytes(flag, encoding='utf-8'))
flag = _rc4_crypt('Sycl0ver', flag, len(flag))
for i in range(len(flag)):
    linux_srand(flag[i])
    flag[i] = str(linux_rand())
ciphertext=['ee197bbac1b0e09c425e1dfd30cea2506bd493a674c4de90d9afbe5abc700b06',
'1a6aafb16a23ffde40c426d5c87f5afcc77fffc96cf041dc8dd2c47e706a7ecb',
'62c62ce7768a4836b10495317a32da6e3943d522bc3b9797ff0a44931e966a31',
'e6222354b50e4d33d73314b515b325633e57a105758e20aca23eb2dadd625f3f',
'78f92a6ad9ffcec47f30e3ca3d18065bdba9c020ff5f477b801d11efdfaa9cd0',
'127291de1f4cbbb35c41556a3c6d5a64f08661bc7ed394ea6210354e6218ad93',
'62c62ce7768a4836b10495317a32da6e3943d522bc3b9797ff0a44931e966a31',
'52080868c07a9ef5646b5f0b198f04f013cf23cfbfb06123d8f2fdd63d359123',
'f69b52599973fc5915ad1d435236863252dc3fd460989bd9f56ffc199ef8ff36',
'e9552f8c3e518306524fa9c9728ad6dee88fa611aa3068c169217f173964f9b4',
'54cb43f463ea082699131b71d45fb0384f8c2f598e8f0072b960b4add731e048',
'97e45e15c74f71ea59ffffb40298f2e5dec119c2205e434e3a0d2510c331037f',
'51b7d78cfe25ede262fd85a65b24721f076ab9dd6562403878ca5cde1ebf3219',
'a1cd6c7990abb6b271695381d78898ec5c4880fbc0f6a0c9fda064422f21361e',
'85ddd3721d173367465373f75e190bd937a8dc3588d5d82ebff8104dec88ac3e',
'd6eeac4ea40f9513391ef0bf72aa2fd2588889cb9d5f4cc638ce4d2c5509527b',
'5023939dca9273fd767d5e4ea329846f9816af461e170b6db8d20b6e5ff3de8c']
for i in range(len(flag)):
    for j in range(2560):
        s = hashlib.sha256()
        s.update(bytes(flag[i], encoding='utf-8'))
        flag[i] = s.hexdigest()
    #print("'" + flag[i]+ "',")
    if (flag[i] != ciphertext[i]):
        sys.exit()
print("you are right")
sys.exit()

混淆python

使用python pyminifier的混淆功能去混淆python代码

 

虽然有一种乱码混淆, 但是可能会导致这样那样的问题, 我就没有使用了

1
pyminifier --obfuscate

混淆出来之后可能点错误, 手动改改就ok了.

转化为C代码并且编译

在文件的开头记得加上python3的声明 不然会转化失败

1
# cython: language_level=3

网上python的一个GCC编译exe的脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import subprocess
import sys
import tempfile
from Cython.Compiler import Main, CmdLine, Options
 
in_file_name = sys.argv[1]
source = open(in_file_name).read()
out_file_name = in_file_name.replace('.py', '.exe')
 
temp_py_file = tempfile.NamedTemporaryFile(suffix='.py', delete=False)
temp_py_file.write(source.encode())
temp_py_file.flush()
 
Main.Options.embed = 'main'
res = Main.compile_single(temp_py_file.name, Main.CompilationOptions(), '')
 
gcc_cmd = 'gcc -static -municode -DMS_WIN64 -fPIC -O2 %s -Id:\\Python\\Python38\\include -Ld:\\Python\\Python38\\libs -lpython38 -o %s' % (res.c_file, out_file_name)
 
print(gcc_cmd)
assert 0 == subprocess.check_call(gcc_cmd.split(' '))

这里的unicode是因为winmain, 如果不用unicode会报错.

 

这样就生成了exe

vmp加壳

网上找一个"学习"版的加壳软件, 关闭内存保护, 这样就可以进行插桩了.

题解

侧信道爆破工具构建

师傅们基本上都是用的官方给的代码来构建, 但是pin官方给的指令计数是算了库的, 这样的话计算出来的指令正常波动就会非常大, 所以我使用了2560次的hash加密, 希望能够使得正确答案的指令大小更为明显, 但是我自己爆破的时候, 如果从头开始爆破, 在得到最后几个字符的时候可能会发生错误. 在和小组另外一个师傅@Cr0ssx2交流之后, 发现如果不计算库的时候指令波动会非常小.

pin

下载地址https://www.intel.com/content/www/us/en/developer/articles/tool/pin-a-binary-instrumentation-tool-downloads.html

 

pin工具可以实现不算库的爆破, 但是官方好像没有给不计算库的指令的代码, 需要自己去写判断, 这里我做的限制条件是 exe的首地址 <= 需要计数指令的地址 <= exe的结束地址.

 

source/MyPinTool/MyPinTool.cpp: (用VS打开.vcxproj后缀文件就可以进行写代码了)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/*
 * Copyright 2002-2020 Intel Corporation.
 *
 * This software is provided to you as Sample Source Code as defined in the accompanying
 * End User License Agreement for the Intel(R) Software Development Products ("Agreement")
 * section 1.L.
 *
 * This software and the related documents are provided as is, with no express or implied
 * warranties, other than those that are expressly stated in the License.
 */
 
 /*! @file
  *  This file contains an ISA-portable PIN tool for counting dynamic instructions
  */
 
#include "pin.H"
#include <iostream>
using std::cerr;
using std::endl;
 
/* ===================================================================== */
/* Global Variables */
/* ===================================================================== */
 
UINT64 ins_count = 0;
bool runing = false;
UINT64 exe_start;
UINT64 exe_size;
/* ===================================================================== */
/* Commandline Switches */
/* ===================================================================== */
 
/* ===================================================================== */
/* Print Help Message                                                    */
/* ===================================================================== */
 
INT32 Usage()
{
    cerr << "This tool prints out the number of dynamic instructions executed to stderr.\n"
        "\n";
 
    cerr << KNOB_BASE::StringKnobSummary();
 
    cerr << endl;
 
    return -1;
}
 
/* ===================================================================== */
 
VOID docount() { ins_count++; }
 
/* ===================================================================== */
 
VOID Instruction(INS ins, VOID* v)
{
    ADDRINT addr = INS_Address(ins);
    if (exe_start <= addr && addr <= exe_start + exe_size) //判断指令是否在库中, 若不在, 则进行插桩
        INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_END);
}
VOID Image(IMG img, VOID* v)
{
    if (IMG_IsMainExecutable(img))
    {
        exe_start = IMG_StartAddress(img);
        exe_size = IMG_SizeMapped(img);
    }
}
 
/* ===================================================================== */
 
VOID Fini(INT32 code, VOID* v) {
    cerr << "Count " << ins_count << " " << exe_start << " " << exe_size + exe_start << endl;
}
 
/* ===================================================================== */
/* Main                                                                  */
/* ===================================================================== */
 
int main(int argc, char* argv[])
{
    if (PIN_Init(argc, argv))
    {
        return Usage();
    }
    IMG_AddInstrumentFunction(Image, 0);
    INS_AddInstrumentFunction(Instruction, 0);
    PIN_AddFiniFunction(Fini, 0);
 
    // Never returns
    PIN_StartProgram();
 
    return 0;
}
 
/* ===================================================================== */
/* eof */
/* ===================================================================== */
dynamorio

另外一款插桩工具dynamorio.这款工具的爆破其实比pin工具更快, 但是inscount.cpp的指令输出是消息框, 需要自己做一些改动.

 

这款工具的官方给的代码是有不计算库的选项的.

 

build官方文档:https://dynamorio.org/page_building.html

1
2
3
4
git clone https://github.com/DynamoRIO/dynamorio.git
cd dynamorio && mkdir build && cd build
cmake -G"Visual Studio 15" .. #从这里指定你的VS版本, 2019为16, 2022为17 后面跟上dynamorio的源路径
cmake --build . --config RelWithDebInfo

接下来直接打开build/api/samples/DynamoRIO_samples.sln就可以进行写代码编译了

 

修改后的inscount.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
/* ******************************************************************************
 * Copyright (c) 2014-2018 Google, Inc.  All rights reserved.
 * Copyright (c) 2011 Massachusetts Institute of Technology  All rights reserved.
 * Copyright (c) 2008 VMware, Inc.  All rights reserved.
 * ******************************************************************************/
 
/*
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * * Neither the name of VMware, Inc. nor the names of its contributors may be
 *   used to endorse or promote products derived from this software without
 *   specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */
 
/* Code Manipulation API Sample:
 * inscount.cpp
 *
 * Reports the dynamic count of the total number of instructions executed.
 * Illustrates how to perform performant clean calls.
 * Demonstrates effect of clean call optimization and auto-inlining with
 * different -opt_cleancall values.
 *
 * The runtime options for this client include:
 *   -only_from_app  Do not count instructions in shared libraries.
 * The options are handled using the droption extension.
 */
 
#include "dr_api.h"
#include "drmgr.h"
#include "droption.h"
#include <string.h>
#include<iostream>
 
#ifdef WINDOWS
#    define DISPLAY_STRING(msg) dr_messagebox(msg)
#else
#    define DISPLAY_STRING(msg) dr_printf("%s\n", msg);
#endif
 
#define NULL_TERMINATE(buf) (buf)[(sizeof((buf)) / sizeof((buf)[0])) - 1] = '\0'
 
static droption_t<bool> only_from_app(
    DROPTION_SCOPE_CLIENT, "only_from_app", false,
    "Only count app, not lib, instructions",
    "Count only instructions in the application itself, ignoring instructions in "
    "shared libraries."); //把选项加上only_from_app就可以不进行库的计数
 
/* Application module */
static app_pc exe_start;
/* we only have a global count */
static uint64 global_count;
/* A simple clean call that will be automatically inlined because it has only
 * one argument and contains no calls to other functions.
 */
static void
inscount(uint num_instrs)
{
    global_count += num_instrs;
}
static void
event_exit(void);
static dr_emit_flags_t
event_bb_analysis(void *drcontext, void *tag, instrlist_t *bb, bool for_trace,
                  bool translating, void **user_data);
static dr_emit_flags_t
event_app_instruction(void *drcontext, void *tag, instrlist_t *bb, instr_t *inst,
                      bool for_trace, bool translating, void *user_data);
 
DR_EXPORT void
dr_client_main(client_id_t id, int argc, const char *argv[])
{
    dr_set_client_name("DynamoRIO Sample Client 'inscount'",
                       "http://dynamorio.org/issues");
 
    /* Options */
    if (!droption_parser_t::parse_argv(DROPTION_SCOPE_CLIENT, argc, argv, NULL, NULL))
        DR_ASSERT(false);
    drmgr_init();
 
    /* Get main module address */
    if (only_from_app.get_value()) {
        module_data_t *exe = dr_get_main_module();
        if (exe != NULL)
            exe_start = exe->start;
        dr_free_module_data(exe);
    }
 
    /* register events */
    dr_register_exit_event(event_exit);
    drmgr_register_bb_instrumentation_event(event_bb_analysis, event_app_instruction,
                                            NULL);
 
    /* make it easy to tell, by looking at log file, which client executed */
    dr_log(NULL, DR_LOG_ALL, 1, "Client 'inscount' initializing\n");
#ifdef SHOW_RESULTS
    /* also give notification to stderr */
    if (dr_is_notify_on()) {
#    ifdef WINDOWS
        /* ask for best-effort printing to cmd window.  must be called at init. */
        dr_enable_console_printing();
#    endif
        dr_fprintf(STDERR, "Client inscount is running\n");
    }
#endif
}
 
static void
event_exit(void)
{
#ifdef SHOW_RESULTS
    char msg[512];
    int len;
    len = dr_snprintf(msg, sizeof(msg) / sizeof(msg[0]),
                      "Instrumentation results: %llu instructions executed\n",
                      global_count);
    DR_ASSERT(len > 0);
    NULL_TERMINATE(msg);
    //DISPLAY_STRING(msg);   //这里是messagebox 把这个数据时间控制台输出就行了
    std::cout << msg << std::endl;
#endif /* SHOW_RESULTS */
    drmgr_exit();
}
 
static dr_emit_flags_t
event_bb_analysis(void *drcontext, void *tag, instrlist_t *bb, bool for_trace,
                  bool translating, void **user_data)
{
    instr_t *instr;
    uint num_instrs;
 
#ifdef VERBOSE
    dr_printf("in dynamorio_basic_block(tag=" PFX ")\n", tag);
#    ifdef VERBOSE_VERBOSE
    instrlist_disassemble(drcontext, tag, bb, STDOUT);
#    endif
#endif
    /* Only count in app BBs */
    if (only_from_app.get_value()) {
        module_data_t *mod = dr_lookup_module(dr_fragment_app_pc(tag));
        if (mod != NULL) {
            bool from_exe = (mod->start == exe_start);
            dr_free_module_data(mod);
            if (!from_exe) {
                *user_data = NULL;
                return DR_EMIT_DEFAULT;
            }
        }
    }
 
    /* Count instructions. If an emulation client is running with this client,
     * we want to count all the original native instructions and the emulated
     * instruction but NOT the introduced native instructions used for emulation.
     */
    bool is_emulation = false;
    for (instr = instrlist_first(bb), num_instrs = 0; instr != NULL;
         instr = instr_get_next(instr)) {
        if (drmgr_is_emulation_start(instr)) {
            /* Each emulated instruction is replaced by a series of native
             * instructions delimited by labels indicating when the emulation
             * sequence begins and ends. It is the responsibility of the
             * emulation client to place the start/stop labels correctly.
             */
            num_instrs++;
            is_emulation = true;
            /* Data about the emulated instruction can be extracted from the
             * start label using the accessor function:
             * drmgr_get_emulated_instr_data()
             */
            continue;
        }
        if (drmgr_is_emulation_end(instr)) {
            is_emulation = false;
            continue;
        }
        if (is_emulation)
            continue;
        if (!instr_is_app(instr))
            continue;
        num_instrs++;
    }
    *user_data = (void *)(ptr_uint_t)num_instrs;
 
#if defined(VERBOSE) && defined(VERBOSE_VERBOSE)
    dr_printf("Finished counting for dynamorio_basic_block(tag=" PFX ")\n", tag);
    instrlist_disassemble(drcontext, tag, bb, STDOUT);
#endif
    return DR_EMIT_DEFAULT;
}
 
static dr_emit_flags_t
event_app_instruction(void *drcontext, void *tag, instrlist_t *bb, instr_t *instr,
                      bool for_trace, bool translating, void *user_data)
{
    uint num_instrs;
    /* By default drmgr enables auto-predication, which predicates all instructions with
     * the predicate of the current instruction on ARM.
     * We disable it here because we want to unconditionally execute the following
     * instrumentation.
     */
    drmgr_disable_auto_predication(drcontext, bb);
    if (!drmgr_is_first_instr(drcontext, instr))
        return DR_EMIT_DEFAULT;
    /* Only insert calls for in-app BBs */
    if (user_data == NULL)
        return DR_EMIT_DEFAULT;
    /* Insert clean call */
    num_instrs = (uint)(ptr_uint_t)user_data;
    dr_insert_clean_call(drcontext, bb, instrlist_first_app(bb), (void *)inscount,
                         false /* save fpstate */, 1, OPND_CREATE_INT32(num_instrs));
    return DR_EMIT_DEFAULT;
}

使用python winpwn进行爆破

这里可以用C++ 的windows api来进行爆破, 使用重定向输入和输出创建子进程.

 

微软的官方文档里面相应的样例代码:

 

https://docs.microsoft.com/zh-cn/windows/win32/procthread/creating-a-child-process-with-redirected-input-and-output

 

我使用的是winpwn这个工具来进行爆破, 我稍微看了一下程序创建过程, 本质上还是python去调用windows api, 和C++调用windows API大致一样

 

爆破脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
from winpwn import *
import threading
from concurrent.futures import ThreadPoolExecutor, Future
 
 
def test(data):
    tmp = data
    cmd = '' #your pin.exe path
    module = '' #your pintool.dll path
    p = process([cmd, '-t', module, '--', '.\\low_re.exe'])
    #p = process([cmd, '-c', module, '-only_from_app', '--', '.\\low_re.exe']) //dynamorio的命令行
    p.recvline()
    p.recvline()
    p.sendline(data)
    data = p.recvuntil("\n")
    if (data[0:5] != "Count"):
        data = p.recvuntil("\n")
    data = int(data.split("Count ")[1].split(" ")[0])
    #if (data[0:5] != "Instr"): //dynamorio的输出
        #data = p.recvuntil("\n")
    #data = int(re.findall('\d+', data)[0])
    p.close()
    return data
 
pool = ThreadPoolExecutor(8)
 
 
testlen = 17
 
if (testlen):
    flag = ""
    while len(flag) != testlen:
        tasks = []
        results = []
        for i in range(32, 0x7f):
            data = flag + chr(i) + 'i' * (testlen - 1 - len(flag))
            tasks.append(pool.submit(test, data))
        for t in tasks:
            results.append(t.result())
        #print(chr(a.index(max(a)) + 32))
        flag += chr(results.index(max(results)) + 32)
        print(flag)
else:
    i = 1
    a = []
    while i < 64:
        a.append(test(i * '1'))
        i += 1
    print(a.index(max(a)) + 1)

这里值得注意的是, 最后输出的Count xxx 是输出到错误信息里面的, 并且是以'\n'结尾所以不能直接使用recvline().

 

winpwn部分源代码:

1
2
3
4
def recvline(self,timeout=None,newline=None):
        if newline is None:
            newline=context.newline #newline='\r\n'
        return self.recvuntil(newline)

winpwn调用了win.py

 

write相关调用:

1
2
3
4
5
6
7
8
def write(self,buf=''):
        buf=Latin1_encode(buf)
        length=len(buf)
        written=wintypes.DWORD()
        x=windll.kernel32.WriteFile(self.hWritePipe,buf,length,byref(written),None) //这个就是windows api了
        if x==0:
            raise(EOFError())
        return written.value

[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

上传的附件:
收藏
点赞2
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回