コード例 #1
0
    def test_mcu_usart_input_stm32f411(self):
        ql = Qiling(["../examples/rootfs/mcu/stm32f411/md5_server.hex"],
                    archtype="cortex_m",
                    profile="stm32f411",
                    verbose=QL_VERBOSE.OFF)

        ql.hw.create('usart2')
        ql.hw.create('rcc')

        ql.run(count=1000)

        ql.hw.usart2.send(b'Hello\n')
        ql.run(count=30000)
        ql.hw.usart2.send(b'USART\n')
        ql.run(count=30000)
        ql.hw.usart2.send(b'Input\n')
        ql.run(count=30000)

        buf = ql.hw.usart2.recv()
        self.assertEqual(
            buf,
            b'8b1a9953c4611296a827abf8c47804d7\n2daeb613094400290a24fe5086c68f06\n324118a6721dd6b8a9b9f4e327df2bf5\n'
        )

        del ql
コード例 #2
0
def crack(passwd):
    ql = Qiling(["../../examples/rootfs/mcu/stm32f407/backdoorlock.hex"],
                archtype="cortex_m",
                env=stm32f407,
                verbose=QL_VERBOSE.OFF)

    ql.hw.create('spi2')
    ql.hw.create('gpioe')
    ql.hw.create('gpiof')
    ql.hw.create('usart1')
    ql.hw.create('rcc')

    ql.hw.show_info()

    print('Testing passwd', passwd)

    ql.patch(0x8000238, b'\x00\xBF' * 4)
    ql.patch(0x80031e4, b'\x00\xBF' * 11)
    ql.patch(0x80032f8, b'\x00\xBF' * 13)
    ql.patch(0x80013b8, b'\x00\xBF' * 10)

    ql.hw.usart1.send(passwd.encode() + b'\r')

    ql.hw.systick.set_ratio(100)
    ql.run(count=1000000, end=0x8003225)
    if ql.arch.effective_pc == 0x8003225:
        print('Success, the passwd is', passwd)
    else:
        print('Fail, the passwd is not', passwd)

    del ql
コード例 #3
0
    def test_mcu_serial_sam3x8e(self):
        ql = Qiling(["../examples/rootfs/mcu/sam3x8e/serial.ino.hex"],
                    archtype="cortex_m",
                    env=sam3x8e,
                    verbose=QL_VERBOSE.DEFAULT)

        ql.hw.create('wdt')
        ql.hw.create('efc0')
        ql.hw.create('efc1')
        ql.hw.create('pmc')
        ql.hw.create('uotghs')
        ql.hw.create('pioa')
        ql.hw.create('piob')
        ql.hw.create('pioc')
        ql.hw.create('piod')
        ql.hw.create('adc')
        ql.hw.create('uart')
        ql.hw.create('pdc_uart')

        ql.hw.systick.ratio = 1000
        ql.run(count=100000)
        self.assertTrue(
            ql.hw.uart.recv().startswith(b'hello world\nhello world\n'))

        del ql
コード例 #4
0
    def test_mcu_usart_stm32f103(self):
        ql = Qiling([
            "../examples/rootfs/mcu/stm32f103/sctf2020-password-lock-plus.hex"
        ],
                    archtype="cortex_m",
                    env=stm32f103,
                    verbose=QL_VERBOSE.DEFAULT)

        ql.hw.create('rcc')
        ql.hw.create('flash interface')
        ql.hw.create('exti')
        ql.hw.create('usart1')
        ql.hw.create('gpioa')
        ql.hw.create('afio')
        ql.hw.create('dma1').watch()

        data = []

        def gpio_set_cb(pin):
            data.append(pin)

        ql.hw.gpioa.hook_set(1, gpio_set_cb, '1')
        ql.hw.gpioa.hook_set(2, gpio_set_cb, '2')
        ql.hw.gpioa.hook_set(3, gpio_set_cb, '3')
        ql.hw.gpioa.hook_set(4, gpio_set_cb, '4')

        ql.run(count=400000)

        self.assertTrue((''.join(data)).find('1442413') != -1)
        self.assertTrue(ql.hw.usart1.recv()[:23] == b'SCTF{that1s___r1ghtflag')

        del ql
