Yeah, they probably did a batch run of the simple cable. The flex cables are super cheap. Not even the same quality you’d get from an FPC you‘d get from jlcpcb. Going to be hard to beat.
Yeah, they probably did a batch run of the simple cable. The flex cables are super cheap. Not even the same quality you’d get from an FPC you‘d get from jlcpcb. Going to be hard to beat.
now what you people triggered my memory, back on the day I used to create prototype PCBs using all copper boards, PCB Traces printed on laser printer, transfer the ink to the copper board using an fabric iron, and then iron-sulfide to remove unprotected copper, that was cheaper as hell but take time, I have some microcontroller developer boards and programmers still working today.
now what you people triggered my memory, back on the day I used to create prototype PCBs using all copper boards, PCB Traces printed on laser printer, transfer the ink to the copper board using an fabric iron, and then iron-sulfide to remove unprotected copper, that was cheaper as hell but take time, I have some microcontroller developer boards and programmers still working today.
yeah been there and done that good old days Xd
these days i use my small cnc milling machiene or if i need more and im to lazy i just order them from jlcpcb,
what i also have done is 3d printed some pcbs for the fun but not even worth it hahahahaha
now what you people triggered my memory, back on the day I used to create prototype PCBs using all copper boards, PCB Traces printed on laser printer, transfer the ink to the copper board using an fabric iron, and then iron-sulfide to remove unprotected copper, that was cheaper as hell but take time, I have some microcontroller developer boards and programmers still working today.
I always order in bulk from JLC. If I need one, I’m getting 50/100 because it’s going to be cheaper and there’s a 10% of the batch that just don’t work. I make a lot of Pi HAT’s that are extremely one-off. I’ve got boxes of spares in the basement.
Edit: can anyone comment on PCBWay? They’re generally more expensive unless you get into JLC’s non-stock options. Then JLC gets out of control. Does PCBWay populate? JLC will not, and when you make a bunch of Pi HAT’s, soldering header pins gets old fast.
I always order in bulk from JLC. If I need one, I’m getting 50/100 because it’s going to be cheaper and there’s a 10% of the batch that just don’t work. I make a lot of Pi HAT’s that are extremely one-off. I’ve got boxes of spares in the basement.
Edit: can anyone comment on PCBWay? They’re generally more expensive unless you get into JLC’s non-stock options. Then JLC gets out of control. Does PCBWay populate? JLC will not, and when you make a bunch of Pi HAT’s, soldering header pins gets old fast.
yeah thats true bulk is the way to go....
still gonna try and create a pcb and then ill check on it again and see how much bulk is and maybe just buy bulk and offer them for picofly we will see
never used pcbway only jlcpcb and never had issues with them
just playing around with some donor boards what solution should i use ?
Post automatically merged:
yeah thats true bulk is the way to go....
still gonna try and create a pcb and then ill check on it again and see how much bulk is and maybe just buy bulk and offer them for picofly we will see
never used pcbway only jlcpcb and never had issues with them
I picked up a v2 with bad m92t from ebay to repair and mess with the pico hekate booted 1st try and ofw works fine using vol buttons and using reboot
To ofw option I'm using the 2.5fw
Is it normal for hekate to show the emmc in slow mode or did I just buy one with a bad emmc? It boots to hos fine and hekate but under emmc info I get the slow mode error..
I picked up a v2 with bad m92t from ebay to repair and mess with the pico hekate booted 1st try and ofw works fine using vol buttons and using reboot
To ofw option I'm using the 2.5fw
Is it normal for hekate to show the emmc in slow mode or did I just buy one with a bad emmc? It boots to hos fine and hekate but under emmc info I get the slow mode error..
I picked up a v2 with bad m92t from ebay to repair and mess with the pico hekate booted 1st try and ofw works fine using vol buttons and using reboot
To ofw option I'm using the 2.5fw
Is it normal for hekate to show the emmc in slow mode or did I just buy one with a bad emmc? It boots to hos fine and hekate but under emmc info I get the slow mode error..
I'm pleased to announce that after significant time working on a cycle-accurate (this is very important) emulator I've finally been able to go past the decryption phase and have dumped the segments of ARM code that is written by the end of the encryption. I have to say, this is the most fun CTF I've ever done, although at some point it ran out of steam and couldn't surprise me that much.
My dump isn't perfect--the code it jumps to itself is a tiny bit obfuscated (as in, it copies code to other locations and jumps there to fool IDA's autoanalysis) but as far as I'm concerned the hard part is over. I even have the PIO code(!!!) it writes and executes on the PIO1 state machines.
This is largely a follow-up of my previous post, and I don't want to duplicate information, so if you're confused I'd recommend reading that one first.
Also, big thanks to the people who sent me their firmwares; without them, none of this would have been possible. If you did send me a firmware and would like others to be able to look at what gets dumped, please tell me so I can do that. I can also release my emulator if it helps someone.. it's just 2000 lines of hastily written Zig code, although you will have to manually find some patch addresses so that it works properly.
I'll split this up into sections to avoid spam.
First, and most unimportant, the mysterious SWD message it sends at the very start just wakes it from a dormant state, so not very interesting.
Then there is the decryption. After initializing some data structures (I called them "wordbank0", "wordbank1", and "constant_random_data_waste_of_time") it sets the VTOR to an initial value (e.g. EE2F8D10, which gets truncated to EE2F8D00) and then goes 16 bytes at a time (we'll call this a block) on the binary blob at the base of the SRAM. It also takes the 8-byte board ID and copies it to a 256 byte structure which I call the flash XOR buffer.
In a block, for each byte, it will first derive a key based on the value stored in the VTOR as well as the flash xor buff and a rolling index into it. This value, along with the previous value of the process stack pointer (PSP/SP_Process/whatever ARM calls it) is then written to the current PSP.
This means that some part of the encryption relies on the *UNINITIALIZED* value of the PSP, which is 0xFFFFFFFC, in case you were having troubles with your emulation.
The key is manipulated some more after that. Interestingly, it then sets the flash XOR buff at the selected index to the value of the current encrypted byte. Finally, it XORs the encrypted byte by the key and writes it.
At the end of a block, after all 16 bytes have been written, it takes the PSP shifted right by 8 bits and XORs it by the decrypted byte at byte 15 (that is, the last decrypted byte in the block) and based on if bits 0 through n (n seems to vary across separate firmwares) are set, in a loop of up to n times (the same n!) it calls a function we'll call readWriteOrCall. This will be looked at later.
Finally, it writes a new PSP. All of this can be seen in publicly available firmwares (in this thread) so I don't want to bother going into specifics; it's not that complicated.
We then have readWriteOrCall, which, based on the input (the value of bit n from (PSP >> 8) ^ last_encrypted_byte ^ key) manipulates the previously mentioned word banks (wordbank0/wordbank1) with some division, multiplication, shifts, etc (it sounds complicated but it's simple enough to just F5 and replicate in IDA) until it eventually maybe decides to call the most important function, which I just called executeRWC.
executeRWC is very funny because in the middle of the controlflow graph there is an innocuous branch that loads a value into R0 and then jumps to it. For a while I thought this was where it jumps inside the encrypted blob, but that is wrong. In fact, it never takes that branch. Go figure! Like I said, this was the most interesting CTF I've ever done.
executeRWC is also very important. It has two other (used) features: that it can arbitrarily read or write memory via sequences to a core's SWD. The action taken, the addresses and data used, and which processor to do it on (this is also important) are vaguely derived from the value given to it, which is derived from how the word banks are manipulated, which is derived from the bit setting of the decrypted byte and the PSP, which is certainly a mouthful.
As expected, the writes are mostly used for anti-debug. After initializing the systick (!!!) it spams writes to 0x4001C080 with the value 0x80 -- this is the pad ctrl disable bit for the external SWD pin, which is why it's impossible to debug the rp2040 while it's decrypting the blob. They also periodically read from this register to make sure it's still 0x80.
There are other things it does with the SWD writes, but that's also for later.
SWD reads will read the value and then manipulate the VTOR. Yes, the very same VTOR that is used to derive the encryption key and modify the PSP... meaning that it's essentially a check to see if a memory address contains an expected value.
SWD writes are always done by processor 0, but SWD reads can alternate between 0 and 1 (where 0 is the core executing this code and 1 is the secondary core). However, they are only done by processor 1 when reading 0xE000101C. What is that peripheral, one might ask? It's on the SCS page, but the RP2040 datasheet does not document it. It turns out, of course, that it's documented by the ARMv6-M ARM to be a register holding a the address of a "recently executed" (they deliberately don't define this) instruction.
Because the read is done by core 1, it means they are essentially checking if core 1 is halting at a WFE instruction in the RP2040's bootrom. In other words, they're checking to see if there is any code running on the other core--if you were trying to run e.g. debugging routines on that core but the decryption was failing, this is why. They do this read fairly often, and the value they expect is either 0x180 or 0x174, depending on the bootrom version.
Beyond this they also mostly read the VTOR (self-explanatory) and other SWD comparator registers (which must return 0xFFFFFFFF)... and the systick.
Normally, emulating the systick would be easy, but because it's being read and written by the SWD protocol, we need to keep in mind not only which SWD bit write causes the memory operation (it's the first read done by the 16 bit "turnaround" right after SWCLK is forced high) but also *when* the protocol is permitted to access memory. This is because it must go through the processor core, which can only read/write one address at a time.
After much pain and experimentation I found that both reads and writes dispatch exactly 4 cycles after the instruction that forces SWCLK high.. unless the processor is accessing memory. This means that if an instruction is aligned to 4 bytes, or accesses memory a bunch of times (like POP or LDM), or is 4 bytes wide (like MSR/MRS/BL), the SWD operation will be delayed.
For further example, If an instruction is NOT aligned to 4 bytes and accesses an AHB-lite address, which normally takes 2 cycles, the first cycle will be used to perform the access, and the processor stalls on the second cycle, where the SWD operation can take place. However, if the instruction IS aligned to 4 bytes the first cycle is spent fetching the word it sits on, and the next cycle is used to perform the access, so there is no room and it has to wait for the next instruction. The actual logic is more involved (like with how it interacts with 4 byte instructions and instructions that reference SIO memory) but the bottom line is that getting something to work accurately is not impossible.
..once all of that is implemented, and your emulator properly counts cycles, it's almost smooth sailing from then on. It will keep reading and writing the peripherals mentioned above until a certain point.
Now for the fun part: I imagine the author of the firmware realized that patching the board ID in code was too trivial. To mitigate this, of course, they just.. read it with SWD operations. This requires them to start using PLL_REF, for some reason, (I noticed while replicating this on my Pico that if I didn't use CLK_REF it would freeze.. not sure why) but after that they do the standard reads/writes to 0x18000060 with the message 0x4B.
Obviously an emulator can just see these reads and writes and just return the flash ID, but someone running this on an unintended system will obviously run into trouble.. you could patch the SWD read/write routines and restore the systick, each time, though.
During this, I assume the systick is somewhat unreliable, so they replace a conspicuous global variable pointer to the XOSC COUNT register (which just happens to now run at the same frequency as the SYS clk because it uses the REF PLL) which accesses it in a sequence like:
Code:
LDR Rx, [0x.......] ; gets changed from a random byte to XOSC COUNT address
...
STRB Rn, [Rx] ; in the loop, it stores a part of the VTOR to that address; this is the first access each loop, which explains why they did this.
...
LDRB [Rx] ; first read. if a normal byte address, will be the same value.
; if the XOSC count register, will be decremented a bit
...
LDRB [Rx] ; ditto
meaning that in between reads you have to emulate cycle differences. I found that from the write instruction an offset like +3 worked, although at that point I had already counted a 4 cycle delay from writing to that peripheral. Otherwise, it's the same clk counting idea as the systick, but a lot smaller and easier to see if you're doing it right via doing the same on actual hardware.
After verifying the board ID, they reset the sys clk back to what it was and stop using the XOSC COUNT register in the loop.
Doing more of the same typical reads/writes, they eventually read the systick again, which initially threw me off because my value was wrong. Doing exactly the same thing on my Pico revealed that in total the flash accesses add a delay of 52 cycles, which also worked here, thankfully.
Finally, using the SWD interface they will write and execute PIO(!!) programs. The following only omits the typical reads and writes as well as specific writes to SRAM.
-GPIO16 funcsel <= NULL
-writes to PIO0 instr mem starting at 8:
-then, does a bunch of normal reads/writes with varying patterns>
-replaces globalvar pointers in executeRWC with SIO INTERP0_BASE0, INTERP0_BASE1, INTERP0_BASE2
-they dont read any result values from this, so they act like normal memory locations
-reads the value of the registers (with SWD) to make sure they're being manipulated
it then starts writing PIO programs into memory, and executing PIO instructions directly:
it then executes the following instructions on PIO1 using the SM3_INSTR register:
swdwrite 0x50300120 <= 0xE042
swdwrite 0x50300120 <= 0x4044
swdwrite 0x50300120 <= 0xE04F
swdwrite 0x50300120 <= 0x4044
swdwrite 0x50300120 <= 0xA0E6
Finally, along with some other innocuous SRAM write, it will jump to the binary blob by overwriting a return address on the stack, ending the encryption algorithm.
I hope this information was helpful to someone, and if you want to know more please do not hesitate to DM me. I've spent a long time working on this and I would be very glad knowing it helped someone.
I'm pleased to announce that after significant time working on a cycle-accurate (this is very important) emulator I've finally been able to go past the decryption phase and have dumped the segments of ARM code that is written by the end of the encryption. I have to say, this is the most fun CTF I've ever done, although at some point it ran out of steam and couldn't surprise me that much.
My dump isn't perfect--the code it jumps to itself is a tiny bit obfuscated (as in, it copies code to other locations and jumps there to fool IDA's autoanalysis) but as far as I'm concerned the hard part is over. I even have the PIO code(!!!) it writes and executes on the PIO1 state machines.
This is largely a follow-up of my previous post, and I don't want to duplicate information, so if you're confused I'd recommend reading that one first.
Also, big thanks to the people who sent me their firmwares; without them, none of this would have been possible. If you did send me a firmware and would like others to be able to look at what gets dumped, please tell me so I can do that. I can also release my emulator if it helps someone.. it's just 2000 lines of hastily written Zig code, although you will have to manually find some patch addresses so that it works properly.
I'll split this up into sections to avoid spam.
First, and most unimportant, the mysterious SWD message it sends at the very start just wakes it from a dormant state, so not very interesting.
Then there is the decryption. After initializing some data structures (I called them "wordbank0", "wordbank1", and "constant_random_data_waste_of_time") it sets the VTOR to an initial value (e.g. EE2F8D10, which gets truncated to EE2F8D00) and then goes 16 bytes at a time (we'll call this a block) on the binary blob at the base of the SRAM. It also takes the 8-byte board ID and copies it to a 256 byte structure which I call the flash XOR buffer.
In a block, for each byte, it will first derive a key based on the value stored in the VTOR as well as the flash xor buff and a rolling index into it. This value, along with the previous value of the process stack pointer (PSP/SP_Process/whatever ARM calls it) is then written to the current PSP.
This means that some part of the encryption relies on the *UNINITIALIZED* value of the PSP, which is 0xFFFFFFFC, in case you were having troubles with your emulation.
The key is manipulated some more after that. Interestingly, it then sets the flash XOR buff at the selected index to the value of the current encrypted byte. Finally, it XORs the encrypted byte by the key and writes it.
At the end of a block, after all 16 bytes have been written, it takes the PSP shifted right by 8 bits and XORs it by the decrypted byte at byte 15 (that is, the last decrypted byte in the block) and based on if bits 0 through n (n seems to vary across separate firmwares) are set, in a loop of up to n times (the same n!) it calls a function we'll call readWriteOrCall. This will be looked at later.
Finally, it writes a new PSP. All of this can be seen in publicly available firmwares (in this thread) so I don't want to bother going into specifics; it's not that complicated.
We then have readWriteOrCall, which, based on the input (the value of bit n from (PSP >> 8) ^ last_encrypted_byte ^ key) manipulates the previously mentioned word banks (wordbank0/wordbank1) with some division, multiplication, shifts, etc (it sounds complicated but it's simple enough to just F5 and replicate in IDA) until it eventually maybe decides to call the most important function, which I just called executeRWC.
executeRWC is very funny because in the middle of the controlflow graph there is an innocuous branch that loads a value into R0 and then jumps to it. For a while I thought this was where it jumps inside the encrypted blob, but that is wrong. In fact, it never takes that branch. Go figure! Like I said, this was the most interesting CTF I've ever done.
executeRWC is also very important. It has two other (used) features: that it can arbitrarily read or write memory via sequences to a core's SWD. The action taken, the addresses and data used, and which processor to do it on (this is also important) are vaguely derived from the value given to it, which is derived from how the word banks are manipulated, which is derived from the bit setting of the decrypted byte and the PSP, which is certainly a mouthful.
As expected, the writes are mostly used for anti-debug. After initializing the systick (!!!) it spams writes to 0x4001C080 with the value 0x80 -- this is the pad ctrl disable bit for the external SWD pin, which is why it's impossible to debug the rp2040 while it's decrypting the blob. They also periodically read from this register to make sure it's still 0x80.
There are other things it does with the SWD writes, but that's also for later.
SWD reads will read the value and then manipulate the VTOR. Yes, the very same VTOR that is used to derive the encryption key and modify the PSP... meaning that it's essentially a check to see if a memory address contains an expected value.
SWD writes are always done by processor 0, but SWD reads can alternate between 0 and 1 (where 0 is the core executing this code and 1 is the secondary core). However, they are only done by processor 1 when reading 0xE000101C. What is that peripheral, one might ask? It's on the SCS page, but the RP2040 datasheet does not document it. It turns out, of course, that it's documented by the ARMv6-M ARM to be a register holding a the address of a "recently executed" (they deliberately don't define this) instruction.
Because the read is done by core 1, it means they are essentially checking if core 1 is halting at a WFE instruction in the RP2040's bootrom. In other words, they're checking to see if there is any code running on the other core--if you were trying to run e.g. debugging routines on that core but the decryption was failing, this is why. They do this read fairly often, and the value they expect is either 0x180 or 0x174, depending on the bootrom version.
Beyond this they also mostly read the VTOR (self-explanatory) and other SWD comparator registers (which must return 0xFFFFFFFF)... and the systick.
Normally, emulating the systick would be easy, but because it's being read and written by the SWD protocol, we need to keep in mind not only which SWD bit write causes the memory operation (it's the first read done by the 16 bit "turnaround" right after SWCLK is forced high) but also *when* the protocol is permitted to access memory. This is because it must go through the processor core, which can only read/write one address at a time.
After much pain and experimentation I found that both reads and writes dispatch exactly 4 cycles after the instruction that forces SWCLK high.. unless the processor is accessing memory. This means that if an instruction is aligned to 4 bytes, or accesses memory a bunch of times (like POP or LDM), or is 4 bytes wide (like MSR/MRS/BL), the SWD operation will be delayed.
For further example, If an instruction is NOT aligned to 4 bytes and accesses an AHB-lite address, which normally takes 2 cycles, the first cycle will be used to perform the access, and the processor stalls on the second cycle, where the SWD operation can take place. However, if the instruction IS aligned to 4 bytes the first cycle is spent fetching the word it sits on, and the next cycle is used to perform the access, so there is no room and it has to wait for the next instruction. The actual logic is more involved (like with how it interacts with 4 byte instructions and instructions that reference SIO memory) but the bottom line is that getting something to work accurately is not impossible.
..once all of that is implemented, and your emulator properly counts cycles, it's almost smooth sailing from then on. It will keep reading and writing the peripherals mentioned above until a certain point.
Now for the fun part: I imagine the author of the firmware realized that patching the board ID in code was too trivial. To mitigate this, of course, they just.. read it with SWD operations. This requires them to start using PLL_REF, for some reason, (I noticed while replicating this on my Pico that if I didn't use CLK_REF it would freeze.. not sure why) but after that they do the standard reads/writes to 0x18000060 with the message 0x4B.
Obviously an emulator can just see these reads and writes and just return the flash ID, but someone running this on an unintended system will obviously run into trouble.. you could patch the SWD read/write routines and restore the systick, each time, though.
During this, I assume the systick is somewhat unreliable, so they replace a conspicuous global variable pointer to the XOSC COUNT register (which just happens to now run at the same frequency as the SYS clk because it uses the REF PLL) which accesses it in a sequence like:
Code:
LDR Rx, [0x.......] ; gets changed from a random byte to XOSC COUNT address
...
STRB Rn, [Rx] ; in the loop, it stores a part of the VTOR to that address; this is the first access each loop, which explains why they did this.
...
LDRB [Rx] ; first read. if a normal byte address, will be the same value.
; if the XOSC count register, will be decremented a bit
...
LDRB [Rx] ; ditto
meaning that in between reads you have to emulate cycle differences. I found that from the write instruction an offset like +3 worked, although at that point I had already counted a 4 cycle delay from writing to that peripheral. Otherwise, it's the same clk counting idea as the systick, but a lot smaller and easier to see if you're doing it right via doing the same on actual hardware.
After verifying the board ID, they reset the sys clk back to what it was and stop using the XOSC COUNT register in the loop.
Doing more of the same typical reads/writes, they eventually read the systick again, which initially threw me off because my value was wrong. Doing exactly the same thing on my Pico revealed that in total the flash accesses add a delay of 52 cycles, which also worked here, thankfully.
Finally, using the SWD interface they will write and execute PIO(!!) programs. The following only omits the typical reads and writes as well as specific writes to SRAM.
-GPIO16 funcsel <= NULL
-writes to PIO0 instr mem starting at 8:
-then, does a bunch of normal reads/writes with varying patterns>
-replaces globalvar pointers in executeRWC with SIO INTERP0_BASE0, INTERP0_BASE1, INTERP0_BASE2
-they dont read any result values from this, so they act like normal memory locations
-reads the value of the registers (with SWD) to make sure they're being manipulated
it then starts writing PIO programs into memory, and executing PIO instructions directly:
it then executes the following instructions on PIO1 using the SM3_INSTR register:
swdwrite 0x50300120 <= 0xE042
swdwrite 0x50300120 <= 0x4044
swdwrite 0x50300120 <= 0xE04F
swdwrite 0x50300120 <= 0x4044
swdwrite 0x50300120 <= 0xA0E6
Finally, along with some other innocuous SRAM write, it will jump to the binary blob by overwriting a return address on the stack, ending the encryption algorithm.
I hope this information was helpful to someone, and if you want to know more please do not hesitate to DM me. I've spent a long time working on this and I would be very glad knowing it helped someone.
It's time to see what surprise PlayStation has in store for the future, with its PS5 Technical Presentation broadcast. Will we see a PS5 Pro? Or will it be something...
Information on the successor to the Nintendo Switch has been hard to come by, as with the launch of a new console, Nintendo is keeping things to themselves for now...
Poke Transporter GB is a homebrew app for the GBA that serves to bridge the gap between the old and new games, providing a way to pull Pokemon from the Game Boy and...
A popular hack of Pokemon Emerald utilising the public decompilation project, Pokemon Elite Redux has just had its 2.1 update go live this week. For those who haven't...
Super Mario Eclipse is an extensive and perhaps the most ambitious mod for Super Mario Sunshine yet. After close to 5 years in development, v1.0 of the mod is now...
Sony have announced that their new hero shooter Concord will be going offline on September 6th 2024 and that sales of the game have been immediately halted with Sony...
With a Switch 2 reveal hopefully on the horizon, the question is how much will Nintendo ask for the base model at launch and what will their launch offerings be...
"Black Myth: Wukong" has been one of the most anticipated games in this later half of 2024 due to the development team's focus and love for its unique background...
Hand Held Legend is a well established and quite popular company that's been around for more than a decade and focuses on controllers, upgrades and replacements for...
The first teaser for the upcoming Minecraft movie, creatively titled A Minecraft Movie has released.
Starring Jack Black as Steve and an expanded cast that includes...
It's time to see what surprise PlayStation has in store for the future, with its PS5 Technical Presentation broadcast. Will we see a PS5 Pro? Or will it be something...
Information on the successor to the Nintendo Switch has been hard to come by, as with the launch of a new console, Nintendo is keeping things to themselves for now...
With a Switch 2 reveal hopefully on the horizon, the question is how much will Nintendo ask for the base model at launch and what will their launch offerings be...
Images of what are supposedly a factory prototype for the Nintendo Switch 2 alongside a spec sheet have appeared on a Chinese forum.
The renders show the device from...
The first teaser for the upcoming Minecraft movie, creatively titled A Minecraft Movie has released.
Starring Jack Black as Steve and an expanded cast that includes...
Sony have announced that their new hero shooter Concord will be going offline on September 6th 2024 and that sales of the game have been immediately halted with Sony...
When it comes to Pokemon spin-offs, none are more beloved than the Mystery Dungeon games. Bringing monster collection and the charm of the larger series to the random...
After limited PlayStation exclusivity, Final Fantasy XVI is being released on other platforms. The game's Steam and Epic Games Store versions will launch on September...
"Black Myth: Wukong" has been one of the most anticipated games in this later half of 2024 due to the development team's focus and love for its unique background...
Hand Held Legend is a well established and quite popular company that's been around for more than a decade and focuses on controllers, upgrades and replacements for...