首页
社区
课程
招聘
[原创]clemency架构(DEF CON ARCHITECTURE)
发表于: 2021-12-6 23:36 10577

[原创]clemency架构(DEF CON ARCHITECTURE)

2021-12-6 23:36
10577

clemency架构(DEF CON ARCHITECTURE)

一:Introduce

cLEMENCy is the LEgitbs Middle ENdian Computer architecture developed
by Lightning for DEF CON CTF.
Each byte is 9 bits of data, bit 0 is the left most significant bit. MiddleEndian data stores bits 9 to 17, followed by bits 0 to 8, then bits 18 to 27 in
memory when handling three bytes. Two bytes of data will have bits 9-17 then
bits 0 to 8 written to memory.
Register XXYYZZ → Memory YYXXZZ
Register XXYY → Memory YYXX

 

https://blog.legitbs.net/2017/07/the-clemency-architecture.html

 

clemency是专门为defcon而设计的一种架构,每个字节是9bit,且一个寄存器是27bit,并且是MiddleEndian的

 

带有内置调试器和返汇编器的模拟器以及官方文档的下载链接https://2017.notmalware.ru/89dc90a0ffc5dd90ea68a7aece686544/clemency-201707280900.tar.bz2

二:反汇编

首先我们可以利用官方的模拟器来进行反汇编

 

上面链接文件下载好解压后可以得到clemency-emu的可执行文件

 

执行:

1
./clemency-emu -d 1 mercy.bin

然后输入u即可得到当前执行地址开始的几十条指令
图片描述

 

但是实际上一个文件肯定不只这些指令,通常我们进行逆向工作都是利用的IDA

 

而IDA目前自身是无法对其进行反汇编和架构识别的,这是一个自定义的架构

 

而在2017年就有人已经研究出了反汇编器(ida_clemency)

 

我们只需要将其安装到ida中即可

 

GitHub地址:https://github.com/cseagle/ida_clemency

 

只是它这个版本比较低,我为了反汇编在虚拟机中装了个python2.7.6和ida6.8

 

进入的时候选择一下
图片描述

 

之后便可以打开进行我们的分析了
图片描述

三:寄存器与栈分析

clemency有32个通用的27bit大小的寄存器,可用于浮点和整数数学运算以及一个单独的标志寄存器

 

多寄存器格式允许在数学运算期间通过并排使用两个寄存器来使用 54 位值,而起始寄存器可以是任何寄存器

 

图片描述

 

R0,通用寄存器,用来存放函数调用时的参数一或返回值的

 

R1-R8,通用寄存器,存放函数调用的参数二到参数八

 

R9-R28,通用寄存器,函数调用间保存

 

ST:Register Number是29,指向保存当前堆栈末尾的内存位置的指针

 

RA:Register Number是30,返回地址寄存器,由调用指令填充,执行return时使用

 

PC:Register Number是31,PC寄存器,寄存器只读

 

FL:flag寄存器
图片描述

 

关于stack,由于没有特定的基于堆栈的指令,堆栈没有预期的增长方向,但是在中断期间,处理器将从堆栈指针中减去 99 个字节的数据,将所有寄存器存储到堆栈中,并且中断返回将从中读取 99 个字节当前堆栈指针。如果实现需要将中断添加到堆栈而不是减去,则更改处理器功能区域中的“中断堆栈方向标志”位将完成此操作。

四:常用指令分析

1. LOAD INSTRUCTION

①LDT(LOAD TRI)

Load three bytes from memory,即从内存中加载三个字节(以中序加载的)

1
Format: LDTm rA, [rB + Offset, RegCount]

例:LDT R8, [R28+9]

 

从R28+9地址处开始以中序Load三个字节到R8中

②LDW(LOAD WORD)

Load two bytes from memory,从内存中加载两个字节

LDS(LOAD SINGLE)

Load a single byte from memory

 

从内存中加载单个字节

2. MOVE INSTRUCTION

MH(MOVE HIGH)

Move an immediate value to the high bits of a register

 

rA中27位值的高17bit设置为指定的立即数

ML(MOVE LOW)

Move a 17-bit immediate value to the register

 

rA设置为指定的17位立即数。高位清零

MS(MOVE LOW SIGNED)

Move a signed 17-bit immediate value to the register

 

rA设置为指定的有符号17位立即数。根据指定的立即数的有符号位设置高位

3. STORE INSTRUCTION

STS(STORE SINGLE)

Store a single byte into memory

1
Format: STSm rA, [rB + Offset, RegCount]

将寄存器的低9bit放入对应地址处

STT(STORE TRI)

Store three bytes into memory

1
Format: STTm rA, [rB + Offset, RegCount]

将rA寄存器的三个字节存放到对应地址处

4.CAR(CALL RELATIVE)

call指令,调用函数

 

说明:当前程序计数器+4存入RA,当前程序计数器加上27位偏移量

5.RE(RETURN)

To return from a call

 

等价于return指令

6. CALCULATION INSTRUCTION

AD(ADD)

Add two 27-bit integer registers together

1
Format: AD rA, rB, rC

rA = rB+rC

②.SB(SUBTRACT)

Subtract two 27-bit integer registers from each other

1
Format: SB rA, rB, rC

rA = rB-rC

OR

Bit-wise OR two 27-bit integer registers together

1
Format: OR rA, rB, rC

rA = rB | rC

④XR

Bit-wise eXclusive OR two 27-bit integer registers together

 

位级异或

1
Format: XR rA, rB, rC

rA = rB ^ rC

SL(SHIFT LEFT)

Shift left a 27-bit integer

1
Format: SL rA, rB, rC

rB 中的 27 位值左移了 rC 中指定的位数。结果放在 rA 中

 

rA = rB << rC

SR(SHIFT RIGHT)

Shift right a 27-bit integer

1
Format: SR rA, rB, rC

rB 中的 27 位值右移了 rC 中指定的位数。结果放在 rA 中

 

rA = rB >> rC

SRI

Shift right a 27-bit integer by a 7-bit immediate value

 

rA ← rB >> IMM

MU

Multiply two 27-bit integer registers together

1
Format: MU rA, rB, rC

rC 中的 27 位值与 rB 中的 27 位值相乘,结果放在 rA 中

MD(MODULUS)

1
Format: MD rA, rB, rC

Access the remainder of dividing two 27-bit integer registers

 

rA = rB % rC

五:调试

通常调试就采用它自带的clemency-emu调试器

 

启动:./clemency-emu -d 1 mercy.bin

 

与gdb调试类似

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
bp    Add a breakpoint
bd    Delete a breakpoint
bl    List breakpoints
bpc    Add a command to a breakpoint
dis    Set commands to run when debug pauses execution
db    Dump memory in bytes
dw    Dump memory in words
dt    Dump memory in tris
ds    Dump memory as ascii string
e    View or set exceptions to trigger on
er    Exception Reset
g    Go, continue execution
mp    Display or set memory protections
p    Run till next instruction
r    Print or set registers
t    Single step
u    Unassemble
wb    Write bytes
ww    Write words
wt    Write tris
q    Exit
?    Print info about a command

示例:

1
2
3
4
5
6
7
8
9
bp BF60     # 在0xBF60处添加一个断点
bd BF60     # 删除0xBF60处的断点
bl          # 列出断点列表
db BE32     # 以byte的形式dump地址0xBE32开始的内存
dw addr     # 以word的形式dump内存
dt addr     # 以三字节一组的形式dunp内存
g           # run指令
t           # 单步执行
u           # 反汇编指令

六:实际案例-2021hitcon-mercy