コード例 #5
0
    def test_mcu_i2c_stm32f411(self):
        ql = Qiling(
            ["../examples/rootfs/mcu/stm32f411/i2c-lcd.bin", 0x8000000],
            archtype="cortex_m",
            profile="stm32f411",
            verbose=QL_VERBOSE.DEBUG)

        ql.hw.create('i2c1')
        ql.hw.create('rcc')
        ql.hw.create('gpioa')
        ql.hw.create('gpiob')

        flag = False

        def indicator():
            nonlocal flag
            flag = True

        ql.hw.gpioa.hook_set(5, indicator)

        class LCD:
            address = 0x3f << 1

            def send(self, data):
                pass

        ql.hw.i2c1.connect(LCD())
        ql.run(count=550000)

        self.assertTrue(flag)

        del ql
コード例 #6
0
        def crack(passwd):
            ql = Qiling(["../examples/rootfs/mcu/stm32f407/backdoorlock.hex"],
                        archtype="cortex_m",
                        profile="stm32f407",
                        verbose=QL_VERBOSE.OFF)

            ql.hw.create('spi2')
            ql.hw.create('gpioe')
            ql.hw.create('gpiof')
            ql.hw.create('usart1')
            ql.hw.create('rcc')

            print('Testing passwd', passwd)

            ql.patch(0x8000238, b'\x00\xBF' * 4)
            ql.patch(0x80031e4, b'\x00\xBF' * 11)
            ql.patch(0x80032f8, b'\x00\xBF' * 13)
            ql.patch(0x80013b8, b'\x00\xBF' * 10)

            ql.hw.usart1.send(passwd.encode() + b'\r')

            ql.hw.systick.set_ratio(400)

            ql.run(count=400000, end=0x8003225)

            return ql.arch.get_pc() == 0x8003225
コード例 #7
0
    def test_mcu_crc_stm32f407(self):
        ql = Qiling(["../examples/rootfs/mcu/stm32f407/ai-sine-test.elf"],
                    archtype="cortex_m",
                    env=stm32f407,
                    verbose=QL_VERBOSE.DEFAULT)

        ql.hw.create('rcc')
        ql.hw.create('pwr')
        ql.hw.create('flash interface')
        ql.hw.create('gpioa')
        ql.hw.create('gpiob')
        ql.hw.create('gpiod')
        ql.hw.create('spi1')
        ql.hw.create('crc')
        ql.hw.create('dbgmcu')

        flag = False

        def indicator(ql):
            nonlocal flag
            ql.log.info('PA7 set')
            flag = True

        ql.hw.gpioa.hook_set(7, indicator, ql)
        ql.hw.systick.ratio = 1000

        ql.run(count=600000)
        self.assertTrue(flag)

        del ql
コード例 #8
0
ファイル: test_mcu.py プロジェクト: profiles/qiling
    def test_mcu_led_stm32f411(self):
        ql = Qiling(["../examples/rootfs/mcu/stm32f411/rand_blink.hex"],                    
                    archtype="cortex_m", env=stm32f411, verbose=QL_VERBOSE.DISASM)

        # Set verbose=QL_VERBOSE.DEFAULT to find warning
        ql.run(count=1000)

        del ql
コード例 #9
0
def stm32f4_led():
    ql = Qiling(["../rootfs/mcu/stm32f411/rand_blink.hex"],
                archtype="cortex_m",
                profile="stm32f411",
                verbose=QL_VERBOSE.DISASM)

    # Set verbose=QL_VERBOSE.DEFAULT to find warning
    ql.run(count=1000)
コード例 #10
0
    def test_riscv32_hello_dyn_linux(self):
        stdout = SimpleOutStream(1)
        ql = Qiling(['../examples/rootfs/riscv32_linux/bin/hello-linux'], '../examples/rootfs/riscv32_linux/', 
                    verbose=QL_VERBOSE.DEFAULT, stdout=stdout)

        ql.run()
        self.assertTrue(stdout.read() == b'Hello, World!\n')

        del ql
コード例 #11
0
def stm32f411_freertos():
    ql = Qiling(["../rootfs/mcu/stm32f411/os-demo.hex"],                    
        archtype="cortex_m", env=stm32f411, verbose=QL_VERBOSE.DEBUG)

    ql.hw.create('usart2').watch()
    ql.hw.create('gpioa').watch()
    ql.hw.create('rcc')    

    ql.hw.systick.set_ratio(100)
    ql.run(count=200000)
