# Get the emsdk repo git clone https://github.com/emscripten-core/emsdk.git # Enter that directory cd emsdk # Download and install the latest SDK tools. ./emsdk install latest # Make the "latest" SDK "active"for the current user. (writes .emscripten file) ./emsdk activate latest # Activate PATH and other environment variables in the current terminal source ./emsdk_env.sh
./test_wasm.js:15: TypeError: WebAssembly.Instance(): Import #0 module="wasi_snapshot_preview1" error: module is not an object or function var wasmInstance = new WebAssembly.Instance(wasmModule, {}); ^ TypeError: WebAssembly.Instance(): Import #0 module="wasi_snapshot_preview1" error: module is not an object or function at ./test_wasm.js:15:20
classJSReceiver : public TorqueGeneratedJSReceiver<JSReceiver, HeapObject> { public: NEVER_READ_ONLY_SPACE // Returns true if there is no slow (ie, dictionary) backing store. DECL_GETTER(HasFastProperties, bool)
// Returns the properties array backing store if it // exists. Otherwise, returns an empty_property_array when there's a // Smi (hash code) or an empty_fixed_array for a fast properties // map. DECL_GETTER(property_array, Tagged<PropertyArray>)
// Gets slow properties for non-global objects (if // v8_enable_swiss_name_dictionary is not set). DECL_GETTER(property_dictionary, Tagged<NameDictionary>)
// Gets slow properties for non-global objects (if // v8_enable_swiss_name_dictionary is set). DECL_GETTER(property_dictionary_swiss, Tagged<SwissNameDictionary>)
// Tagged<T> represents an uncompressed V8 tagged pointer. // // The tagged pointer is a pointer-sized value with a tag in the LSB. The value // is either: // // * A small integer (Smi), shifted right, with the tag set to 0 // * A strong pointer to an object on the V8 heap, with the tag set to 01 // * A weak pointer to an object on the V8 heap, with the tag set to 11 // * A cleared weak pointer, with the value 11 // // The exact encoding differs depending on 32- vs 64-bit architectures, and in // the latter case, whether or not pointer compression is enabled. // // On 32-bit architectures, this is: // |----- 32 bits -----| // Pointer: |______address____w1| // Smi: |____int31_value___0| // // On 64-bit architectures with pointer compression: // |----- 32 bits -----|----- 32 bits -----| // Pointer: |________base_______|______offset_____w1| // Smi: |......garbage......|____int31_value___0| // // On 64-bit architectures without pointer compression: // |----- 32 bits -----|----- 32 bits -----| // Pointer: |________________address______________w1| // Smi: |____int32_value____|00...............00| // // where `w` is the "weak" bit. // // We specialise Tagged separately for Object, Smi and HeapObject, and then all // other types T, so that: // // Tagged<Object> -> StrongTaggedBase // Tagged<Smi> -> StrongTaggedBase // Tagged<T> -> Tagged<HeapObject> -> StrongTaggedBase // // We also specialize it separately for MaybeWeak types, with a parallel // hierarchy: // // Tagged<MaybeWeak<Object>> -> WeakTaggedBase // Tagged<MaybeWeak<Smi>> -> WeakTaggedBase // Tagged<MaybeWeak<T>> -> Tagged<MaybeWeak<HeapObject>> -> WeakTaggedBase
var overlapping_buf = newArrayBuffer(0x20); var for_double_value = newFloat64Array(overlapping_buf); var for_bigint_value = newBigUint64Array(overlapping_buf);
var overlapping_buf = newArrayBuffer(0x20); var for_double_value = newFloat64Array(overlapping_buf); var for_bigint_value = newBigUint64Array(overlapping_buf);
// ----------------------------------------------------------------------------------------------- // ---- type confusion && get object address && treat address as object
var first_object = {"1": 2}; var second_object = [1, 2, 3];
var obj_arr = [first_object, second_object]; var float_arr = [0.1];
var float_typemap = float_arr.oob(); var obj_typemap = obj_arr.oob();
var overlapping_buf = newArrayBuffer(0x20); var for_double_value = newFloat64Array(overlapping_buf); var for_bigint_value = newBigUint64Array(overlapping_buf);
// ----------------------------------------------------------------------------------------------- // ---- type confusion && get object address && treat address as object
var first_object = {"1": 2}; var second_object = [1, 2, 3];
var obj_arr = [first_object, second_object]; var float_arr = [0.1, 0.2, 0.3];
var float_typemap = float_arr.oob(); var obj_typemap = obj_arr.oob();
if (has_read_only_length) { returnGenericArrayPush(isolate, &args); }
// Fast Elements Path int to_add = args.length() - 1; uint32_t len = static_cast<uint32_t>(Object::NumberValue(array->length())); if (to_add == 0) return *isolate->factory()->NewNumberFromUint(len);
// Currently fixed arrays cannot grow too big, so we should never hit this. DCHECK_LE(to_add, Smi::kMaxValue - Smi::ToInt(array->length()));
// v8 13.3.0, compiler/turbofan.cc, line 2019 case Builtin::kArrayPrototypeJoin: return Type::String(); case Builtin::kArrayPrototypeLastIndexOf: return Type::Range(-1, kMaxSafeInteger, t->zone()); case Builtin::kArrayMap: return Type::Receiver(); case Builtin::kArrayPush: return t->cache_->kPositiveSafeInteger; case Builtin::kArrayPrototypeReverse: case Builtin::kArrayPrototypeSlice: return Type::Receiver(); case Builtin::kArraySome: return Type::Boolean();
// This is a Ghidra script for adding string literals that may not be recognized by Ghidra. // // Usage: // Before the script process, you need to notice the JSON file including necessary information. It's automatically // saved in "<HOMEDIR>/.ghidra_scripts/ShortStringAdder.json". // // The JSON file needs to be deserialized into `ConfigReader` class, here is its format: // // { // "minimum_string_length": 3, // "maximum_search_range": 256, // 0 represents no check for adjacent strings // "separators": [0], // "extra_regex": ["\u001b\\[([0-9]+;)*([0-9]+)m([\\x20-\\x7e]|\\t|\\n|\\r|(\u001b\\[([0-9]+;)*([0-9]+)m))*"] // } // // - minimum_string_length: The minimum length of string that will be recognized and added. // - maximum_search_range: We will search if there exist other strings frontward and backward, it's the maximum range. // - separators: The separators of string literal. In some programming language (like rust), string literals might // not end with "\0". You can add ascii integer value here to add separators. // - extra_regex: some regex strings that you may want to define them as strings (even if they may have some unprintable // chars), like UNIX color control (Added by default). // //@author Hornos - Hornos3.github.com, hornos@hust.edu.cn //@category Strings
if (!has_adjacent_strings(addr)) { printf("Address %#x (%s) has no adjacent string, skipped\n", addr.getOffset(), created); continue; }
try { currentProgram.getListing().createData(addr, dt); printf("Created a string \"%s\" at %#x.\n", created, addr.getOffset()); currentProgram.getListing().setComment(addr, EOL_COMMENT, "Script-added string literal"); } catch (CodeUnitInsertionException e) { printf("Error while adding string at %#x, skipped.\n", addr.getOffset()); errorAddrs.add(addr); e.printStackTrace(); } }
if (errorAddrs.isEmpty()) printf("\n0 exception occurred0.\n"); else { printf("\n%d exception occurred, you may need to handle it manually.\n", errorAddrs.size()); println("At: "); for (Address errorAddr : errorAddrs) printf("%#x\n", errorAddr.getOffset()); } println(); }
// check if the dir exists, if not, create it if (!dir.exists()) { println("[-] ~/.ghidra_scripts not found, will create..."); if (!dir.mkdirs()) { println("[!] Failed to create ~/.ghidra_scripts"); return; } }
// check if the file exists, if not, create it if (!file.exists()) { println("[-] ~/.ghidra_scripts/config.json not found, will create..."); try (FileWriterwriter=newFileWriter(file)) { // default configs, the regex is for UNIX color control (like \033[1;31m...\033[0m) writer.write("{\n" + " \"minimum_string_length\": 3,\n" + " \"maximum_search_range\": 255,\n" + " \"separators\": [0],\n" + " \"extra_regex\": [\"\\u001b\\\\[([0-9]+;)*([0-9]+)m([\\\\x20-\\\\x7e]|\\\\t|\\\\n|\\\\r|(\\u001b\\\\[([0-9]+;)*([0-9]+)m))*\"]\n" + "}"); println("[-] Default config created, you can change it in ~/.ghidra_scripts/ShortStringAdder.json."); } catch (IOException e) { println("[!] Failed to create ~/.ghidra_scripts/ShortStringAdder.json: " + e.getMessage()); thrownewException("Create config file failed"); } }
if (currentProgram.getListing().getDataContaining(addr) != null) { if (currentProgram.getListing().getDataContaining(addr).isDefined()) returnnull; } if (currentProgram.getListing().getInstructionContaining(addr) != null) { // TODO: get strings wrongly identified as codes returnnull; }
try { if (currentProgram.getMemory().contains(addr.subtract(1))){ byteprevious_byte= currentProgram.getMemory().getByte(addr.subtract(1)); if (!this.config.separators.contains(previous_byte)) returnnull; } } catch (MemoryAccessException e) { printf("Failed to read byte located in %#x\n", addr.subtract(1).getOffset()); e.printStackTrace(); returnnull; } catch (AddressOutOfBoundsException ignored) {}
Addressptr= addr; intoffset=0; while (true) { try { ptr = addr.add(offset); byteb= currentProgram.getMemory().getByte(ptr); // all printable character (including \t, \n, \r), if conflicted with separators, the latter is dominant if (((b >= 0x20 && b <= 0x7e) || b == 9 || b == 10 || b == 0xd) && !this.config.separators.contains(b)) { ret.append((char) (b & 0xff)); offset++; } elseif (!this.config.separators.contains(b)) // It ends with neither a printable nor a separator returnnull; else break; } catch (MemoryAccessException e) { printf("Failed to read byte located in %#x\n", ptr.getOffset()); e.printStackTrace(); returnnull; } }
if (offset >= config.minimum_string_length) return ret.toString(); returnnull; }
private TreeMap<Address, String> merge_regex_result(TreeMap<String, TreeMap<Address, String>> results) { // This method is used for selecting the primary matches while discarding overlapping matches // E.g. There are 3 matches at 0x0~0x10, 0x9~0x15, 0x12~0x20, then the second one will be discarded. TreeMap<Address, String> ret = newTreeMap<>(); for (Map.Entry<String, TreeMap<Address, String>> entry: results.entrySet()) ret.putAll(entry.getValue());
// eliminate overlapping matches longlargestRangeUpperLimit= -1; Iterator<Map.Entry<Address, String>> iter = ret.entrySet().iterator(); while (iter.hasNext()) { Map.Entry<Address, String> entry = iter.next(); if (entry.getKey().getOffset() + entry.getValue().length() > largestRangeUpperLimit) largestRangeUpperLimit = entry.getKey().getOffset() + entry.getValue().length(); else { printf("Warning: %s (at %#x) removed due to overlapping memory address.\n", entry.getValue(), entry.getKey().getOffset()); iter.remove(); } }
/* Structure describing a loaded shared object. The `l_next' and `l_prev' members form a chain of all the shared objects loaded at startup. These data structures exist in space used by the run-time dynamic linker; modifying them may have disastrous results. */
structlink_map { /* These first few members are part of the protocol with the debugger. This is the same format used in SVR4. */
ElfW(Addr) l_addr; /* Difference between the address in the ELF file and the addresses in memory. */ char *l_name; /* Absolute file name object was found in. */ ElfW(Dyn) *l_ld; /* Dynamic section of the shared object. */ structlink_map *l_next, *l_prev;/* Chain of loaded objects. */ };
/* We use this macro to refer to ELF types independent of the native wordsize. `ElfW(TYPE)' is used in place of `Elf32_TYPE' or `Elf64_TYPE'. */ #define ElfW(type) _ElfW (Elf, __ELF_NATIVE_CLASS, type) #define _ElfW(e,w,t) _ElfW_1 (e, w, _##t) #define _ElfW_1(e,w,t) e##w##t
if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL) { constElfW(Half) *vernum = (constvoid *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]); ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff; version = &l->l_versions[ndx]; if (version->hash == 0) version = NULL; }
/* We need to keep the scope around so do some locking. This is not necessary for objects which cannot be unloaded or when we are not using any threads (yet). */ int flags = DL_LOOKUP_ADD_DEPENDENCY; if (!RTLD_SINGLE_THREAD_P) { THREAD_GSCOPE_SET_FLAG (); flags |= DL_LOOKUP_GSCOPE_LOCK; }
这里的if判断条件中:
1 2 3 4 5 6
// /elf/elf.h, line 620
#define ELF32_ST_VISIBILITY(o) ((o) & 0x03)
/* For ELF64 the definitions are the same. */ #define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o)
constuint_fast32_t new_hash = dl_new_hash (undef_name); unsignedlongint old_hash = 0xffffffff; structsym_valcurrent_value = { NULL, NULL }; structr_scope_elem **scope = symbol_scope; bump_num_relocations (); /* DL_LOOKUP_RETURN_NEWEST does not make sense for versioned lookups. */ assert (version == NULL || !(flags & DL_LOOKUP_RETURN_NEWEST)); size_t i = 0; if (__glibc_unlikely (skip_map != NULL)) /* Search the relevant loaded objects for a definition. */ while ((*scope)->r_list[i] != skip_map) ++i;
/* Search the relevant loaded objects for a definition. */ for (size_t start = i; *scope != NULL; start = 0, ++scope) if (do_lookup_x (undef_name, new_hash, &old_hash, *ref, ¤t_value, *scope, start, version, flags, skip_map, type_class, undef_map) != 0) break;
// /elf/dl-lookup.c, line 578
staticuint_fast32_t dl_new_hash(constchar *s) { uint_fast32_t h = 5381; for (unsignedchar c = *s; c != '\0'; c = *++s) h = h * 33 + c; return h & 0xffffffff; }
staticint __attribute_noinline__ do_lookup_x(constchar *undef_name, uint_fast32_t new_hash, unsignedlongint *old_hash, const ElfW(Sym) *ref, struct sym_val *result, struct r_scope_elem *scope, size_t i, conststruct r_found_version *const version, int flags, struct link_map *skip, int type_class, struct link_map *undef_map) { size_t n = scope->r_nlist; /* Make sure we read the value before proceeding. Otherwise we might use r_list pointing to the initial scope and r_nlist being the value after a resize. That is the only path in dl-open.c not protected by GSCOPE. A read barrier here might be to expensive. */ __asm volatile("" : "+r" (n), "+m" (scope->r_list)); structlink_map **list = scope->r_list;
do { conststructlink_map *map =list[i]->l_real; ... } while (++i < n);
thread 'main' panicked at src/main.rs:2:5: This is a panic stack backtrace: 0: rust_begin_unwind at /rustc/3f5fd8dd41153bc5fdca9427e9e05be2c767ba23/library/std/src/panicking.rs:652:5 1: core::panicking::panic_fmt at /rustc/3f5fd8dd41153bc5fdca9427e9e05be2c767ba23/library/core/src/panicking.rs:72:14 2: lab_01::main at ./src/main.rs:2:5 3: core::ops::function::FnOnce::call_once at /rustc/3f5fd8dd41153bc5fdca9427e9e05be2c767ba23/library/core/src/ops/function.rs:250:5 note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated)] #[non_exhaustive] pubenumErrorKind { /// An entity was not found, often a file. #[stable(feature = "rust1", since = "1.0.0")] NotFound, ......
可以看到NotFound是第一个,索引值应该为0。而if的判断条件是不为0,因此这部分分支对应的是other_error部分,这从后面的字符串输出也可以看出。在第61行的core::fmt::Arguments::new_v1::h6c1ad96880db1e98函数调用中第2个参数就是"Problem opening the file: "的字符串切片。
在第22行,Rust调用了_<core::result::Result<T, E> as core::ops::try_trait::Try>::branch,从上面的实现源码可以看到branch通过match控制执行流。又因为如果结果为Ok时返回的是Self::Residual实例,后续要使用Result真正包装的实例还需要调用反汇编窗口26行的_<core::result::Result<T, F> as core::ops::try_trait::FromResidual<core..result..Result<core::convert::Infallible, E>>>::from_residual获取。这部分内容可以参考资料中有关于try_trait的解释。
[NETWORK] # override the ifr_name field in ifreq structures to match the hosts network interface name. # that fixes certain socket ioctl errors where the requested interface name does not match the # one on the host. comment out to avoid override ifrname_override=eth0
# To use IPv6 or not, to avoid binary double bind. ipv6 and ipv4 bind the same port at the same time bindtolocalhost=True # Bind to localhost ipv6=False
对于堆栈,qiling 也特别设计了 API 便于操作。ql.arch.stack_read用于读取以rsp为基地址的任意偏移量的堆栈值(读取 4/8 字节,取决于字长)。stack_write为写入指定偏移处,stack_push和stack_pop即为直接修改rsp的值,在程序执行外完成 push 和 pop 操作。
from unicorn import * from unicorn.x86_const import * import pickle
print("Save/restore CPU context in opaque blob") address = 0 code = b'\x40'# inc eax try: # Initialize emulator mu = Uc(UC_ARCH_X86, UC_MODE_32)
# map 8KB memory for this emulation mu.mem_map(address, 8 * 1024, UC_PROT_ALL)
# write machine code to be emulated to memory mu.mem_write(address, code)
# set eax to 1 mu.reg_write(UC_X86_REG_EAX, 1)
print(">>> Running emulation for the first time") mu.emu_start(address, address+1)
print(">>> Emulation done. Below is the CPU context") print(">>> EAX = 0x%x" %(mu.reg_read(UC_X86_REG_EAX))) print(">>> Saving CPU context") saved_context = mu.context_save()
print(">>> Pickling CPU context") pickled_saved_context = pickle.dumps(saved_context)
print(">>> Running emulation for the second time") mu.emu_start(address, address+1) print(">>> Emulation done. Below is the CPU context") print(">>> EAX = 0x%x" %(mu.reg_read(UC_X86_REG_EAX)))
print(">>> Unpickling CPU context") saved_context = pickle.loads(pickled_saved_context)
print(">>> Modifying some register.") saved_context.reg_write(UC_X86_REG_EAX, 0xc8c8)
print(">>> CPU context restored. Below is the CPU context") mu.context_restore(saved_context) print(">>> EAX = 0x%x" %(mu.reg_read(UC_X86_REG_EAX)))