As I Commented the operation here is R0 == R0+R1
the Current Program Status Register or
Application Program Status Register are modified based on the result
Assume for example
R0 contains 0xffffffff (maximum possible 32 bit unsigned integer)
R1 contains 0x00000001
R0+R1 will be
>>> print(hex(0xffffffff+0x00000001))
0x100000000
>>>
the result is a 33bit or in other words the result is 0x0 and the 33rd bit is lost
since result is 0 N or Negative flag is not set N=0
again since result is 0 zero flag is set to 1 Z=1
the upper 33rd bit is lost so Carry flag is set C==1
signed or v flag doesn't not have any overflow (-1 + 1 ) == 0 so V flag is not set V=0
this all are explained in this document
there is a source code and demo also attached in the link
where adds and the cpsr details are retrieved by this assembly stub
you can use unicorn or unicorn based qiling to emulate these instruction
doADDS:
@ Perform the operation, leaving the result in r0 to return it.
adds r0, r0, r1
@ Get the APSR flags and dump them into flags (*r2).
mrs r3, CPSR
str r3, [r2]
@ The recommended (interworking-compatible) return method:
bx lr
a sample emulation and cpsr flags using Qiling Framework
:\>type armshell.py
from qiling import *
from qiling.const import QL_VERBOSE
# 0x0000000000000000: 00 00 E0 E3 mvn r0, #0
# 0x0000000000000004: 01 10 A0 E3 mov r1, #1
# 0x0000000000000008: 01 00 90 E0 adds r0, r0, r1
# 0x000000000000000c: 00 F0 20 E3 nop
# 0x0000000000000010: 00 F0 20 E3 nop
shellcode = b"\x00\x00\xe0\xe3\x01\x10\xa0\xe3\x01\x00\x90\xe0\x00\xf0\x20\xe3\x00\xf0\x20\xe3"
ql = Qiling(
code=shellcode,
rootfs=r"F:\QILING\examples\rootfs\x8664_windows",
ostype="linux",
archtype="arm",
verbose=QL_VERBOSE.DEBUG
)
ql.reg.cpsr = 0
ql.reg.r0 = 0
ql.reg.r1 = 0
print("registers before starting")
print("r0 = 0x{:x}".format(ql.reg.r0))
print("r1 = 0x{:x}".format(ql.reg.r1))
print("cpsr = 0x{:x}\n".format(ql.reg.cpsr))
for i in range(1, 5, 1):
ql.run(0, len(shellcode), -1, i)
print("r0 = 0x{:x}".format(ql.reg.r0))
print("r1 = 0x{:x}".format(ql.reg.r1))
print("cpsr = 0x{:x}\n".format(ql.reg.cpsr))
executed
:\>python armshell.py
[+] Profile: Default
[+] Set init_kernel_get_tls
registers before starting
r0 = 0x0
r1 = 0x0
cpsr = 0x0
r0 = 0xffffffff
r1 = 0x0
cpsr = 0x0
r0 = 0xffffffff
r1 = 0x1
cpsr = 0x0
r0 = 0x0
r1 = 0x1
cpsr = 0x60000000
r0 = 0x0
r1 = 0x1
cpsr = 0x60000000
the cpsr is set with bit 29,30 representing flags Z and C (Zero and Carry)
>>> hex(1<<30|1<<29)
'0x60000000'
>>>
feeding with a ladle
from qiling import *
from qiling.const import QL_VERBOSE
# 0x0000000000000000: 00 F0 20 E3 nop
# 0x0000000000000004: 01 00 A0 E3 mov r0, #1
# 0x0000000000000008: 06 10 A0 E3 mov r1, #6
# 0x000000000000000c: 00 00 11 E1 tst r1, r0
# 0x0000000000000010: FA 17 00 1A bne #0x6000
# 0x0000000000000014: 00 F0 20 E3 nop
shellcode = b"\x00\xf0\x20\xe3\x01\x00\xa0\xe3\x06\x10\xa0\xe3\x00\x00\x11\xe1\xfa\x17\x00\x1a\x00\xf0\x20\xe3"
ql = Qiling(
code=shellcode,
rootfs=r"F:\QILING\examples\rootfs\x8664_windows",
ostype="linux",
archtype="arm",
verbose=QL_VERBOSE.DEBUG
)
ql.reg.cpsr = 0
ql.reg.r0 = 0
ql.reg.r1 = 0
print("registers before starting")
print("r0 = 0x{:x}".format(ql.reg.r0))
print("r1 = 0x{:x}".format(ql.reg.r1))
print("cpsr = 0x{:x}\n".format(ql.reg.cpsr))
for i in range(1, 5, 1):
ql.run(0, len(shellcode), -1, i)
print("r0 = 0x{:x}".format(ql.reg.r0))
print("r1 = 0x{:x}".format(ql.reg.r1))
print("cpsr = 0x{:x}\n".format(ql.reg.cpsr))
emulation of tst instruction.
F:\QILING\testqiling>python armtstop.py
[+] Profile: Default
[+] Set init_kernel_get_tls
registers before starting
r0 = 0x0
r1 = 0x0
cpsr = 0x0
r0 = 0x0
r1 = 0x0
cpsr = 0x0
r0 = 0x1
r1 = 0x0
cpsr = 0x0
r0 = 0x1
r1 = 0x6
cpsr = 0x0
r0 = 0x1
r1 = 0x6
cpsr = 0x40000000 <<<<<<<<<<