コード例 #12
0
    def test_riscv64_hello_linux(self):
        stdout = SimpleOutStream(1)
        ql = Qiling(['../examples/rootfs/riscv64_linux/bin/hello'], '../examples/rootfs/riscv64_linux/', 
                    verbose=QL_VERBOSE.DEFAULT)

        def close(ql, fd):
            return 0
        ql.os.set_syscall("close", close, QL_INTERCEPT.CALL)
        ql.os.stdout = stdout
        ql.run()
        self.assertTrue(stdout.read() == b'Hello, World!\n')

        del ql
コード例 #13
0
ファイル: test_mcu.py プロジェクト: profiles/qiling
    def test_mcu_usart_output_stm32f411(self):
        ql = Qiling(["../examples/rootfs/mcu/stm32f411/hello_usart.hex"],                    
                    archtype="cortex_m", env=stm32f411, verbose=QL_VERBOSE.DEFAULT)        
        
        ql.hw.create('usart2')
        ql.hw.create('rcc')

        ql.run(count=2000)
        buf = ql.hw.usart2.recv()
        print('[1] Received from usart: ', buf)
        self.assertEqual(buf, b'Hello USART\n')

        del ql
コード例 #14
0
ファイル: test_mcu.py プロジェクト: profiles/qiling
    def test_mcu_spi_stm32f411(self):
        ql = Qiling(["../examples/rootfs/mcu/stm32f411/spi-test.bin", 0x8000000],
            archtype="cortex_m", env=stm32f411, verbose=QL_VERBOSE.DEFAULT)

        ql.hw.create('spi1')
        ql.hw.create('rcc')
        ql.hw.create('usart2')
        ql.hw.create('gpioa')

        ql.run(count=30000)
        self.assertTrue(ql.hw.usart2.recv() == b'----------------SPI TEST----------------\najcmfoiblenhakdmgpjclfoibkengajd\nmfpicleohbkdngajcmfoiblenhakdmgp\njclfoibkengajdmfpicleohbkdngajcm\nfoiblenhakdmgpjclfoibkengajdmfpi\ncleohbkdngajcmfoiblenhakdmgpjclf\noibkenhajdmfpicleohbkdngajcmfpib\nlenhakdmgpjclfoibkenhajdmfpicleo\nhbkdngajcmfpiblenhakdmgpjclfoibk\n----------------TEST END----------------\n')

        del ql
コード例 #15
0
def test_mcu_gpio_stm32f411():
    ql = Qiling(["../../examples/rootfs/mcu/stm32f411/hello_gpioA.hex"],                    
                archtype="cortex_m", profile="stm32f411", verbose=QL_VERBOSE.DEBUG)

    ql.hw.create('usart2')
    ql.hw.create('rcc')
    ql.hw.create('gpioa')
    

    ql.hw.gpioa.hook_set(5, lambda: print('LED light up'))
    ql.hw.gpioa.hook_reset(5, lambda: print('LED light off'))

    ql.run(count=10000)
コード例 #16
0
def stm32f411_dma():
    ql = Qiling(["../rootfs/mcu/stm32f411/dma-clock.hex"],                    
        archtype="cortex_m", profile="stm32f411", verbose=QL_VERBOSE.DEBUG)

    ql.hw.create('usart2')
    ql.hw.create('dma1')
    ql.hw.create('rcc')

    ql.run(count=200000)
    buf = ql.hw.usart2.recv()

    ## check timestamp
    tick = [int(x) for x in buf.split()]
    for i in range(1, len(tick)):
        assert(4 <= tick[i] - tick[i - 1] <= 6)
コード例 #17
0
ファイル: test_mcu.py プロジェクト: profiles/qiling
    def test_mcu_uart_rust_stm32f411(self): 
        ql = Qiling(["../examples/rootfs/mcu/stm32f411/uart-rust.hex"],
                    archtype="cortex_m", env=stm32f411, verbose=QL_VERBOSE.DEFAULT)

        ## cover env by profiles

        ql.hw.create('rcc')
        ql.hw.create('gpioa')
        ql.hw.create('usart2')

        ql.hw.usart2.send(b'123')
        ql.run(count=10000)
        self.assertTrue(ql.hw.usart2.recv() == b'1')

        del ql
