KittyMemoryEx 1.0.0
Dedicated library for runtime code patching, memory scanning, tracing, injection and some more useful memory utilities. works for both Android and Linux
Loading...
Searching...
No Matches
KittyAsm.hpp
1#pragma once
2
3#include <cstdint>
4#include <cstdio>
5#include <string>
6
20enum class EKittyArmCond : uint8_t
21{
22 EQ, NE, HS, LO, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL, NV,
23 None,
24};
25
29enum class EKittyInsnTypeArm32
30{
34 UNKNOWN,
35
39 ADR,
40
46 ADR_REG,
47
48 ADD,
49 SUB,
50
55 SUB_SP_IMM,
56
57 MOV_IMM,
58 MOV_REG,
63 MOV_SHIFTED,
64
65 LDR,
66 STR,
67 LDRB,
68 STRB,
69 LDRH,
70 STRH,
71 LDRSB,
72 LDRSH,
73
77 LDR_LITERAL,
78
85 LDR_PC_REG,
86
87 B,
88 BL,
89 B_COND,
90 BX,
94 PUSH_SP,
98 POP_PC,
102 MOVW,
106 MOVT,
107
113 COUNT,
114};
115
122struct KittyInsnArm32
123{
124 EKittyInsnTypeArm32 type;
125
130 int rd = -1, rn = -1, rt = -1;
131 uint32_t word;
132 uint32_t address;
133 int32_t immediate;
134 uint32_t target;
137 EKittyArmCond cond = EKittyArmCond::None;
138
147
155
163
176
189
200
211
212 KittyInsnArm32()
213 : type(EKittyInsnTypeArm32::UNKNOWN), word(0), address(0), immediate(0), target(0),
214 writeback(false), postIndexed(false), registerOffset(false), shiftedOperand(false),
215 subtractsOffset(false), registerForm(false), pcRelative(false)
216 {
217 }
218
222 inline bool isValid() const
223 {
224 return word != 0 && type != EKittyInsnTypeArm32::UNKNOWN;
225 }
226
230 std::string ToString() const;
231};
232
236enum class EKittyInsnTypeArm64
237{
240 UNKNOWN,
241
242 ADR,
247 ADRP,
248
249 ADD,
250 SUB,
251
252 MOVZ,
253 MOVN,
257 MOVK,
258
259 // ── Scaled immediate-offset load/store ────────────────────────────────────
260 // The width is carried by the *type*, not by a field, so each width is its
261 // own value. Folding any of them together reports the wrong access size, and
262 // access size is what separates a pointer field from a counter.
263
264 LDR,
265 STR,
266 LDRW,
267 STRW,
268 LDRB,
269 STRB,
270 LDRH,
271 STRH,
272 LDRSB,
273 LDRSH,
277 LDRSW,
278
279 // ── Pre-indexed (writeback before the access) ─────────────────────────────
280 // `[Xn, #imm]!` - the offset is applied to Xn *and* kept. A consumer tracking
281 // register values has to invalidate Xn, which `writeback` reports.
282
284 LDR_PRE,
286 STR_PRE,
291 LDRW_PRE,
293 STRW_PRE,
295 LDRW_POST,
297 STRW_POST,
299 LDRB_PRE,
301 STRB_PRE,
303 LDRH_PRE,
305 STRH_PRE,
307 LDRSB_PRE,
309 LDRSH_PRE,
311 LDRSW_PRE,
312
313 // ── Post-indexed (writeback after the access) ─────────────────────────────
314 // `[Xn], #imm` - the access happens at [Xn] and the immediate is applied
315 // afterwards, so the immediate is *not* an access offset. Reading it as one
316 // places the access at an address the program never formed, which is what
317 // `postIndexed` exists to prevent.
318
320 LDR_POST,
322 STR_POST,
324 LDRB_POST,
326 STRB_POST,
328 LDRH_POST,
330 STRH_POST,
332 LDRSB_POST,
334 LDRSH_POST,
336 LDRSW_POST,
337
338 // ── Unscaled immediate offset (LDUR / STUR) ───────────────────────────────
339 // Same widths again, but the 9-bit offset is signed and *unscaled*, where the
340 // LDR/STR forms scale theirs by the access size. Same access, different
341 // immediate encoding - which is exactly why they cannot share a type.
342
344 LDUR,
346 STUR,
348 LDURW,
350 STURW,
352 LDURB,
354 STURB,
356 LDURH,
358 STURH,
360 LDURSB,
362 LDURSH,
364 LDURSW,
365
366 // ── PC-relative literal loads ─────────────────────────────────────────────
367 // `LDR Xt,label` - reads the word *stored at* a PC-relative address, so
368 // `target` is the slot and the register receives its contents.
369
370 LDR_LITERAL,
371 LDRW_LITERAL,
372 LDRSW_LITERAL,
373
374 B,
375 BL,
376 B_COND,
377 CBZ,
378 CBNZ,
379 TBZ,
380 TBNZ,
386 RET,
391 STP_PRE_SP,
395 SUB_SP_IMM,
401 MOV,
402
405 COUNT,
406};
407
414struct KittyInsnArm64
415{
416 EKittyInsnTypeArm64 type = EKittyInsnTypeArm64::UNKNOWN;
417
424 int rd = -1, rn = -1, rt = -1;
427 int rm = -1;
428 uint32_t word;
429 uint64_t address;
430 int64_t immediate;
431 int64_t bitpos;
432 uint64_t target;
435 EKittyArmCond cond = EKittyArmCond::None;
436
445
453
464
465 KittyInsnArm64()
466 : type(EKittyInsnTypeArm64::UNKNOWN), word(0), address(0), immediate(0), bitpos(0),
467 target(0), writeback(false), postIndexed(false), registerOffset(false)
468 {
469 }
470
474 inline bool isValid() const
475 {
476 return word != 0 && type != EKittyInsnTypeArm64::UNKNOWN;
477 }
478
482 std::string ToString() const;
483};
484
490namespace KittyAsm
491{
500 uint32_t bits(uint32_t v, int hi, int lo);
501
509 inline bool bit(uint32_t v, int pos)
510 {
511 return bits(v, pos, pos) != 0;
512 }
513
521 inline uint8_t ror8(uint8_t value, unsigned int shift)
522 {
523 shift &= 7u;
524 return static_cast<uint8_t>((value >> shift) | (value << ((8 - shift) & 7u)));
525 }
526
534 inline uint8_t rol8(uint8_t value, unsigned int shift)
535 {
536 shift &= 7u;
537 return static_cast<uint8_t>((value << shift) | (value >> ((8 - shift) & 7u)));
538 }
539
547 inline uint16_t ror16(uint16_t value, unsigned int shift)
548 {
549 shift &= 15u;
550 return static_cast<uint16_t>((value >> shift) | (value << ((16 - shift) & 15u)));
551 }
552
560 inline uint16_t rol16(uint16_t value, unsigned int shift)
561 {
562 shift &= 15u;
563 return static_cast<uint16_t>((value << shift) | (value >> ((16 - shift) & 15u)));
564 }
565
573 inline uint32_t ror32(uint32_t value, unsigned int shift)
574 {
575 shift &= 31u;
576 return (value >> shift) | (value << ((32 - shift) & 31u));
577 }
578
586 inline uint32_t rol32(uint32_t value, unsigned int shift)
587 {
588 shift &= 31u;
589 return (value << shift) | (value >> ((32 - shift) & 31u));
590 }
591
599 inline uint64_t ror64(uint64_t value, unsigned int shift)
600 {
601 shift &= 63u;
602 return (value >> shift) | (value << ((64 - shift) & 63u));
603 }
604
612 inline uint64_t rol64(uint64_t value, unsigned int shift)
613 {
614 shift &= 63u;
615 return (value << shift) | (value >> ((64 - shift) & 63u));
616 }
617
624 inline uint16_t swap16(uint16_t value)
625 {
626 return static_cast<uint16_t>((value >> 8) | (value << 8));
627 }
628
635 inline uint32_t swap32(uint32_t value)
636 {
637 return ((value & 0x000000FFu) << 24) |
638 ((value & 0x0000FF00u) << 8) |
639 ((value & 0x00FF0000u) >> 8) |
640 ((value & 0xFF000000u) >> 24);
641 }
642
649 inline uint64_t swap64(uint64_t value)
650 {
651 return ((value & 0x00000000000000FFull) << 56) |
652 ((value & 0x000000000000FF00ull) << 40) |
653 ((value & 0x0000000000FF0000ull) << 24) |
654 ((value & 0x00000000FF000000ull) << 8) |
655 ((value & 0x000000FF00000000ull) >> 8) |
656 ((value & 0x0000FF0000000000ull) >> 24) |
657 ((value & 0x00FF000000000000ull) >> 40) |
658 ((value & 0xFF00000000000000ull) >> 56);
659 }
660
668 inline std::string toHexString(uint64_t v)
669 {
670 char Buf[20];
671 std::snprintf(Buf, sizeof(Buf), "0x%llX", static_cast<unsigned long long>(v));
672 return Buf;
673 }
674} // namespace KittyAsm
675
681namespace KittyArm32
682{
683 // ──── Constants ───────────────────────────────────────────────────────────
684
689 constexpr uint32_t kInstructionStride = 4;
690
695 constexpr uint32_t kPcBias = 8;
696
699 constexpr int kLinkRegister = 14;
700
706 constexpr int kNumGPRegisters = 15;
707
708 // ──── Bit helpers ─────────────────────────────────────────────────────────
709
717 inline int32_t signExtend(uint32_t val, int bits)
718 {
719 // Guarded against the field's own width, not the ARM64 twin's: `m` is
720 // 32-bit here, so a shift landing at bit 32 or above would compute a
721 // value the narrowing conversion silently reduces to 0, leaving the
722 // out-of-range guard doing nothing to earn its keep.
723 if (bits <= 0 || bits >= 32)
724 return (int32_t)val;
725
726 uint32_t m = 1u << (bits - 1);
727 return (int32_t)((val ^ m) - m);
728 }
729
730 // ──── Register naming and indexing ────────────────────────────────────────
731
738 inline std::string regName(unsigned r)
739 {
740 if (r == 13)
741 return "sp";
742 if (r == 14)
743 return "lr";
744 if (r == 15)
745 return "pc";
746
747 std::string reg = "r";
748 return reg + std::to_string(r);
749 }
750
762 int regIndex(const std::string &name);
763
770 inline std::string branchCondName(uint32_t cond)
771 {
772 // Masked, not rejected: the condition is a 4-bit field and callers pass
773 // it straight out of the instruction word, so anything above 15 is a
774 // caller error rather than a distinct condition. Masking keeps the
775 // lookup in range for every possible input.
776 static const char *names[16] =
777 {"EQ", "NE", "CS/HS", "CC/LO", "MI", "PL", "VS", "VC", "HI", "LS", "GE", "LT", "GT", "LE", "AL", "NV"};
778 return names[cond & 0xF];
779 }
780
781 // ──── Instruction classification ──────────────────────────────────────────
782
789 std::string typeToString(EKittyInsnTypeArm32 t);
790
799 uint8_t loadStoreWidth(EKittyInsnTypeArm32 t);
800
805 bool isLoad(EKittyInsnTypeArm32 t);
806
811 bool isStore(EKittyInsnTypeArm32 t);
812
813 // ──── Decoding ────────────────────────────────────────────────────────────
814
821 EKittyInsnTypeArm32 decodeInsnType(uint32_t instr);
822
830 KittyInsnArm32 decodeInsn(uint32_t instr, uint32_t address = 0);
831
832} // namespace KittyArm32
833
834
840namespace KittyArm64
841{
842 // ──── Constants ───────────────────────────────────────────────────────────
843
847 constexpr uint32_t kInstructionStride = 4;
848
852 constexpr int kNumGPRegisters = 32;
853
854 // ──── Bit helpers ─────────────────────────────────────────────────────────
855
863 inline int64_t signExtend(uint32_t val, int bits)
864 {
865 if (bits <= 0 || bits >= 64)
866 return (int64_t)val;
867
868 uint64_t m = 1ULL << (bits - 1);
869 return (int64_t)((val ^ m) - m);
870 }
871
872 // ──── Register naming and indexing ────────────────────────────────────────
873
890
894 inline std::string xRegName(unsigned reg, EZeroRegOrStackPointer at31)
895 {
896 if (reg == 31)
897 {
898 return at31 == EZeroRegOrStackPointer::StackPointer ? "SP" : "XZR";
899 }
900 return std::string("X") + std::to_string(reg);
901 }
902
906 inline std::string wRegName(unsigned reg, EZeroRegOrStackPointer at31)
907 {
908 if (reg == 31)
909 {
910 return at31 == EZeroRegOrStackPointer::StackPointer ? "SP" : "WZR";
911 }
912 return std::string("W") + std::to_string(reg);
913 }
914
929 inline int resolveRegIndex(unsigned reg, EZeroRegOrStackPointer at31)
930 {
931 if (reg == 31)
932 return at31 == EZeroRegOrStackPointer::StackPointer ? 31 : -1;
933 return static_cast<int>(reg);
934 }
935
945 int regIndex(const std::string &name);
946
953 inline std::string branchCondName(uint32_t cond)
954 {
955 static const char *names[16] =
956 {"EQ", "NE", "HS", "LO", "MI", "PL", "VS", "VC", "HI", "LS", "GE", "LT", "GT", "LE", "AL", "NV"};
957 uint32_t index = cond & 0xF;
958 return index < 16 ? names[index] : "";
959 }
960
961 // ──── Instruction classification ──────────────────────────────────────────
962
969 std::string typeToString(EKittyInsnTypeArm64 t);
970
986 uint8_t loadStoreWidth(EKittyInsnTypeArm64 t);
987
997 bool isLoad(EKittyInsnTypeArm64 t);
998
1003 bool isStore(EKittyInsnTypeArm64 t);
1004
1005 // ──── Decoding ────────────────────────────────────────────────────────────
1006
1013 EKittyInsnTypeArm64 decodeInsnType(uint32_t instr);
1014
1022 KittyInsnArm64 decodeInsn(uint32_t instr, uint64_t address = 0);
1023
1024} // namespace KittyArm64
Namespace containing utility functions for ARM32 instructions.
Definition KittyAsm.hpp:682
EKittyInsnTypeArm32 decodeInsnType(uint32_t instr)
Decodes type of an ARM32 instruction.
int32_t signExtend(uint32_t val, int bits)
Sign-extends a 32-bit unsigned integer to a 32-bit signed integer.
Definition KittyAsm.hpp:717
bool isLoad(EKittyInsnTypeArm32 t)
True when t is one of the load instruction types.
std::string branchCondName(uint32_t cond)
Returns the name of a conditional execution flag.
Definition KittyAsm.hpp:770
constexpr uint32_t kPcBias
Definition KittyAsm.hpp:695
constexpr uint32_t kInstructionStride
Definition KittyAsm.hpp:689
bool isStore(EKittyInsnTypeArm32 t)
True when t is one of the store instruction types.
constexpr int kLinkRegister
Definition KittyAsm.hpp:699
std::string typeToString(EKittyInsnTypeArm32 t)
Converts an EKittyInsnTypeArm32 to a string representation.
constexpr int kNumGPRegisters
Definition KittyAsm.hpp:706
int regIndex(const std::string &name)
Inverse of regName(): maps a decoded register name back to its index.
uint8_t loadStoreWidth(EKittyInsnTypeArm32 t)
Load/Store access width in bytes for an ARM32 instruction type.
std::string regName(unsigned r)
Returns the name of an ARM32 general-purpose register.
Definition KittyAsm.hpp:738
KittyInsnArm32 decodeInsn(uint32_t instr, uint32_t address=0)
Decodes an ARM32 instruction.
Namespace containing utility functions for ARM64 instructions.
Definition KittyAsm.hpp:841
EKittyInsnTypeArm64 decodeInsnType(uint32_t instr)
Decodes type of an ARM64 instruction.
EZeroRegOrStackPointer
What register index 31 means in a given instruction field.
Definition KittyAsm.hpp:886
@ ZeroRegister
register 31 denotes XZR/WZR
Definition KittyAsm.hpp:887
@ StackPointer
register 31 denotes SP
Definition KittyAsm.hpp:888
int resolveRegIndex(unsigned reg, EZeroRegOrStackPointer at31)
Resolves a raw 5-bit encoded register field into a trackable index, without naming it.
Definition KittyAsm.hpp:929
uint8_t loadStoreWidth(EKittyInsnTypeArm64 t)
Load/Store access width in bytes for an AArch64 instruction type.
std::string typeToString(EKittyInsnTypeArm64 t)
Converts an EKittyInsnTypeArm64 to a string representation.
std::string wRegName(unsigned reg, EZeroRegOrStackPointer at31)
Returns the name of an ARM64 general-purpose W-register.
Definition KittyAsm.hpp:906
constexpr int kNumGPRegisters
Definition KittyAsm.hpp:852
KittyInsnArm64 decodeInsn(uint32_t instr, uint64_t address=0)
Decodes an ARM64 instruction.
std::string xRegName(unsigned reg, EZeroRegOrStackPointer at31)
Returns the name of an ARM64 general-purpose X-register.
Definition KittyAsm.hpp:894
constexpr uint32_t kInstructionStride
Definition KittyAsm.hpp:847
std::string branchCondName(uint32_t cond)
Returns the name of a conditional execution flag.
Definition KittyAsm.hpp:953
int64_t signExtend(uint32_t val, int bits)
Sign-extends a 32-bit unsigned integer to a 64-bit signed integer.
Definition KittyAsm.hpp:863
bool isStore(EKittyInsnTypeArm64 t)
True when t is one of the store instruction types.
bool isLoad(EKittyInsnTypeArm64 t)
True when t is one of the load instruction types.
int regIndex(const std::string &name)
Inverse of xRegName()/wRegName(): maps a decoded register name back to its index.
Namespace containing utility functions for asm instructions.
Definition KittyAsm.hpp:491
uint16_t rol16(uint16_t value, unsigned int shift)
Rotates a 16-bit unsigned integer to the left by a specified amount.
Definition KittyAsm.hpp:560
uint8_t rol8(uint8_t value, unsigned int shift)
Rotates an 8-bit unsigned integer to the left by a specified amount.
Definition KittyAsm.hpp:534
uint64_t rol64(uint64_t value, unsigned int shift)
Rotates a 64-bit unsigned integer to the left by a specified amount.
Definition KittyAsm.hpp:612
uint64_t swap64(uint64_t value)
Reverses the byte order of a 64-bit unsigned integer.
Definition KittyAsm.hpp:649
uint32_t ror32(uint32_t value, unsigned int shift)
Rotates a 32-bit unsigned integer to the right by a specified amount.
Definition KittyAsm.hpp:573
uint8_t ror8(uint8_t value, unsigned int shift)
Rotates an 8-bit unsigned integer to the right by a specified amount.
Definition KittyAsm.hpp:521
uint32_t bits(uint32_t v, int hi, int lo)
Extracts a bit value from a 32-bit unsigned integer.
bool bit(uint32_t v, int pos)
Checks if a specific bit is set in a 32-bit unsigned integer.
Definition KittyAsm.hpp:509
uint16_t ror16(uint16_t value, unsigned int shift)
Rotates a 16-bit unsigned integer to the right by a specified amount.
Definition KittyAsm.hpp:547
uint16_t swap16(uint16_t value)
Reverses the byte order of a 16-bit unsigned integer.
Definition KittyAsm.hpp:624
std::string toHexString(uint64_t v)
Formats a value as a "0x"-prefixed, upper-case hex string.
Definition KittyAsm.hpp:668
uint32_t rol32(uint32_t value, unsigned int shift)
Rotates a 32-bit unsigned integer to the left by a specified amount.
Definition KittyAsm.hpp:586
uint64_t ror64(uint64_t value, unsigned int shift)
Rotates a 64-bit unsigned integer to the right by a specified amount.
Definition KittyAsm.hpp:599
uint32_t swap32(uint32_t value)
Reverses the byte order of a 32-bit unsigned integer.
Definition KittyAsm.hpp:635
Struct representing an ARM32 instruction.
Definition KittyAsm.hpp:123
uint32_t address
Where it lives, for PC-relative targets.
Definition KittyAsm.hpp:132
EKittyArmCond cond
Definition KittyAsm.hpp:137
bool isValid() const
Checks if the instruction is valid.
Definition KittyAsm.hpp:222
uint32_t word
The instruction word itself, as decoded.
Definition KittyAsm.hpp:131
bool shiftedOperand
The second operand / offset register is a shifted register.
Definition KittyAsm.hpp:175
bool pcRelative
Rn is PC, so the address is relative to this instruction.
Definition KittyAsm.hpp:210
bool writeback
The instruction updates its base register (Rn) as a side effect.
Definition KittyAsm.hpp:146
int rd
Definition KittyAsm.hpp:130
bool registerOffset
The offset is a register (held in rt), not immediate.
Definition KittyAsm.hpp:162
bool postIndexed
The access happens at [Rn]; immediate is applied afterwards.
Definition KittyAsm.hpp:154
bool subtractsOffset
The register operand is subtracted rather than added.
Definition KittyAsm.hpp:188
std::string ToString() const
Renders the instruction as a single line of ARM32 assembly text.
bool registerForm
The second operand is a register (held in rt), not immediate.
Definition KittyAsm.hpp:199
Struct representing an ARM64 instruction.
Definition KittyAsm.hpp:415
int rd
Definition KittyAsm.hpp:424
uint64_t address
Where it lives, for PC-relative targets.
Definition KittyAsm.hpp:429
EKittyArmCond cond
Definition KittyAsm.hpp:435
bool writeback
The instruction updates its base register (Rn) as a side effect.
Definition KittyAsm.hpp:444
int64_t immediate
Decoded immediate, sign-applied. 0 when there is none.
Definition KittyAsm.hpp:430
int rm
Definition KittyAsm.hpp:427
bool isValid() const
Checks if the instruction is valid.
Definition KittyAsm.hpp:474
uint64_t target
Definition KittyAsm.hpp:432
int64_t bitpos
Bit position for the bitfield/test forms; -1 when n/a.
Definition KittyAsm.hpp:431
uint32_t word
The instruction word itself, as decoded.
Definition KittyAsm.hpp:428
std::string ToString() const
Renders the instruction as a single line of ARM64 assembly text.
bool registerOffset
The offset is a register (held in rm), not immediate.
Definition KittyAsm.hpp:463
bool postIndexed
The access happens at [Rn]; immediate is applied afterwards.
Definition KittyAsm.hpp:452