利用IDA反汇编后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
.text:00000000                 public _start
.text:00000000 _start:
.text:00000000                 LDT    R1-R3, [R0+0x57]
.text:00000006                 SMP    R0, R1, RX
.text:00000009                 AD     R0, R0, R1
.text:0000000C                 ML     R4, 0x400
.text:0000000F                 MU     R5, R0, R4
.text:00000012                 SMP    R5, R2, RW
.text:00000015                 AD     R0, R0, R2
.text:00000018                 MU     R5, R0, R4
.text:0000001B                 SMP    R5, R3, RW
.text:0000001E                 AD     R0, R0, R3
.text:00000021                 ADI    R0, R0, 1
.text:00000024                 ML     R2, 0xFFDE
.text:00000027                 SB     R2, R2, R0
.text:0000002A                 MU     R5, R0, R4
.text:0000002D                 SMP    R5, R2, RW
.text:00000030                 MI     R0, 0x3FF7C00
.text:00000036                 ML     R1, 0x20
.text:00000039                 SMP    R0, R1, RW
.text:0000003C                 MU     R1, R1, R4
.text:0000003F                 AD     ST, R0, R1
.text:00000042                 ML     R0, 0x1FF
.text:00000045                 EI     R0
.text:00000047                 OR     R0, R5, R5
.text:0000004A                 MU     R1, R2, R4
.text:0000004D                 CAR    sub_901
.text:00000051                 CAR    sub_61C4
.text:00000055                 HT
.text:00000055 ; End of function _start

start函数的话,关键是调用的sub_901和sub_61C4函数

 

CAR等价于CALL指令

 

而sub_901函数没有关键逻辑

 

关键逻辑在sub_61C4函数中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.text:000061C4 sub_61C4:                               ; CODE XREF: _start+51p
.text:000061C4                 STTD   R28-RA, [ST]
.text:000061CA                 OR.    R28, ST, ST
.text:000061CD                 MI     R0, FlagIO
.text:000061D3                 CAR    sub_5EBF
.text:000061D7                 AN.    R0, R0, R0
.text:000061DA                 BE     loc_61F0
.text:000061DD                 MI     R1, FlagIO
.text:000061E3                 MI     R0, unk_6B3C     ; "Nice job: %s"
.text:000061E9                 CAR    sub_4D0F
.text:000061ED                 B      loc_61FA
.text:000061F0 ; ---------------------------------------------------------------------------
.text:000061F0
.text:000061F0 loc_61F0:                               ; CODE XREF: sub_61C4+16j
.text:000061F0                 MI     R0, byte_6B49
.text:000061F6                 CAR    sub_4D0F
.text:000061FA
.text:000061FA loc_61FA:                               ; CODE XREF: sub_61C4+29j
.text:000061FA                 ML     R0, 0
.text:000061FD                 LDT    R28-RA, [R28]
.text:00006203                 RE

MI指令:将我们从flag文件读取的字符串存放的首地址传递给R0寄存器

 

然后调用sub_5EBF函数

 

之后AN判断返回值来执行不同的逻辑

 

返回值是1就"Nice job: %s", flag

 

进入sub_5EBF函数分析:

 

首先是一个函数的环境准备

1
2
3
4
5
6
.text:00005EBF sub_5EBF:                               ; CODE XREF: sub_61C4+Fp
.text:00005EBF                 STTD   R28-RA, [ST]
.text:00005EC5                 OR.    R28, ST, ST
.text:00005EC8                 SBI.   ST, ST, 0x39
.text:00005ECB                 STTD   R8-R14, [ST]
.text:00005ED1                 ML     R1, 0

创建了栈帧

 

然后一个循环初始化0x806C开始的内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.text:00005ED4 loc_5ED4:                               ; CODE XREF: sub_5EBF+24j
.text:00005ED4                 CMI    R1, 0x200
.text:00005ED7                 BSGE   loc_5EF8
.text:00005EDA                 B      loc_5EE6
.text:00005EDD ; ---------------------------------------------------------------------------
.text:00005EDD
.text:00005EDD loc_5EDD:                               ; CODE XREF: sub_5EBF+36j
.text:00005EDD                 ADI.   R8, R1, 1
.text:00005EE0                 OR.    R1, R8, R8
.text:00005EE3                 B      loc_5ED4
.text:00005EE6 ; ---------------------------------------------------------------------------
.text:00005EE6
.text:00005EE6 loc_5EE6:                               ; CODE XREF: sub_5EBF+1Bj
.text:00005EE6                 MI     R8, 0x806C
.text:00005EEC                 AD.    R9, R8, R1
.text:00005EEF                 STS    R1, [R9]
.text:00005EF5                 B      loc_5EDD

然后又是一个循环,关键部分
图片描述

 

依旧是指进行系列运算后存放到0x806c中(同样是在初始化)

 

这个fuck是个数组:(数组大小为9)
图片描述

 

再往下走,循环27次,到处理flag的地方了

 

图片描述

 

关键代码loc_5FE5地址处开始

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
.text:00005FBB loc_5FBB:                               ; CODE XREF: sub_5EBF+4Bj
.text:00005FBB                 ML     R8, 0
.text:00005FBE                 STS    R8, [R28-6]      ; r[28-6] = 0
.text:00005FC4                 ML     R8, 0
.text:00005FC7                 STS    R8, [R28-36]     ; [r28-36] = 0
.text:00005FCD                 ML     R8, 0            ; r8=0
.text:00005FD0                 OR.    R1, R8, R8       ; r1=0
.text:00005FD3
.text:00005FD3 loc_5FD3:                               ; CODE XREF: sub_5EBF+123j
.text:00005FD3                 CMI    R1, 27
.text:00005FD6                 BSGE   loc_60E4         ; signed 大于等于才跳转
.text:00005FD9                 B      loc_5FE5
.text:00005FDC ; ---------------------------------------------------------------------------
.text:00005FDC
.text:00005FDC loc_5FDC:                               ; CODE XREF: sub_5EBF+222j
.text:00005FDC                 ADI.   R8, R1, 1
.text:00005FDF                 OR.    R1, R8, R8
.text:00005FE2                 B      loc_5FD3
.text:00005FE5 ; ---------------------------------------------------------------------------
.text:00005FE5
.text:00005FE5 loc_5FE5:                               ; CODE XREF: sub_5EBF+11Aj
.text:00005FE5                 MI     R8, 0x806C
.text:00005FEB                 AD.    R9, R8, R1
.text:00005FEE                 LDS    R8, [R9]         ; r8=*(0x806c+i)
.text:00005FF4                 LDS    R9, [R28-6]
.text:00005FFA                 AD.    R8, R9, R8
.text:00005FFD                 STS    R8, [R28-6]      ; [r28-6]= [R28-6]+r8
.text:00006003                 MI     R8, 0x806C
.text:00006009                 AD.    R9, R8, R1       ; r9 = 0x806c+i
.text:0000600C                 LDS    R8, [R28-6]
.text:00006012                 MI     R10, 0x806C
.text:00006018                 AD.    R11, R10, R8     ; r11 = 0x806c+[r28-6]
.text:0000601B                 MI     R8, 0x806C
.text:00006021                 AD.    R10, R8, R1      ; r10 = 0x806c+i
.text:00006024                 LDS    R8, [R28-6]
.text:0000602A                 MI     R12, 0x806C
.text:00006030                 AD.    R13, R12, R8     ; r13 = 0x806c+[r28-6]
.text:00006033                 LDS    R8, [R13]        ; r8 = [r13]
.text:00006039                 LDS    R12, [R10]       ; r12 = [r10]
.text:0000603F                 XR.    R13, R12, R8     ; r13 = r12 ^ r8
.text:00006042                 STS    R13, [R10]       ; [r10] = r13
.text:00006048                 LDS    R8, [R11]        ; r8 = r[11]
.text:0000604E                 XR.    R10, R8, R13     ; r10 = r8 ^ r13
.text:00006051                 STS    R10, [R11]       ; [r11] = r10
.text:00006057                 LDS    R8, [R9]         ; r8=[r9]
.text:0000605D                 XR.    R11, R8, R10     ; r11 = r8 ^ r10
.text:00006060                 STS    R11, [R9]        ; [r9] = r11
.text:00006066                 MI     R8, 0x806C
.text:0000606C                 AD.    R9, R8, R1       ; r9=0x806c+i
.text:0000606F                 LDS    R8, [R28-6]
.text:00006075                 MI     R10, 0x806C
.text:0000607B                 AD.    R11, R10, R8     ; r11=0x806c+[r28-6]
.text:0000607E                 LDS    R8, [R11]        ; r8=[r11]
.text:00006084                 LDS    R10, [R9]        ; r10=[r9]
.text:0000608A                 AD.    R8, R10, R8      ; r8=r10+r8
.text:0000608D                 STS    R8, [R28-39]     ; [R28-39]=r8
.text:00006093                 MS     R27, -33         ; 将一个带符号的 17 位立即数移到寄存器中
.text:00006096                 AD.    R8, R28, R27     ; r8=r28-33 加密后字符串存放地址
.text:00006099                 AD.    R9, R8, R1       ; r9=r8+i
.text:0000609C                 AD.    R8, R0, R1       ; R0是输入字符串地址, r8=r0+i
.text:0000609F                 LDS    R10, [R28-39]
.text:000060A5                 MI     R11, 0x806C
.text:000060AB                 AD.    R12, R11, R10    ; r12=0x806c+[r28-39]
.text:000060AE                 LDS    R10, [R12]
.text:000060B4                 LDS    R11, [R8]        ; r11=[r8]    输入的字符串第i个byte
.text:000060BA                 XR.    R8, R11, R10     ; r8=r11^r10
.text:000060BD                 LDS    R10, [R28-36]
.text:000060C3                 AD.    R11, R8, R10     ; r11=r8+[r28-36]
.text:000060C6                 STS    R11, [R9]        ; [r9]=r11
.text:000060CC                 MS     R27, -33
.text:000060CF                 AD.    R8, R28, R27
.text:000060D2                 AD.    R9, R8, R1       ; r9=r28-33 + i
.text:000060D5                 LDS    R9, [R9]         ; r9=[r9]
.text:000060DB                 STS    R9, [R28-36]     ; [r28-36]=r9
.text:000060E1                 B      loc_5FDC