コード例 #18
0
def get_kaimendaji_password():
    def my_getenv(ql, *args, **kwargs):
        env = {"ID": b"000000000000000", "ethaddr": b"11:22:33:44:55:66"}
        params = ql.os.resolve_fcall_params({'key': STRING})
        value = env.get(params["key"], b"")

        value_addr = ql.os.heap.alloc(len(value))
        ql.mem.write(value_addr, value)

        ql.arch.regs.r0 = value_addr
        ql.arch.regs.arch_pc = ql.arch.regs.lr

    def get_password(ql, *args, **kwargs):
        password_raw = ql.mem.read(ql.arch.regs.r0, ql.arch.regs.r2)

        password = ''
        for item in password_raw:
            if 0 <= item <= 9:
                password += chr(item + 48)
            else:
                password += chr(item + 87)

        print("The password is: %s" % password)

    def partial_run_init(ql):
        # argv prepare
        ql.arch.regs.arch_sp -= 0x30
        arg0_ptr = ql.arch.regs.arch_sp
        ql.mem.write(arg0_ptr, b"kaimendaji")

        ql.arch.regs.arch_sp -= 0x10
        arg1_ptr = ql.arch.regs.arch_sp
        ql.mem.write(arg1_ptr, b"000000")  # arbitrary password

        ql.arch.regs.arch_sp -= 0x20
        argv_ptr = ql.arch.regs.arch_sp
        ql.mem.write_ptr(argv_ptr, arg0_ptr)
        ql.mem.write_ptr(argv_ptr + ql.arch.pointersize, arg1_ptr)

        ql.arch.regs.r2 = 2
        ql.arch.regs.r3 = argv_ptr

    with open("../examples/rootfs/blob/u-boot.bin.img", "rb") as f:
        uboot_code = f.read()

    ql = Qiling(code=uboot_code[0x40:],
                archtype="arm",
                ostype="blob",
                profile="uboot_bin.ql",
                verbose=QL_VERBOSE.OFF)

    image_base_addr = ql.loader.load_address
    ql.hook_address(my_getenv, image_base_addr + 0x13AC0)
    ql.hook_address(get_password, image_base_addr + 0x48634)

    partial_run_init(ql)

    ql.run(image_base_addr + 0x486B4, image_base_addr + 0x48718)
コード例 #19
0
ファイル: test_mcu.py プロジェクト: profiles/qiling
    def test_mcu_led_rust_stm32f411(self):
        ql = Qiling(["../examples/rootfs/mcu/stm32f411/led-rust.hex"],
                    archtype="cortex_m", env=gd32vf103, profile="profiles/stm32f411.yml", verbose=QL_VERBOSE.DEFAULT)

        count = 0
        def counter():
            nonlocal count
            count += 1            

        ql.hw.create('gpioa').hook_set(5, counter)
        ql.hw.create('rcc')        

        ql.run(count=1000)
        self.assertTrue(count >= 5)

        del ql
コード例 #20
0
ファイル: test_mcu.py プロジェクト: profiles/qiling
    def test_mcu_dma_stm32f411(self):
        ql = Qiling(["../examples/rootfs/mcu/stm32f411/dma-clock.elf"],                    
            archtype="cortex_m", env=stm32f411, verbose=QL_VERBOSE.DEFAULT)

        ql.hw.create('usart2')
        ql.hw.create('dma1')
        ql.hw.create('rcc')

        ql.run(count=200000)
        buf = ql.hw.usart2.recv()

        ## check timestamp
        tick = [int(x) for x in buf.split()]
        for i in range(1, len(tick)):
            assert(4 <= tick[i] - tick[i - 1] <= 6)

        del ql
