TGViewer
ThePedroo ThePedroo @thepedr002 · 1.22K subscribers
Post #36 2.86K
Morning, everyone. Quite some time, but now we're (we? Who? Me) back here.

Treat Wheel. Or Threat Wheel, or Tricky Wheel. Whatever you call it, it is a hiding module of age already. It dates back to the beginning of 2025. It has been recently FOSS-ed, so we can take a look on it. For the sake of simplicity, let's see the simplest ones

1. Zygote mountinfo leak

This detection happens early on Zygote. On initialization, Zygote does several operations regarding mounts. Creating a new mount namespace, marking rootfs as MS_SLAVE, mounting tmpfs, and... umounting /storage mounts.

While plain umount is fine by itself, Zygote utilizes a very specific libc function to grab the active mounts: getmntent. The special characteristic of that function is that it is backed by a global buffer in libc (getmntent.c#L25). When Zygote uses it, it ends up leaving the last mount on /proc/mounts on that buffer.

Root implementations and modules usually mount late. That leads to their mounts being the last ones there. When they umount, the mounts will disappear from procfs, but the trace of the last mount will be cached in that global buffer, and it can be observed.

Treat Wheel fixes this by forking a new process to run setmntent and getmntent (bypassing procfs related detections), grabbing its address and new buffer value, passing to parent, and having the parent write to the address the children's buffer value. Since they share the same mount namespace, they should have equal values when up-to-date. This fixes this detection.

2. AtExit handles

Bjarne did not approve this detection — but let's focus on C++ for this one. When developing any software, especially on C++, the standard library needs to be initialized. However, not only that, it also needs to be de-initialized. That's the issue AtExit handles solve.

libc keeps track of all AtExit handles, so that before it exists, it can call them. However, the problem is that when they are not utilized, instead of being removed, they're simply zero-d. In a list that all of the entries will point to valid addresses, and usually non-zero, having a zero address is abnormal.

Treat Wheel fixes that by directly manipulating the list. However, detecting which ones are from the system, and which ones are created by Zygisk modules is not an easy task when they're already zero. Treat Wheel uses heuristics for that. All entries before libminikin.so happen before most system libraries — but after Zygisk modules. By removing those, we can safely patch this detection.

3. Frida

Frida is extremely detectable as-is. However, those standard Frida detections is not what we go after. Something that got my attention is how Frida left detections on SoList too, similarly to old Zygisks.

This detection involves doing the same as what we used to do in Zygisk before custom linkers came — set the size of the VMAs to 0, then call soinfo_unload, which removed its SoInfo entry from SoList, evading most generic injection detections.



This was simply a post I made rapidly out of boredom and willingness to spread more information about the detections Treat Wheel fixes. While I would love to include URL to sources, Telegram didn't find the same so attracting, by persisting the image of a bird in my clipboard. Enjoy the text and the bird, then.

Sincerely,
ThePedroo.
  • 👍 17
  • 🔥 6
  • ❤ 3
  • 👏 2
  • 🤔 1
More from @thepedr002
  1. Sep 24, 2026(And ReZygisk 1.0.1, too.Two ones, two two ones!)
  2. Sep 16, 2026(off-topic: Treat Wheel 0.0.11 soon!)
  3. Sep 16, 2026Hello, everyone. Another day, another ignorance. When it comes to RASP (Runtime Applicatio…
  4. Aug 11, 2026(Some credits for me. I've been testing and writing at 2am)
  5. Aug 11, 2026Post #41
  6. Aug 8, 2026Post #40
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →