#two more functions... then on to extra testing n debugging
Explore tagged Tumblr posts
Text
I've more or less been coding for three days straight, this is truly the worst
#two more functions... then on to extra testing n debugging#then the man file and the demo vid... ugh#this is gonna be three days late lmao I rly hope they ignore that or I'll have to plead my case n it'll be a whole thing#but also they haven't even graded the first project so like . you guys are late too y'know ?#text#paersonal#coding
6 notes
·
View notes
Text
Advent of Code
As a child, advent calendars always added to the sense of anticipation in the lead up to Christmas. In my day you would be lucky to get a small picture behind each of the doors. These days, children expect chocolates or sweets. My wife has once even had a "Ginvent Calendar", with gin behind each door.
This year I marked Advent by having a go at the "Advent of Code" which has Christmas-themed programming puzzles posted each day. Most days are in two parts, with an easier puzzle followed by a harder one. Traditionally, I've posted a (mostly ignored) programming puzzle to our development team each Christmas. Last year I just recycled one of the Advent of Code puzzles, but this year I suggested we attempt the whole thing. The puzzles are so well thought out, in comparison to my efforts, that it seemed pointless to compete.
In the end, several of the team had a go. Some of the puzzles were harder than others, but I managed to solve them all by Boxing Day. What follows are some personal anecdotes from the various days with some general thoughts at the end. Note that there are some spoilers and the notes won't mean much if you've not done the puzzles. So in this case just skip to the end.
a sum-finder. I implemented the search tree via recursive calls. I drifted into using Python right from the start. It just felt like the easiest way to hack the puzzles quickly. In the past I had thought about using the puzzles to learn a new language. A colleague had done that with Rust in a previous year. Despite these good intentions, expediency took a firm hold. That said, in several puzzles I would have liked immutable collections or at least Lisp-style lists.
a pattern counter. Not that interesting except patterns were emerging in the programs themselves. Regular expressions got used a lot to read in the puzzle data. I learnt about things like match.group(1,2,3) which returns a tuple of the first three match groups, so you don't have to write (m.group(1), m.group(2), m.group(3)).
a grid tracer. The first interesting one because it was unfamiliar. Some other patterns started emerging: problem parameters got promoted to command line arguments, and data structure printers got hacked to help debugging. These two were often added between part 1 and part 2 of each problem.
a data validator. This felt like a bit of a slog. It was mostly about capturing the validation rules as code. Even though I made a point of reminding myself at the start that re.search doesn't match the whole string I still forgot it later. Duh.
an indexing problem. I patted myself on the back for realizing that the index was a binary number (or pair of binary numbers as I did it). At this point the solutions were still neat and I would do a little code golfing after the solution to tidy them up a bit and make them more concise.
another pattern counter. Pre-calculating some things during data reading kept the later code simple.
a recursive calculator. This was one of those puzzles where I had to reread the description several times to try and understand what it was asking for. It entailed a slightly tricky recursive sum and product, which was again made easier by creating more supporting data structures while reading the input data.
an interpreter. Probably my favourite individual puzzle because it was so sweet, especially after a bit of refactoring to make the language more data-driven.
another sum-finder. I found I didn't particularly like these.
an order-finder. This was the first one that made me pause for thought. An overly naive search algorithm from part 1 hit a computational complexity wall in part 2. I beat the problem by realizing that the search only had to be done on small islands of the data, but a colleague pointed out there was a better linear solution. The code was starting to get a bit ragged, with commented out debugging statements.
the game of life. The classic simulation but with some out-of-bounds spaces and some line-of-sight rules. It helped to print the board.
a map navigator. I liked this one even though I forgot to convert degrees to radians and that rotation matrices go anti-clockwise. I even introduced an abstract data type (ADT) to see if it would simplify the code (I'm not sure it ever did - I mostly used lists, tuples, strings, and numbers). The second parts of the puzzles were starting to get their own files now (usually bootstrapped by copying and pasting the first part's file).
a prime number theorem. I actually got stalled on this one for a bit. It eventually turned out I had a bug in the code and was missing a modulus. In effect I wasn't accounting for small primes far to the right. I left the puzzle and went on to complete a couple of others before coming back to this one. I checked what I was doing by Googling for hints, but in the end I had to take a long hard look at the data and find my own bug.
some bit twiddling. Part 1 felt like I found the expected bitwise operations, but part 2 felt like I was bashing square pegs into round holes.
a number sequence problem. Another pat on the back, this time for keeping a dictionary of recent occurrences and not searching back down the list of numbers each time. Another recurring pattern is evident: running a sequence of steps over the data. I liked to code the step as its own function.
a constraint solver. A nice one about labelling fields that satisfy the known constraints. Half the code was parsing the textual rules into data.
another game of life simulation. This time it was in more dimensions. I generalized from 3 dimensions to N instead of just doing 4. This made it more of a drag. I started naming auxiliary functions with placeholder names (social services should have been called). Also, I tacked on extra space along each dimension to make room at each step. This felt very ugly. I should have used a sparser representation like I did for day 24.
an expression evaluator. I used another actual ADT and wrote a simple but horrible tokenizer. The evaluator was okay but I hacked the precedence by inserting parentheses into the token stream. Don't try this at home kids.
another pattern matcher. Probably my biggest hack. My code compiled the pattern rules into a single regular expression. This was cute but meant the recursive rules in part 2 needed special treatment. One rule just compiled into a repeated pattern with +. Unfortunately, the other rule entailed matching balanced sub-patterns, which every schoolchild knows regular languages can't do. Perhaps some recursive pattern extensions might have worked, but I assumed there would be no more than 10 elements of the sub-patterns and compiled the rule into a large alternative of the possible symmetrical matchers. Yuck.
a map assembler. I did this one the most methodically. It had proper comments and unit tests. Overall it took the most code but perhaps it was just dealing with all the edge cases (ba dum tss). But seriously, it seemed to take a lot of code for rotating and flipping the tiles even after knowing how they must be connected. So probably there was a better approach. It was still satisfying the see the answer come out after all that work. Curiously, this one involved little debugging. I wonder if perhaps there is some connection between preparation and outcome?
a constraint solver. I tried a dumb approach first based on searching all the possible bindings. That didn't look like it was terminating any time soon. So I reverted to a previously successful technique of intersecting the associations and then then refining them based on the already unique ones.
a recursive card game. This card game playing puzzle seemed to be going okay, but the real data didn't converge for part 2. Had a quick Google for a hint after battling with it for a while, and the first hit was from someone who said they'd misread the question. Sure enough I had too. My recursive games were on the whole deck instead of the part dictated by the cards played. Duh. The description was clear enough and included a whole worked game. I just hadn't read it properly. It still seemed to need some game state memoization to run tolerably fast.
a circular sequence. Took three attempts. A brute force approach using an array was good enough for part 1, but no way was it going to work on part 2. Even optimizing it to use ranges was still 'non-terminating' for the array-based solution. So I Googled for a little inspiration and found the phrase "linked lists" and slapped my forehead hard. I switched to a dictionary of labels to labels and the solution popped out very easily, without any further optimization. Embarrassing. Was it time to ceremonially hand in my Lisp symbol and fall on a sharpened parenthesis?
another game of life. This one sounded neat because it was about a hex grid, but I didn't know how hex grids are usually indexed. So for the first time I did a little bit of general research at the start. Turns out there are a bunch of ways to index a hex grid. I opted for using 3-axes as that seemed natural despite the redundancy. The map itself was just a dictionary of locations. I should have looked up how to have structured dictionary keys in Python (implement __hash__) but I couldn't be bothered so I (look away now) serialized and deserialized the locations to and from strings. I still had a bug which I couldn't find until I hacked a crude hex board printer and realized I wasn't carrying the unchanged cells over from one iteration to the next.
a cryptographic puzzle. Came out quite short but only after some faffing around. Main trick seemed to be to keep the transformation ticking along instead of recalculating it from scratch each time. There was slight disappointment (tinged with relief) that there was no part 2.
Some general lessons I felt I (re)learned:
Read the questions very carefully, then reread them.
Try and use terms from the questions. Don't invent your own terminology and then have to map back and forth.
Make the trace output exactly like the examples to help comparison.
Next time I'd consider using BDD to turn their examples directly into tests. Next time.
Try the problem for a while by yourself, then think about it offline, and only then Google for hints.
Next time I'd consider using some form of source control from the start, or just a better set of file naming conventions.
Regular expressions go a long way, but can then they can get in the way.
Next time I'll consider doing it using a language I'm learning.
Sometimes when you get stuck you have to start again.
During some low moments it all felt like make-work that I'd inflicted on myself, but in the end it was a nice set of training exercises. I'd encourage others to have a go at their leisure.
"Practice is the best of all instructors." -- Publilius Syrus
2 notes
·
View notes
Text
Nintendo Switch Kernel Patching and Emulation - Achieving Maximum Modification Flexibility with Minimal Replacement
Ever since shofEL2 was released earlier this year it's been interesting to watch how different custom firmwares have tackled the prospect of modifying Nintendo's firmware for both homebrew and piracy applications, and as someone who hasn't really had much stake in that race I feel like it's interesting to watch how different solutions tackle different problems, but at the same time since I do have a stake in a few places (namely, Smash Bros modding, vulnerability hunting, personal projects) I ended up in a situation where I needed to sort of 'set up camp' with Nintendo's Horizon microkernel and have a comfortable working environment for modification.
Binary Patching is Weird, and Horizon makes it weirder.
Probably the biggest difficulty in Switch development I would say is iteration time, followed by a general difficulty in actually modifying anything; even just booting modified core services (ie filesystem management, service management, spl which talks to the EL0 TrustZone SPL [commonly misnomered as the security processer liaison...?], the boot service which shows boot logos and battery indications, ...) requires, at a minimum, reimplementing Nintendo's package1 implementation which boots TrustZone and patches for TrustZone to disable signatures on those services and kernel. Beyond the core services, modifying executables loaded from eMMC requires either patching Loader, patching FS, reimplementing Loader, or something else.
Unfortunately with binary patching there generally isn't a silver bullet for things, generally speaking the three methods of modifications are userland replacement, userland patching, and kernel patching. The first two are currently used for Atmosphere, but the solution I felt would be the most robust and extensible for the Nintendo Switch was kernel patching. Here's a quick rundown on the pros and cons for each method:
Userland Replacement
- Requires rewriting an entire functionally identical executable - Often not feasible for larger services such as FS - Can easily break between firmware updates, especially if new functionality is added or services split. This makes it difficult to maintain when the OS is in active development. - Added processes can potentially leave detectable differences in execution (different PIDs, different order of init, etc) + Easier to add functionality, since you control all code + Can operate easily on multiple firmwares + Can serve as an open-source reference for closed-source code
Userland Patching
- Adding additional code and functionality can be difficult, since expanding/adding code pages isn't always feasible without good control of Loader - Finding good, searchable signatures can often be difficult - Can easily break between firmware updates, especially if functionality or compilers are tweaked + With good signatures, can withstand firmware updates which add functionality + Often has less maintenance between updates when functionality does change; patching is usually easier than writing new code + Harder to detect unless the application checks itself or others check patched applications
Kernel Patching
- Greater chance of literally anything going wrong (concurrency, cache issues, ...), harder to debug than userland - Minimal (formerly no) tooling for emulating the kernel, vs userland where Mephisto, yuzu, etc can offer assistance - Can easily break between firmware updates, and is more difficult (but not impossible) to develop a one-size-fits-all-versions patch since kernel objects change often - Easier to have adverse performance impacts with syscall hooks + Harder to detect modifications from userland; userland cannot read kernel and checking if kernel has tampered with execution state can be trickier + Updating kernel object structures can take less time than updating several rewritten services, since changes are generally only added/removed fields + Direct access to kernel objects makes more direct alterations easier (permission modification, handle injection, handle object swapping). + Direct access to hardware registers allows for UART printf regardless of initialization state and without IPC + Hooking for specific IPC commands avoids issues with userland functionality changes, and in most cases IPC commands moving to different IDs only disables functionality vs creating an unbootable system.
mooooooo, a barebones Tegra X1 emulator for Horizon
Obviously the largest hangup with kernel patching is debugging, the Switch has RAM to spare (unlike 3DS) and setting up an intercept for userland exceptions isn't impossible to do by trial and error using SMC panics/UART and a lot of patience, but for ease of use and future research I really, really wanted an emulator to experiment with the Switch kernel. I ended up building a quick-n-dirty emulator in Unicorn, and with a few processes it works rather well but it still struggles with loading Nintendo's core processes currently, but for a small and contained test environment (two processes talking to each other and kernel patches watching them), I would say I had reached my goal and it was enough to at least be able to work quickly and sanely on my intercept.
For the most part, the Switch Horizon microkernel doesn't actually use much of the Tegra MMIO; it uses some of the more obvious ARM components like GIC for interrupts, and it also has a large initialization sequence for the memory controller, but as long as interrupts are functional, timers work, MC returns some correct values and SMC return values are all correct, it boots into userland without issue.
I actually found that emulating multiple cores in Unicorn actually isn't all that difficult, provided you're using a compiled language where uc_mem_map_ptr works. Rather than messing with threads, I opted for a round-robin scheduling scheme where I run each Unicorn instance for a set number of instructions, with memory being mapped and unmapped from the running core so that any cached data gets written out before the next core has its turn. A lot of modifications/cherry-picking to Unicorn did have to be made in order to properly support interrupts, certain coprocessor registers (ie core indexes), translation tables (for uc_mem_read/uc_mem_write, vaddr->paddr translation, and just in general there were some odd issues).
Patching Horizon for Syscall MiTM
With a decent environment for modifying kernel, the next issue really just became actually bootstrapping an SVC intercept. Figuring out where exception vectors are located isn't difficult with the emulator handy, but really the issue becomes
1. Extra code has to be loaded and copied by TrustZone, along with kernel 2. New code needs to be placed in a safe location and then given memory mappings 3. Existing code needs to be modified with hooks pointing to the new code
To guide the kernel towards salvation I ended up hooking just before translation table addresses are written into coprocessor registers. This way, the payload can allocate pages and copy code from less-safe soon-to-be-condemned .bss memory for the bulk of the SVC interception code, set up those pages in the translation tables, and then patch the ARM64 SVC handler to actually jump to the new mapping. For ease of development, the mapping is given a constant address along with any hardware registers which it needs to access, as opposed to being randomized like the rest of the kernel.
In the end, patched the kernel executes as follows:
Since hashtagblessed is able to map UART, CAR and PINMUX registers into translation tables, getting communication from the right Joy-Con rail using existing BPMP-based drivers was fairly straightforward, and even without any source code to reference there's a fairly basic driver in TrustZone. Between the transition from emulation to hardware however, I had kept an SMC to print information to the console, but I ultimately ended up using UART even in emulation. On hardware, I got by using UART-B (the right Joy-Con railed) for a while, but had to switch to UART-A (internal UART lines for debugging) due to GPIO issues once HOS tried to initialize Joy-Con.
Identifying IPC Packets, Accurate Results With Simple Tools
With therainsdowninafrica loaded, hooked in and blessed, the next step is actually being able to identify specific IPC requests sent through svcSendSyncRequest, and doing this requires getting our hands dirty with kernel objects. Userland is able to utilize different kernel objects indirectly through the use of handles and system calls. Each KProcess has a handle table which maps handles to the underlying object structures, so translating handles to KObjects is simply a matter of checking the table for a given handle passed to a syscall. To access the current KProcess object which has the handle table, we can use the per-core context stored in register X18 as of 5.0.0 (prior to Horizon implementing ASLR, it was stored in a hardcoded address per-CPU) and the handle table can be accessed through the current KProcess object. Printf debugging was extremely useful while figuring out exactly how KProcess has its fields laid out since the structure changed slightly between versions, and with a bit of reversing added in it's not particularly difficult to figure out exactly where the KProcessHandleTable is at how handles are translated into objects.
Probably the most useful fields in KProcess/KThread, in our case, are the process name and title ID, the handle table, and the active thread's local storage, where all IPC packets are read from and written to. To give a quick overview on how Switch IPC generally works, services are able to register a named port to wait for communications on which can be connected to by other processes via svcConnectToNamedPort. In practice, Nintendo only uses globally-accessible named ports for their service manager, `sm:`. On a successful call to svcConnectToNamedPort, processes recieve a KClientSession handle for sm: which allows those processes to send IPC requests to the service manager to either register/unregister/connect to 'private' named ports managed by the service manager, with sm checking whether the requesting process actually has access to said service.
From a practicality standpoint, since so much communication relies on IPC the most obvious mechanism to establish is a system for hooking requests going into specific named ports, both globally accessible ones such as sm: and private ones provided by sm itself. This kinda leads into why it's important to have access to the underlying KClientSession objects as opposed to trying to track handles; mapping out exactly which KProcess' handles go to what, while also tracking where handles might be copied and moved to is an almost impossible task, however mapping specific KClientSessions to specific handlers avoids the handle copying/moving issue since the KClientSession object pointer does not change in those cases.
Additionally, many interfaces aren't actually accessible from either svcConnectToNamedPort nor sm, as is the case with fsp-srv which gives out IFileSystem handles for different storage mediums. However, by providing a generic means for mapping KClientSession objects to specific intercept handlers, you can set up a chain of handlers registering handlers. For example, intercepting a specific eMMC partition's IFile's commands would involve setting up a handler for the sm global port, and then from that handler setting up a handler for any returned fsp-srv handles, and then from the fsp-srv handler checking for the OpenBisFileSystem command for a specific partition to hook the IFileSystem to a handler, which can have its OpenFile command hooked to hook any outgoing IFile handles to a specific handler for IFiles from that eMMC partition. From that point all incoming and outgoing data from that IFile's KClientSession can be modified and tweaked.
Finally, in order to prevent issues with KProcess handle tables being exhausted, Nintendo provided virtual handle system implemented in userland for services which manage large amounts of sessions. Effectively, a central KClientSession is able to give out multiple virtual handles (with virtual handles given out by virtual interfaces) only accessible through that KClientSession handle. As such, a process can take a service such as fsp-srv and with a single handle can manage hundreds of virtual interfaces and sub-interfaces, easing handle table pressure on both the client and server ends. These handles can be accommodated for by watching for KClientSession->virtual handle conversion, and then keeping mappings for KClientSession-virtual ID pairs. And again, since copied/moved KClientSessions keep their same pointer, in the event that somehow the central handle and a bunch of domain IDs were copied to another process, they would still function correctly.
Tying it All Together
Let's take a look at what it would take to boot homebrew via hbloader utilizing only SVC interception. The key interface of interest is fsp-ldr, which offers command 0 OpenCodeFileSystem taking a TID argument and an NCA path. From a userland replacement standpoint, booting homebrew involves redirecting the returned IFileSystem to be one from the SD card rather than one from fsp-ldr, since Loader (the process accessing fsp-ldr) doesn't really do any authentication on NSOs/NPDMs, only FS does. From a kernel standpoint, we just need to watch for an IPC packet sent to fsp-ldr for that command, hook the resulting handle, and then for each OpenFile request check if an SD card file can better override it. From there, swap handles and now Loader is reading from an IFile on the SD card rather than an NCA.
Taking a few steps back, there's obviously a few things to keep in mind: Loader never actually accesses the SD card, in fact it doesn't even ask for a fsp-srv handle. Since it is a builtin service it has permissions for everything, but the issue still remains of actually making sure handles can be gotten and swapped in. As it turns out, however, calling SVC handlers from SVCs is more than possible, so if Loader sends a request to sm asking for fsp-ldr, we can send out a request for fsp-srv, initialize it, and then send out the original request without Loader even knowing.
Interestingly, the first thing Loader does with its fsp-ldr handle is it converts it into a virtual domain handle, so that all OpenCodeFileSystem handles are virtual handles. This does make working with it a little more tricky since the code filesystem and code files all operate under the same KClientSession object, but it was an issue which needed resolving anyhow. For SD card IFile sessions, it also means that we have to convert them to virtual handles and then swap both the file KClientSession handle and the file virtual handle ID, while also watching for their virtual ID to close so that we can close our handles at the same time and avoid leakage.
A few other tricks are required to properly emulate the SD redirection experience: swapping in handles isn't the only concern, it's also important to ensure that if the SD card *doesn't* have a file then that error should be returned instead, and if the SD card has a file which doesn't exist in the original IFileSystem, we still need a file handle to replace. To accomodate for this, the original FileOpen request is altered to always open "/main" and if the SD card errors, that virtual handle is closed, and otherwise the SD handles are swapped in.
The end result is, of course, the homebrew launcher being accessible off boot from the album applet:
youtube
Other Potential Thoughts for Kernel Stuff
* Process patching is as easy as hooking svcUnmapProcessMemory and patching the memory before it's unmapped from Loader. Same goes for NROs but with different SVCs, all .text ultimately passes through kernel. * Reply spoofing. IPC requests can simply return without ever calling the SVC, by having kernel write in a reply it wants the process to see. * SVC additions. I'm not personally a fan of this because it starts to introduce ABIs specific to the custom firmware, but it's possible. One of the things I'm not personally a fan of with Luma3DS was that they added a bunch of system calls in order to access things which, quite frankly, were better left managed in kernel. The kernel patches for fs_mitm also violate this. Userland processes shouldn't be messing with handle information and translation tables, i m o. That's hacky. * Virtual services and handles. Since the intercept is able to spoof anything and everything a userland process knows, it can provide fake handles which can map to a service which lies entirely in kernel space. * IPC augmentation: Since any IPC request can be hooked, it can be possible to insert requests inbetween expected ones. One interesting application might be watching for outgoing HID requests and then, on the fly, translating these requests to the protocol of another controller which also operates using HID. * IPC forwarding: similar to augmentation, packets can be forwarded to a userland process to be better handled. Unfortunately, kernel presents a lot of concurrency issues which can get really weird, especially since calling SVC handlers can schedule more threads that will run through the same code. * As currently implemented, A32 SVCs are not hooked, however this is really more an issue if you want to hook outgoing requests from A32 games like MK8, since services such as Loader will generally only operate in a 64-bit mode.
Source
Horizon emulator, https://github.com/shinyquagsire23/moooooooo
therainsdowninafrica, https://github.com/shinyquagsire23/therainsdowninafrica
2 notes
·
View notes
Text
Ugoos Am6 Plus is Ugoos’ newest Android TV box, and I can honestly say, it’s not only their best box yet, but one of the best boxes I ever reviewed so far!
Thanks goes to James from Ugoos for providing the Review sample.
What’s in the Box?
AM6 Plus arrived in a simply designed square cardboard box, in white and green. Inside I found, aside for the AM6 Plus itself and its two antennas, a quite standard affair of: Infrared remote control, HDMI 2.1 cable, a 12v power adapter and a UK adapter plug, as well as a small user manual. No OTG cable is included, but considering the power-box this model is, I cannot complain.
AM6 Plus Looks
AM6 Plus is a black metallic square box, not dissimilar to other boxes from other manufacturers.. but.. for one, it is made of metal. Blacked out aluminium shell to be exact with plenty of venting holes and slots. The power led is actually on the top of the box, inside the first “o” of Ugoos manufacturer’s logo. The box also comes with a dual antenna setup which may be responsible for the very good WiFi performance measured. The rest are quite standard – multiple ports and slots arranged around the box’s periphery, with one port which I miss – a USB-C port. There are USB 2.0 ports, and a single USB 3.0 port, but not USB-C port.
AM6 Plus – Top
AM6 Plus – Bottom
AM6 Plus – Left
AM6 Plus – Right
AM6 Plus – Back
AM6 Plus – Front
Here are the items that come in the box: AM6 Plus, Two antennas, Infrared remote control, HDMI 2.1 cable, a 12v power adapter and a UK adapter plug, as well as a small user manual.
AM6 Plus Specifications
Chipset Amlogic S922XJ with 12nm,Quad core ARM Cortex-A73 and dual-core ARM Cortex-A53 CPU GPU ARM Mali TM-G52 MP6(6ppc) Memory / Storage LPDDR4 4GB / 32GB (EMMC) LAN 1*RJ45 1000M (1000M Ethernet Integrated IEEE 802.3 10/100/1000M Ethernet MAC with RGMII interface) Wireless 2.4G+5G The wireless module complies with IEEE 802.11 a/b/g/n/ac 2×2 MIMO standard and it can achieve up to a speed of 867Mbps with dual stream in 802.11n to connect the wireless LAN (WiFi Module: AmPak 6398S) Bluetooth Bluetooth 5.0 with LE technology OS Android 9.0 Pie Video Output HDMI (2.1 and 2.0) to support maximum 4K@60fps output(HDCP2.2) Audio Output / Input Supports MP3, AAC, WMA, RM, FLAC, Ogg and programmable with 7.1/5.1 down-mixing I2S audio interface supporting 2-channel input and 8-channel (7.1) output Built-in serial digital audio SPDIF/IEC958 output and PCM input/output Built-in stereo audio DAC, Stereo digital microphone PDM input Support concurrent dual audio stereo channel output with combination of analog+PCM or I2S+PCM Power DC 12V/2A Peripheral Interface 1 * HDMI 2.1, 1 * USB 3.0 Fast Speed, 3 * USB 2.0 Host, 1 * SPDIF, 1 * DC Jack, 1 * TF Card (MicroSD), up to 32GB (SD 2.X, SD 3.X, SD 4.X, eMMC ver 5.0), 1 * 3.5mm Stereo input/output Packing Included IR Remote controller, Dimensions 117mm * 117mm * 18.5mm, 300 grams
AM6 Plus Benchmarks and Testing
All completed benchmarks have been repeated 3 times and results have been averaged to give a more accurate reading:
Antutu Benchmark
The Antutu benchmark tests single core performance over multi-core as it is a better indication of the performance of one device over others in most situations. Ugoos AM6 Plus jumps the queue and takes first place! above any other device I’ve tested so far.
Bellow is a screenshot of the latest Antutu benchmark (I have not tested other boxes with this new version, so there is no comparison graph as yet):
GPU Mark Benchmark
Average & Screen Normalized Score
Average frames per second
GPU Mark tests 3d gaming performance and also provides a normalized score according to the used screen resolution (for a more accurate result). The test is quite short and should be taken as a supporting result to that of the more serious 3D Mark benchmark. Ugoos AM6 Plus reached 3rd place in average frames per second, and only 5th place in the average and screen normalized score. However, the seemingly low placement may be deceiving as it’s not far off from it’s competitors, and the one that tops the chart is a tablet that contains a much more powerful GPU.
A1 SD Benchmark
Internal/External Storage Speed
RAM Copy Speed
A1 SD Benchmark tests RAM and flash memory speeds. As can be seen in the provided graphs, RAM is much faster (by a factor of about 40) than flash memory – that is why it’s in smaller amount and is also volatile (does not keep its contents after a reboot). Ugoos AM6 Plus shows its muscles as its DDR4 speedy RAM wins it second place in RAM copy speed test, and its updated eMMC storage flash places it in third place in Internal/External storage speed tests.
PC Mark Benchmark
AM6 Plus wins first place in this extensive suite of productivity tests which is designed to show a more balanced picture of an android device’s ability to perform various tasks such as spreadsheet calculations, photo editing, complex web page browsing and more.
3D Mark Benchmark
3D Mark benchmark is considered as one of the best ways to test 3d performance on Android (and other platforms). Ugoos AM6 Plus takes second place, right after the aforementioned tablet.
AM6 Plus Extra Benchmarks
For some more information, see the following screenshots from other benchmarks such as AndroBench, Mobile GPU Benchmark, Vellamo, Passmark, and SD Card Test Pro:
AndroBench
Mobile GPU Benchmark
Vellamo
Passmark
SD Card Test Pro
AM6 Plus Bugs and Issues
AM6 Plus refuses ADB connect requests, thus making use of SCRCPY remote control program not possible. For SCRCPY to work, wifi debugging need to be enabled under debug settings, developer options need to be enabled and USB Debugging needs to be on, and then adb kill-server command followed by adb shell command ran from the PC. Then a prompt will appear to authorize the RSA key for the connecting PC, and after that, SCRCPY will work.
Built in Samba file server only supports SMB v1, which means Windows 10 owners are unable to access the AM6 Plus files over the LAN. However, the built-in Samba client supports upto and including v3 – so there is no issue for the AM6 Plus to read files from any Samba share on a local network.
The IR remote that comes with the AM6 Plus is less than ideal. It’s very directional and keys are not sensitive.
8k video works somewhat, but I think software optimization can improve on it and give this box a much longer shelf life.
Angry Birds 2 no-click-possible-for-next-level bug is present and accounted for.
AM6 Plus Root function
AM6 Plus come with a built-in root function which works like a charm. All you need to do is go to the settings, locate it under Uggos settings section, and turn it on or off as needed. It will keep after reboot, till you decide to turn it off. The power and decision are all yours!
AM6 Plus Video Playback testing (Using KODI)
Resolution Video Format Local Playback Network (Wi-Fi) Playback 720p (1280*720) AVC ([email protected]) Plays Okay Plays Okay 1080P (1920*1080) AVC (High@L4) Plays Okay Plays Okay 2160P (3840*2160) HEVC (H.265) Plays Okay Plays Okay 4K (4096*2304) AVC ([email protected]) Plays Okay Plays Okay 4K HDR HEVC Main 10@L5@High HDR10 Plays Okay Some buffering 4K TS HEVC HEVC (H.265) 10Bit Plays Okay Mixed experience
The box does perform well in day to day situations (most videos played are 1080p or standard 4k), however, it seems that network optimization needs some work as even with the good speedtest results, buffering of heavy video files was observed. Still, the playback results are quite good in general.
AM6 Plus showed excellent picture quality, and also surprised with partial support for 8K video. This chipset does not officially support 8k, but with the new crop of 8k supporting AV receivers (for now, Denon already declared it) coming later this year, I have tested a few 8k video files. As can be seen in the above table, all of them actually played, with mixed results. For me it means that this box is capable of playing 8k, even though it would be limited up-to 30 frames per second. With some software optimization, it can even be a smooth experience.
AM6 Plus was also tested with HDR content as it becomes more and more common in today’s TV and projector sets. HDR contains more information and is definitely heavier than standard 4k content. As such I did notice a bit of buffering with it – when played over wireless LAN. Again, an issue that may be solvable with some updates.
AM6 Plus Network performance
I tested the network performance using the popular Speedtest.net application from the play store. I tested both WiFi (the fastest WiFi supported – in this case 5GHz 802.11ac), and wired connection (in my case AV1200 Ethernet over power line). My home connection is a symmetric 500 Mb Fiber connection so it would not limit the testing (but the Ethernet over power line connection does) – Ugoos AM6 Plus performed above average with high ranking results for 5GHz WiFi network, and much lower results with my powerline adapter wired connection:
Speed Test – WiFi
Speedtest Wired
AM6 Plus Gaming performance
Ugoos AM6 Plus performed smoothly as expected in the gaming tests. It’s graphic chip can handle most games without an issue. However, control may be an issue, unless you splurge for a game pad or other more gaming friendly remote.
Asphalt 8 Airborne – a 3d graphic intensive racing game. AM6 Plus Loads and runs it smoothly, Using the included standard IR remote I could steer, but could not use Nitro..
Angry Birds 2 – a popular 2d action game. AM6 Plus run it smoothly and loaded the stages fast, but had this bug I noticed in other TV boxes, where at one point you could not click on an icon to continue to the next level and so you get stuck.
Walking War Robots – an online robot warfare game that requires a game-pad (I don’t have a game-pad). AM6 Plus runs it well, as long as you have a gamepad or a air mouse. Otherwise, control is only partial and you cannot complete a battle.
AM6 Plus Conclusions
Did I like it? Yes. It is not a perfect box, and the lack of v2/v3 samba server support is very disturbing, but it is a very stable, and a super fast TV Box.
Would I recommend it? Yes. It is a serious contender in the top android TV box market. Good quality build, lots of ventilation, a little pricey but you can see what you are paying for.
You like it! Where can you buy it? There are few options to buy the Ugoos AM6 Plus:
159 USD (before any coupon discount, currently at 10%): UGOOS AM6 PLUS Amlogic S922X-J 2.2GHz 4GB DDR4 32GB ROM Smart Android 9.0 TV Box 2.4G 5G WiFi 1000M Bluetooth 4K HD Media Player
Some cheaper options are out there, but this is the higher spec version with 4GB of RAM and 32GB of built in storage.
Same price can be found here (no coupons available on page though): UGOOS AM6 Plus Amlogic S922XJ 4GB/32GB Android 9.0 4K TV BOX Wake Up on LAN with 2.4G+5G MIMO WIFI 1000M LAN Bluetooth 5.0 HDMI 2.1 USB 3.0 – Black
Other places I checked are: Amazon (no availability or price), eBay (significantly higher pricing), or other Chinese stores which either offer a higher price or different models such as the AM6 Pro.
Review | Ugoos AM6 Plus Amlogic S922XJ TV Box Ugoos Am6 Plus is Ugoos' newest Android TV box, and I can honestly say, it's not only their best box yet, but one of the best boxes I ever reviewed so far!
0 notes