コード例 #21
0
ファイル: test_blob.py プロジェクト: xwings/qiling
    def test_uboot_arm(self):
        def my_getenv(ql, *args, **kwargs):
            env = {"ID": b"000000000000000", "ethaddr": b"11:22:33:44:55:66"}
            params = ql.os.resolve_fcall_params({'key': STRING})
            value = env.get(params["key"], b"")

            value_addr = ql.os.heap.alloc(len(value))
            ql.mem.write(value_addr, value)

            ql.reg.r0 = value_addr
            ql.reg.arch_pc = ql.reg.lr

        def check_password(ql, *args, **kwargs):
            passwd_output = ql.mem.read(ql.reg.r0, ql.reg.r2)
            passwd_input = ql.mem.read(ql.reg.r1, ql.reg.r2)
            self.assertEqual(passwd_output, passwd_input)

        def partial_run_init(ql):
            # argv prepare
            ql.reg.arch_sp -= 0x30
            arg0_ptr = ql.reg.arch_sp
            ql.mem.write(arg0_ptr, b"kaimendaji")

            ql.reg.arch_sp -= 0x10
            arg1_ptr = ql.reg.arch_sp
            ql.mem.write(arg1_ptr, b"013f1f")

            ql.reg.arch_sp -= 0x20
            argv_ptr = ql.reg.arch_sp
            ql.mem.write(argv_ptr, ql.pack(arg0_ptr))
            ql.mem.write(argv_ptr + ql.pointersize, ql.pack(arg1_ptr))

            ql.reg.r2 = 2
            ql.reg.r3 = argv_ptr

        print("ARM uboot bin")

        with open("../examples/rootfs/blob/u-boot.bin.img", "rb") as f:
            uboot_code = f.read()

        ql = Qiling(code=uboot_code[0x40:],
                    archtype="arm",
                    ostype="blob",
                    profile="profiles/uboot_bin.ql",
                    verbose=QL_VERBOSE.DEBUG)

        image_base_addr = ql.loader.load_address
        ql.hook_address(my_getenv, image_base_addr + 0x13AC0)
        ql.hook_address(check_password, image_base_addr + 0x48634)

        partial_run_init(ql)

        ql.run(image_base_addr + 0x486B4, image_base_addr + 0x48718)

        del ql
コード例 #22
0
def make(path, lcd):
    ql = Qiling([path],
        archtype="cortex_m", profile="stm32f411", verbose=QL_VERBOSE.DEFAULT)

    ql.hw.create('i2c1').connect(lcd)
    ql.hw.create('rcc')
    ql.hw.create('gpioa')
    ql.hw.create('gpiob')    
    
    return ql
コード例 #23
0
ファイル: test_mcu.py プロジェクト: profiles/qiling
    def test_mcu_freertos_stm32f411(self):
        ql = Qiling(["../examples/rootfs/mcu/stm32f411/os-demo.elf"],
            archtype="cortex_m", env=stm32f411, verbose=QL_VERBOSE.DEBUG)

        ql.hw.create('usart2')
        ql.hw.create('rcc')
        ql.hw.create('gpioa')

        count = 0
        def counter():
            nonlocal count
            count += 1

        ql.hw.gpioa.hook_set(5, counter)

        ql.run(count=200000)

        self.assertTrue(count >= 5)
        self.assertTrue(ql.hw.usart2.recv().startswith(b'Free RTOS\n' * 5))

        del ql
コード例 #24
0
ファイル: test_mcu.py プロジェクト: profiles/qiling
    def test_mcu_i2c_interrupt_stm32f411(self):
        ql = Qiling(['../examples/rootfs/mcu/stm32f411/i2cit-lcd.elf'], 
                archtype="cortex_m", env=stm32f411, verbose=QL_VERBOSE.DEFAULT)

        ql.hw.create('i2c1')
        ql.hw.create('rcc').watch()
        ql.hw.create('gpioa')
        ql.hw.create('gpiob') 

        class LCD:
            address = 0x3f << 1

            def send(self, data):
                pass

            def step(self):
                pass

        lcd = LCD()
        ql.hw.i2c1.connect(lcd)

        ql.hw.systick.set_ratio(100)

        delay_start = 0x8002936
        delay_end = 0x8002955
        def skip_delay(ql):
            ql.arch.regs.pc = delay_end

        ql.hook_address(skip_delay, delay_start)

        ql.run(count=100000)

        del ql