最后就是一些比较,每次加载三个字节
图片描述

 

通过分析,得知加密后的flag存放内存为:

 

[r28-33] 加密后第一个字节

 

30

 

27

 

24

 

21

 

18

 

[r28-15]

 

[r28-12] 0x3FFFBE2

 

[r28-11] 0x3FFFBE3

 

[r28-10] 0x3FFFBE3

 

[r28-9] 0x3FFFBE5

 

[r28-8] 0x3FFFBE6

 

[r28-7] 0x3FFFBE7 加密后最后一个字节

 

总共27字节

 

基本还原出逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <stdio.h>
#include <stdlib.h>
 
typedef unsigned short int type;
 
int main()
{
    type* mem = (type*)malloc(sizeof(type) * 0x200);
    type inp[27] = {0};
    type dst[27] = {0};
    type fuck[9] = {0x12B, 0x62, 0xBC, 0x9C, 0x3B, 0x34, 0x111, 0x89, 0x144};
    for(int i=0; i<0x200; i++)
    {
        mem[i] = i;
    }
 
    type data = 0;
    type r8, r9, r10, r11, r12, r13;
    for(int j=0; j<0x200; j++)
    {
        r10 = mem[j] + data;
        r8 = fuck[j % 9];
        r9 = r10 + r8;
        r10 = r9 << 15;
        r10 = r10 >> 15;
        data = r10;
        /*
        r9  = &mem[j];
        r11 = &mem[data];
        r10 = &mem[j];
        r13 = &mem[data];
        */
        mem[j] = mem[j] ^ mem[data];
        mem[data] = mem[data] ^ mem[j];
        mem[j] = mem[j] ^ mem[data];
    }
 
    data = 0;
    type data1 = 0;    //[r28-36] = 0
    for(int k=0; k<27; k++)
    {
        r8 = mem[i];
        data = data + r8;
        mem[k] = mem[k] ^ mem[data];
        mem[data] = mem[data] ^ mem[k];
        mem[k] = mem[k] ^ mem[data];
        r8 = mem[k] + mem[data];
        dst[k] = (inp[k] ^ mem[r8]) + data1;
        data1 = dst[k];
    }
    return 0;
}