コード例 #25
0
    def test_mcu_tim_speed_stm32f411(self):
        ql = Qiling(['../examples/rootfs/mcu/stm32f411/basic-timer.elf'],
                    archtype="cortex_m",
                    env=stm32f411,
                    verbose=QL_VERBOSE.DEFAULT)

        ql.hw.create('rcc')
        ql.hw.create('flash interface')
        ql.hw.create('pwr')
        ql.hw.create('gpioa')
        ql.hw.create('usart2')
        ql.hw.create('tim1')

        ql.hw.tim1.set_ratio(1500)
        ql.run(count=2500)

        count = 0

        def counter():
            nonlocal count
            count += 1

        ql.hw.gpioa.hook_set(5, counter)
        ql.run(count=10000)
        count1 = count
        count = 0

        ql.hw.tim1.set_ratio(1400 * 2)
        ql.run(count=10000)
        count2 = count
        count = 0

        ql.hw.tim1.set_ratio(1600 // 2)
        ql.run(count=10000)
        count3 = count
        count = 0

        self.assertTrue(round(count2 / count1) == 2)
        self.assertTrue(round(count1 / count3) == 2)
        self.assertTrue(ql.hw.usart2.recv().startswith(b'hello\n'))
コード例 #26
0
    def test_mcu_blink_gd32vf103(self):
        ql = Qiling(['../examples/rootfs/mcu/gd32vf103/blink.hex'],
                    archtype="riscv64",
                    env=gd32vf103,
                    verbose=QL_VERBOSE.DEFAULT)

        ql.hw.create('rcu')
        ql.hw.create('gpioa')
        ql.hw.create('gpioc').watch()

        delay_cycles_begin = 0x800015c
        delay_cycles_end = 0x800018c

        def skip_delay(ql):
            ql.reg.pc = delay_cycles_end

        count = 0

        def counter():
            nonlocal count
            count += 1

        ql.hook_address(skip_delay, delay_cycles_begin)
        ql.hw.gpioc.hook_set(13, counter)
        ql.run(count=20000)
        self.assertTrue(count > 350)

        del ql
コード例 #27
0
def stm32f411_freertos():
    ql = Qiling(["../rootfs/mcu/stm32f411/os-demo.hex"],
                archtype="cortex_m",
                profile="stm32f411",
                verbose=QL_VERBOSE.DEBUG)

    ql.hw.create('usart2')
    ql.hw.create('rcc')
    ql.hw.create('gpioa')

    count = 0

    def counter():
        nonlocal count
        count += 1

    ql.hw.gpioa.hook_set(5, counter)

    ql.run(count=200000)

    print(count >= 5)
    print(ql.hw.usart2.recv())
コード例 #28
0
ファイル: stm32f411_i2c_lcd.py プロジェクト: xwings/qiling
def create(path, lcd):
    ql = Qiling([path], archtype="cortex_m", env=stm32f411, verbose=QL_VERBOSE.DEBUG)

    ql.hw.create('i2c1')
    ql.hw.create('rcc')
    ql.hw.create('gpioa')
    ql.hw.create('gpiob')   

    ql.hw.i2c1.watch()
    ql.hw.i2c1.connect(lcd)    
    
    ql.hw.systick.set_ratio(100)

    return ql
コード例 #29
0
ファイル: test_mcu.py プロジェクト: profiles/qiling
    def test_mcu_patch_stm32f411(self):
        ql = Qiling(["../examples/rootfs/mcu/stm32f411/patch_test.hex"],                    
                    archtype="cortex_m", env=stm32f411, verbose=QL_VERBOSE.DEFAULT)

        ql.hw.create('usart2')
        ql.hw.create('rcc')
        ql.hw.create('gpioa')

        ql.patch(0x80005CA, b'\x00\xBF')
        ql.run(count=4000)

        del ql
コード例 #30
0
ファイル: gd32vf103_blink.py プロジェクト: xwings/qiling
#!/usr/bin/env python3
# 
# Cross Platform and Multi Architecture Advanced Binary Emulation Framework
#

import sys
sys.path.append("../..")

from qiling.core import Qiling
from qiling.const import QL_VERBOSE
from qiling.extensions.mcu.gd32vf1 import gd32vf103

ql = Qiling(['../rootfs/mcu/gd32vf103/blink.hex'], archtype="riscv64", 
                    env=gd32vf103, verbose=QL_VERBOSE.DEBUG)

ql.hw.create('rcu')
ql.hw.create('gpioa').watch()
ql.hw.create('gpioc').watch()

delay_cycles_begin = 0x800015c
delay_cycles_end = 0x800018c

def skip_delay(ql):
    ql.reg.pc = delay_cycles_end

ql.hook_address(skip_delay, delay_cycles_begin)
ql.hw.gpioc.hook_set(13, lambda : print('Set PC13'))

ql.run(count=20000)