魔改的RC4,CBC模式

 

在60BA地址处下断点,调试起来并获取R10的值,就可以得到xor_table

 

python实现逻辑并解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
xor_tbl = [0x17f, 0x183, 0x193, 0x60, 0x4a, 0x1f6, 0xbc, 0xe, 0x103, 0x12f, 0x1d3, 0x1e1, 0xa3,
           0x130, 0x15a, 0x175, 0x7, 0x162, 0x159, 0x129, 0x93, 0x1be, 0xcc, 0x16b, 0x2, 0x22, 0x27]
 
orig_input = [0x0c46032,
              0x0d06635,
              0x0dc6c38,
              0x1847262,
              0x190c665,
              0x19ccc68,
              0x1a8d26b,
              0x1b4d86e,
              0x1c0de71]
 
encrypted = [0x4069ea2,
             0x5cdeb36,
             0x7e78134,
             0x7f0957f,
             0x64c7ed2,
             0x117cb4f,
             0x708feba,
             0x4b51832,
             0x3c53f47]
 
'''
idx value
-21 0x4062EE8
-1e 0x441D6A8
-1b 0x69EDF0E
-18 0x7885B66
-15 0x40FF43F
-12 0x6F30D11
-f 0x624E22D
-c 0x183716F
-9 0xC7A45E
'''
dst = [0x4062EE8, 0x441D6A8, 0x69EDF0E, 0x7885B66,
       0x40FF43F, 0x6F30D11, 0x624E22D, 0x183716F, 0xC7A45E]
 
def enc(m):
    result = []
    m = to_9bits(m)
    prev = 0
    for i in range(len(m)):
        cur = ((m[i] ^ xor_tbl[i]) + prev) & 0b111111111
        result.append(cur)
        prev = cur
    return from_9bits(result)
 
def dec(m):
    result = []
    m = to_9bits(m)
    for i in range(len(m)):
        if i:
            cur = ((m[i] - m[i-1]) & 0b111111111) ^ xor_tbl[i]
        else:
            cur = ((m[i] ^ xor_tbl[i])) & 0b111111111
        result.append(cur)
 
    return from_9bits(result)
 
def from_9bits(src):
    dst = []
    assert len(src) % 3 == 0
    for i in range(len(src)//3):
        dst.append((src[3*i] << 9) + (src[3*i+1] << 18) + (src[3*i+2]))
    return dst
 
def to_9bits(src):
    dst = []
    for each in src:
        dst.append((each >> 9) & 0b111111111)
        dst.append(each >> 18)
        dst.append(each & 0b111111111)
    return dst
 
assert from_9bits(to_9bits(dst)) == dst
assert enc(orig_input) == encrypted
assert dec(enc(orig_input)) == orig_input
 
print(bytearray(to_9bits(dec(dst))).decode())

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2021-12-6 23:46 被SYJ-Re编辑 ,原因:
上传的附件:
收藏
免费 1
支持
分享
最新回复 (2)
雪    币: 8745
活跃值: (5673)
能力值: ( LV13,RANK:296 )
在线值:
发帖
回帖
粉丝
2
2021-12-6 23:41
1
雪    币: 21
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
2021-12-6 23:44
1
游客
登录 | 注册 方可回帖
返回
//