graf

6. 복호화 로직 분석 본문

RSW-725R/분석 자료 (공개용)

6. 복호화 로직 분석

graf 2026. 4. 22. 10:17

 

삽질을 좀 하긴 했지만 세그먼트를 모두 확보하는데 성공했다. 

이번 분석 목표는 추출한 세그먼트에서 인증서와 개인키의 디코딩 혹은 복호화 루틴의 존재 유무를 찾는거다. 

 

리버싱은 깊게 공부해본 적이 없긴 한데,, 

그래도 한번 시도나 해보자. 

 

 

1. 문자열 참조 위치 찾기

대부분의 문자열 리터럴들은 세그먼트 1과 5에 몰려있다. 

 

private key loaded는 세그먼트 5번에서 나왔다. 

 

오프셋은 0x3ea
로드되는 주소를 고려하면 0x3ffe8570 + 0x3ea = 0x3ffe895a

이 데이터를 참조하는 곳이 있다면 그곳에 개인키를 로드하는 동작이 있다는 말이다. 

 

 

세그먼트가 로드되는 주소를 기준으로 문자열 주소를 계산하고

이 값을 참조하는 명령어들을 찾아봤지만 나오는게 하나도 없었다. 

 

 

strings -t x segment5.bin > strliteral
strliteral_path = "strliteral"
disasm_path = "disasm.txt"


with open(strliteral_path, 'r', encoding='utf-8') as f1:
    str_lines = f1.readlines()

datas = []
for str_line in str_lines:
    offset = str_line.strip().split()[0]
    datas.append(hex(int(offset, 16) + 0x3ffe8570))

# print(datas)

with open(disasm_path, 'r', encoding='utf-8') as f2:
    disasm_lines = f2.readlines()

for line in disasm_lines:
    for data in datas:
        if data in line:
            print(hex(int(data, 16) - 0x3ffe8570))
            print(line)
3fd /ca.der
456 null
715 opt1
761 ssid :
78b version
bcf e%ld
a84 .local
928  0x%.2x
970 2.6.3
978 %s %u
bb4 esp8266

 

전부 비교를 해봤는데 나오는 문자열이 이것 뿐이다. 

 

내가 오프셋을 잘못 맞췄거나, 

배포 전에 디버그 메시지를 전부 지워버렸거나 

둘 중 하나일 거다. 

 

 

이건 세그먼트1에서 나왔다. 
이거로 시도해보자. 

 

 

0x40201010 + 0x631d9 = 0x402641e9

 

안 나온다... 

 

키 포멧을 맞추려면 적어도 이건 있어야지 않나..? 

내가 주소를 잘못 계산하고있는건가? 
오프셋을 잘못 잡았나? 

 

 

 17746 4020c0f3:   fffd41          l32r    a4, 0x4020c0e8 (0x403fb000)
 23821 4020fd4d:   fff321          l32r    a2, 0x4020fd1c (0x40300000)
 23826 4020fd5a:   fff131          l32r    a3, 0x4020fd20 (0x403fa000)

 

세그먼트1에서만 0x403이 등장한다. 
그마저도 해석 이상한걸 빼면 이 세개가 전부다. 

 

 

생각을 해봤는데 내가 파싱을 잘못한 것 같다. 

 

 

이게 image2의 세그먼트 추출 결과였다. 

 

이 파일에는 분명히 이미지 헤더가 포함된 상태다. 

 

그런데 데이터를 hexdump에서 확인한 결과 분명히 저 len에 헤더 크기는 포함돼있지 않았다. 

저 추출 결과에서 offset는 세그먼트 헤더의 시작 주소를 말하고 있지만 
실제 데이터의 시작 주소는 8바이트 이후가 된다는 말이다. 

 

이게 맞다면 계산했던 오프셋이 전부 틀렸을거다. 
내가 바보같이 툴을 너무 맹신했다. 

 

 

1.1 오프셋 보정 후 재시도

dd if=image2.bin of=segment1.bin bs=1 skip=$((0x00000008+0x08)) count=$((0x0068818))

dd if=image2.bin of=segment2.bin bs=1 skip=$((0x00068828+0x08)) count=$((0x00000e8))

dd if=image2.bin of=segment3.bin bs=1 skip=$((0x00068918+0x08)) count=$((0x006d08))

dd if=image2.bin of=segment4.bin bs=1 skip=$((0x0006f628+0x08)) count=$((0x0000568))

dd if=image2.bin of=segment5.bin bs=1 skip=$((0x0006fb98+0x08)) count=$((0x0000c3c))

 

세그먼트 추출

 

strliteral_path = "strliteral"
disasm_path = "disasm1.txt"


with open(strliteral_path, 'r', encoding='utf-8') as f1:
    str_lines = f1.readlines()

datas = []
for str_line in str_lines:
    offset = str_line.strip().split()[0]
    datas.append(hex(int(offset, 16) + 0x3ffe8570 - 0x08))

# print(datas)

with open(disasm_path, 'r', encoding='utf-8') as f2:
    disasm_lines = f2.readlines()

for line in disasm_lines:
    for data in datas:
        if data in line:
            print(hex(int(data, 16) - 0x3ffe8570 + 0x08))
            print(line)

 

이전에 시도했던 방법인데 
추출을 8바이트 앞에서 했으니 오프셋들이 전부 8씩 증가한걸 고려해서 주소를 보정했다. 

 

 

이번엔 결과가 보기 힘들 정도로 많이 나온다. 
웬만한 문자열들은 다 참조가 되고 있다는 말이다. 

드디어 희망이 생겼다. 

 

 

이 중에 3ea나 405가 나왔던 곳은 

 

 

0x3ea
402020bb:       ffc331          l32r    a3, 0x40201fc8 (0x3ffe8952)

0x405
402020d9:       ffbd31          l32r    a3, 0x40201fd0 (0x3ffe896d)

 

여기다. 
드디어 나왔다. 

둘의 주소가 인접한게 우연이 아닐 거다. 
저 주소에 개인키를 복구하는 로직이 분명히 있다. 

 

 

2. 개인키 로드 함수 분석

2.1 메인 동작 분석

402020b0:   28c132          addi    a3, a1, 40
402020b3:   202ee0          or  a2, a14, a14
402020b6:   074845          call0   0x4020953c
402020b9:   c29c        beqz.n  a2, 0x402020d9
402020bb:   ffc331          l32r    a3, 0x40201fc8 (0x3ffe8952)
402020be:   0c2d        mov.n   a2, a12
402020c0:   0b2e85          call0   0x4020d3ac
402020c3:   ffc241          l32r    a4, 0x40201fcc (0x3ffe8965)
402020c6:   012d        mov.n   a2, a1
402020c8:   0d3d        mov.n   a3, a13
402020ca:   0f5d        mov.n   a5, a15
402020cc:   0ad845          call0   0x4020ce54
402020cf:   012d        mov.n   a2, a1
402020d1:   15d7c5          call0   0x40217e50
402020d4:   92dc        bnez.n  a2, 0x402020f1
402020d6:   0003c6          j   0x402020e9
402020d9:   ffbd31          l32r    a3, 0x40201fd0 (0x3ffe896d)
402020dc:   0c2d        mov.n   a2, a12
402020de:   0b2cc5          call0   0x4020d3ac
402020e1:   1c0c        movi.n  a12, 1
402020e3:   0012c6          j   0x40202132
402020e6:   000000          ill

 

확인된 두 참조 명령어가 모두 포함되도록 디스어셈블 코드를 가져왔다. 

 

0x3ffe8952와 0x3ffe896d 모두 a3에 로드 후 0x4020d3ac를 호출한다. 
0x4020d3ac는 거의 확실히 print 함수다. 

 

문자열 주소가 두번째 인자인게 조금 마음에 걸리긴 하는데 
출력 함수도 종류가 워낙 다양하니 말이 안되는 일은 아니다. 

이제 위에부터 구체적으로 쪼개서 분석해보자. 

 

 

40202073:   28c122          addi    a2, a1, 40
40202076:   15dd85          call0   0x40217e50
40202079:   c2dc        bnez.n  a2, 0x40202099

 

여기서 리턴이 0이 아니라면 0x40202099로 분기한다. 

 

40202099:   ffca31          l32r    a3, 0x40201fc4 (0x3ffe8930)
4020209c:   202cc0          or  a2, a12, a12
4020209f:   0b30c5          call0   0x4020d3ac

 

0x3ffe8930은 오프셋으로는 0x3ffe8930 - 0x3ffe8570 = 0x3c0

 

 

이 문자열이다. 

40202099 이후의 동작들은 인증서가 올라온 이후에 이루어진다. 

 

 

402020a2:   64a022          movi    a2, 100
402020a5:   0cadc5          call0   0x4020eb84
402020a8:   28c122          addi    a2, a1, 40
402020ab:   15d885          call0   0x40217e34
402020ae:   024d        mov.n   a4, a2
402020b0:   28c132          addi    a3, a1, 40
402020b3:   202ee0          or  a2, a14, a14
402020b6:   074845          call0   0x4020953c
402020b9:   c29c        beqz.n  a2, 0x402020d9

 

이후엔 함수 호출을 반복한다. 

세번의 호출이 끝나고 마지막 함수의 리턴값이 0이면 0x402020d9로 분기한다. 

 

402020d9:   ffbd31          l32r    a3, 0x40201fd0 (0x3ffe896d)
402020dc:   0c2d        mov.n   a2, a12
402020de:   0b2cc5          call0   0x4020d3ac

 

0x3ffe896d는 private key not loaded 문자열이다. 
이곳은 로드가 제대로 이루어지지 않았을 때 실행되는 블록이다. 

나머지 블록을 확인해보자. 

 

402020bb:   ffc331          l32r    a3, 0x40201fc8 (0x3ffe8952)
402020be:   0c2d        mov.n   a2, a12
402020c0:   0b2e85          call0   0x4020d3ac
402020c3:   ffc241          l32r    a4, 0x40201fcc (0x3ffe8965)
402020c6:   012d        mov.n   a2, a1
402020c8:   0d3d        mov.n   a3, a13
402020ca:   0f5d        mov.n   a5, a15
402020cc:   0ad845          call0   0x4020ce54
402020cf:   012d        mov.n   a2, a1
402020d1:   15d7c5          call0   0x40217e50
402020d4:   92dc        bnez.n  a2, 0x402020f1
402020d6:   0003c6          j   0x402020e9

 

이곳은 리턴이 0이 아닐때다. 
0x3ffe8952는 private key loaded 문자열이다. 

여기는 키가 정상적으로 로드됐을 때 실행되는 부분인가보다. 

bnez.n a2, 0x402020f1 코드는 에러 예외처리로 보인다. 
단서가 될지 모르니 무슨 에러인지 확인해보자. 

 

402020f1:   ffb931          l32r    a3, 0x40201fd8 (0x3ffe8997)
402020f4:   202cc0          or  a2, a12, a12
402020f7:   0b2b45          call0   0x4020d3ac

 

0x3ffe8997은 Success to open ca로 확인된다. 

뭐지? 그럼 분기하지 않았을 때는 뭐가 나오는거지? 

 

402020d4:   92dc        bnez.n  a2, 0x402020f1
402020d6:   0003c6          j   0x402020e9

 

bnez에서 분기를 하지 않았다면 0x402020e9로 넘어간다. 

 

402020e9:   0c2d        mov.n   a2, a12
402020eb:   ffba31          l32r    a3, 0x40201fd4 (0x3ffe8984)
402020ee:   000d06          j   0x40202126
40202126:   0b2845          call0   0x4020d3ac

 

0x3ffe8984는 Failed to open ca
여기가 예외처리였다. 

Success 부분부터 다시 이어가보자. 

 

402020f1:   ffb931          l32r    a3, 0x40201fd8 (0x3ffe8997)
402020f4:   202cc0          or  a2, a12, a12
402020f7:   0b2b45          call0   0x4020d3ac
402020fa:   64a022          movi    a2, 100
402020fd:   0ca845          call0   0x4020eb84
40202100:   202110          or  a2, a1, a1
40202103:   15d305          call0   0x40217e34
40202106:   204220          or  a4, a2, a2
40202109:   013d        mov.n   a3, a1
4020210b:   0e2d        mov.n   a2, a14
4020210d:   071805          call0   0x40209290
40202110:   d28c        beqz.n  a2, 0x40202121

 

Success to open ca 출력 후 함수를 계속 호출한다. 

이후엔 또 분기가 갈린다. 

 

40202112:   ffb231          l32r    a3, 0x40201fdc (0x3ffe89aa)
40202115:   0c2d        mov.n   a2, a12
40202117:   0b2945          call0   0x4020d3ac
4020211a:   0c0c        movi.n  a12, 0
4020211c:   000306          j   0x4020212c

 

분기하지 않았을 때다. 
0x3ffe89aa는 ca loaded다. 

 

4020212c:   202110          or  a2, a1, a1
4020212f:   ffe445          call0   0x40201f74
40202132:   28c122          addi    a2, a1, 40
40202135:   ffe3c5          call0   0x40201f74
40202138:   50c122          addi    a2, a1, 80
4020213b:   ffe385          call0   0x40201f74
4020213e:   272102          l32i    a0, a1, 156
40202141:   a0a092          movi    a9, 160
40202144:   0c2d        mov.n   a2, a12
40202146:   2521d2          l32i    a13, a1, 148
40202149:   2621c2          l32i    a12, a1, 152
4020214c:   2421e2          l32i    a14, a1, 144
4020214f:   2321f2          l32i    a15, a1, 140
40202152:   119a        add.n   a1, a1, a9
40202154:   f00d        ret.n

 

이후 함수 몇개를 더 호출하고 리턴한다. 

 

 

더보기
40201fe4:   a0a092          movi    a9, 160
40201fe7:   c01190          sub a1, a1, a9
40201fea:   2561d2          s32i    a13, a1, 148
40201fed:   ffebd1          l32r    a13, 0x40201f9c (0x3ffef69c)
40201ff0:   2661c2          s32i    a12, a1, 152
40201ff3:   202dd0          or  a2, a13, a13
40201ff6:   276102          s32i    a0, a1, 156
40201ff9:   2461e2          s32i    a14, a1, 144
40201ffc:   2361f2          s32i    a15, a1, 140
40201fff:   15e745          call0   0x40217e74
40202002:   fd70c1          l32r    a12, 0x402015c4 (0x3ffef4c4)
40202005:   00c256          bnez    a2, 0x40202015
40202008:   ffe631          l32r    a3, 0x40201fa0 (0x3ffe888d)
4020200b:   0c2d        mov.n   a2, a12
4020200d:   0b39c5          call0   0x4020d3ac
40202010:   1c0c        movi.n  a12, 1
40202012:   004a06          j   0x4020213e
40202015:   ffe4f1          l32r    a15, 0x40201fa8 (0x3ffe896b)
40202018:   ffe341          l32r    a4, 0x40201fa4 (0x3ffe88a9)
4020201b:   50c122          addi    a2, a1, 80
4020201e:   203dd0          or  a3, a13, a13
40202021:   205ff0          or  a5, a15, a15
40202024:   0ae2c5          call0   0x4020ce54
40202027:   50c122          addi    a2, a1, 80
4020202a:   15e245          call0   0x40217e50
4020202d:   82cc        bnez.n  a2, 0x40202039
4020202f:   0c2d        mov.n   a2, a12
40202031:   ffde31          l32r    a3, 0x40201fac (0x3ffe88b3)
40202034:   001386          j   0x40202086
40202037:   310000          srai    a0, a0, 16
4020203a:   ffdd        excw
4020203c:   202cc0          or  a2, a12, a12
4020203f:   0b36c5          call0   0x4020d3ac
40202042:   64a022          movi    a2, 100
40202045:   0cb3c5          call0   0x4020eb84
40202048:   50c122          addi    a2, a1, 80
4020204b:   15de85          call0   0x40217e34
4020204e:   fe61e1          l32r    a14, 0x402019d4 (0x3ffef11c)
40202051:   024d        mov.n   a4, a2
40202053:   50c132          addi    a3, a1, 80
40202056:   0e2d        mov.n   a2, a14
40202058:   075c45          call0   0x40209620
4020205b:   22ac        beqz.n  a2, 0x40202081
4020205d:   ffd531          l32r    a3, 0x40201fb4 (0x3ffe88e6)
40202060:   202cc0          or  a2, a12, a12
40202063:   0b3485          call0   0x4020d3ac
40202066:   ffd441          l32r    a4, 0x40201fb8 (0x3ffe88f2)
40202069:   28c122          addi    a2, a1, 40
4020206c:   0d3d        mov.n   a3, a13
4020206e:   0f5d        mov.n   a5, a15
40202070:   0ade05          call0   0x4020ce54
40202073:   28c122          addi    a2, a1, 40
40202076:   15dd85          call0   0x40217e50
40202079:   c2dc        bnez.n  a2, 0x40202099

40202099:   ffca31          l32r    a3, 0x40201fc4 (0x3ffe8930)
4020209c:   202cc0          or  a2, a12, a12
4020209f:   0b30c5          call0   0x4020d3ac
402020a2:   64a022          movi    a2, 100
402020a5:   0cadc5          call0   0x4020eb84
402020a8:   28c122          addi    a2, a1, 40
402020ab:   15d885          call0   0x40217e34
402020ae:   024d        mov.n   a4, a2
402020b0:   28c132          addi    a3, a1, 40
402020b3:   202ee0          or  a2, a14, a14
402020b6:   074845          call0   0x4020953c
402020b9:   c29c        beqz.n  a2, 0x402020d9
402020bb:   ffc331          l32r    a3, 0x40201fc8 (0x3ffe8952)
402020be:   0c2d        mov.n   a2, a12
402020c0:   0b2e85          call0   0x4020d3ac
402020c3:   ffc241          l32r    a4, 0x40201fcc (0x3ffe8965)
402020c6:   012d        mov.n   a2, a1
402020c8:   0d3d        mov.n   a3, a13
402020ca:   0f5d        mov.n   a5, a15
402020cc:   0ad845          call0   0x4020ce54
402020cf:   012d        mov.n   a2, a1
402020d1:   15d7c5          call0   0x40217e50
402020d4:   92dc        bnez.n  a2, 0x402020f1

402020f1:   ffb931          l32r    a3, 0x40201fd8 (0x3ffe8997)
402020f4:   202cc0          or  a2, a12, a12
402020f7:   0b2b45          call0   0x4020d3ac
402020fa:   64a022          movi    a2, 100
402020fd:   0ca845          call0   0x4020eb84
40202100:   202110          or  a2, a1, a1
40202103:   15d305          call0   0x40217e34
40202106:   204220          or  a4, a2, a2
40202109:   013d        mov.n   a3, a1
4020210b:   0e2d        mov.n   a2, a14
4020210d:   071805          call0   0x40209290
40202110:   d28c        beqz.n  a2, 0x40202121
40202112:   ffb231          l32r    a3, 0x40201fdc (0x3ffe89aa)
40202115:   0c2d        mov.n   a2, a12
40202117:   0b2945          call0   0x4020d3ac
4020211a:   0c0c        movi.n  a12, 0
4020211c:   000306          j   0x4020212c

4020212c:   202110          or  a2, a1, a1
4020212f:   ffe445          call0   0x40201f74
40202132:   28c122          addi    a2, a1, 40
40202135:   ffe3c5          call0   0x40201f74
40202138:   50c122          addi    a2, a1, 80
4020213b:   ffe385          call0   0x40201f74
4020213e:   272102          l32i    a0, a1, 156
40202141:   a0a092          movi    a9, 160
40202144:   0c2d        mov.n   a2, a12
40202146:   2521d2          l32i    a13, a1, 148
40202149:   2621c2          l32i    a12, a1, 152
4020214c:   2421e2          l32i    a14, a1, 144
4020214f:   2321f2          l32i    a15, a1, 140
40202152:   119a        add.n   a1, a1, a9
40202154:   f00d        ret.n

스택 메모리 할당(a1) 동작부터 메모리 정리 후 리턴까지 찾아서 
예외처리가 아닌 부분만 모두 모아왔다. 

 

이게 개인키와 인증서를 로드하는 함수 전문이다. 

 

 

40205196:   fce4c5          call0   0x40201fe4

# allocate stack memory
40201fe4:   a0a092          movi    a9, 160
40201fe7:   c01190          sub a1, a1, a9
# save register
40201fea:   2561d2          s32i    a13, a1, 148
40201fed:   ffebd1          l32r    a13, 0x40201f9c (0x3ffef69c)
40201ff0:   2661c2          s32i    a12, a1, 152
40201ff3:   202dd0          or  a2, a13, a13
40201ff6:   276102          s32i    a0, a1, 156
40201ff9:   2461e2          s32i    a14, a1, 144
40201ffc:   2361f2          s32i    a15, a1, 140
40201fff:   0x40217e74(a2)
40202002:   fd70c1          l32r    a12, 0x402015c4 (0x3ffef4c4)
if ret == 0
4020200d:   0x4020d3ac(a12, "Failed to mount file system")
40202010:   1c0c        movi.n  a12, 1
40202012:   004a06          j   0x4020213e
else if ret != 0
40202024:   0x4020ce54(a1+80, a13, "/cert.der", "r")
4020202a:   0x40217e50(a1+80)
if ret == 0
0x4020d3ac(a12, "Failed to open cert file")
else if ret != 0
40202037:   310000          srai    a0, a0, 16
4020203a:   ffdd        excw
4020203f:   0x4020d3ac(a12) # maybe print "Success to open cert file"
40202045:   0x4020eb84(100)
4020204b:   0x40217e34(a1+80)
40202058:   0x40209620(0x3ffef11c, a1+80, a2)
if ret == 0
0x4020d3ac(a12, "cert not loaded")
else if ret != 0
40202063:   0x4020d3ac(a12, "cert loaded")
40202070:   0x4020ce54(a1+40, a13, "/private.der", a15)
40202076:   0x40217e50(a1+40)
if ret == 0
0x4020d3ac(a12, "Failed to open private cert file")
else if ret != 0
4020209f:   0x4020d3ac(a12, "Success to open private cert file")
402020a5:   0x4020eb84(100)
402020ab:   0x40217e34(a1+40)
402020b6:   0x4020953c(a14, a1+40, a2)
if ret == 0
0x4020d3ac(a12, "private key not loaded")
else if ret != 0
402020c0:   0x4020d3ac(a12, "private key loaded")
402020cc:   0x4020ce54(a1, a13, "/ca.der", a15)
402020d1:   0x40217e50(a1)
if ret == 0
0x4020d3ac(a12, "Failed to open ca")
else if ret != 0
402020f7:   0x4020d3ac(a12, "Success to open ca")
402020fd:   0x4020eb84(100)
40202103:   0x40217e34(a1)
4020210d:   0x40209290(a14, a1, a2)
40202110:   d28c        beqz.n  a2, 0x40202121
if ret == 0
40202121:   ???? # maybe print "ca failed"
else if ret != 0
40202117:   0x4020d3ac(a12, "ca loaded")
4020211a:   0c0c        movi.n  a12, 0 # set return value
4020211c:   000306          j   0x4020212c
4020212c:   0x40201f74(a1)
40202135:   0x40201f74(a1+40)
4020213b:   0x40201f74(a1+80)
# restore register
4020213e:   272102          l32i    a0, a1, 156
40202141:   a0a092          movi    a9, 160
40202144:   0c2d        mov.n   a2, a12
40202146:   2521d2          l32i    a13, a1, 148
40202149:   2621c2          l32i    a12, a1, 152
4020214c:   2421e2          l32i    a14, a1, 144
4020214f:   2321f2          l32i    a15, a1, 140
40202152:   119a        add.n   a1, a1, a9
40202154:   f00d        ret.n

 

가독성을 위해 분기가 있는 부분만 c언어로 표현했다. 

예외처리랑 함수 호출 동작은 전부 모았고. 
문자열 리터럴도 인자로 가져왔다. 

 

앞뒤로 레지스터 보존을 위한 동작 몇가지를 제외하면 
복잡한 연산 없이 정직하게 함수 호출만 한다. 

버퍼로 추정되는 인자를 통해 추측하자면 


a1 - /ca.der
a1+40 - /private.der
a1+80 - /cert.der
이렇게가 각 파일 로드에 사용된 버퍼다. 

 

 

2.2 세부 함수 분석

40202024:   0x4020ce54(a1+80, a13, "/cert.der", "r")
4020202a:   0x40217e50(a1+80)

40202070:   0x4020ce54(a1+40, a13, "/private.der", "r")
40202076:   0x40217e50(a1+40)

402020cc:   0x4020ce54(a1, a13, "/ca.der", "r")
402020d1:   0x40217e50(a1)

 

open으로 추정되는 동작은 모두 이 패턴이다. 

파일을 버퍼로 읽어온 뒤에 0x40217e50 함수를 호출한다. 
저 곳에 복구 루틴이 있을 가능성이 높을 것으로 의심된다. 

 

40217e50:   4248        l32i.n  a4, a2, 16
40217e52:   130c        movi.n  a3, 1
40217e54:   020c        movi.n  a2, 0
40217e56:   932340          movnez  a2, a3, a4
40217e59:   f00d        ret.n

 

코드는 이게 전문이다. 
인자만 설정하고 리턴..

 

리턴???

아니다 이건 확실히 아니다. 

 

4020212c:   0x40201f74(a1)
40202135:   0x40201f74(a1+40)
4020213b:   0x40201f74(a1+80)

 

이건가? 이거 같기도 하다. 

제발

 

40201f74:   f0c112          addi    a1, a1, -16
40201f77:   0261c2          s32i    a12, a1, 8
40201f7a:   20c220          or  a12, a2, a2
40201f7d:   fffc21          l32r    a2, 0x40201f70 (0x40218600)
40201f80:   3109        s32i.n  a0, a1, 12
40201f82:   006c22          s32i    a2, a12, 0
40201f85:   1ccc22          addi    a2, a12, 28
40201f88:   155805          call0   0x4021750c
40201f8b:   14cc22          addi    a2, a12, 20
40201f8e:   1557c5          call0   0x4021750c
40201f91:   3108        l32i.n  a0, a1, 12
40201f93:   21c8        l32i.n  a12, a1, 8
40201f95:   10c112          addi    a1, a1, 16
40201f98:   f00d        ret.n

 

0x4021750c 함수 찾아가보자. 
안 나올까봐 슬슬 무섭다. 

 

4021750c:   f0c112          addi    a1, a1, -16
4021750f:   21c9        s32i.n  a12, a1, 8
40217511:   02c8        l32i.n  a12, a2, 0
40217513:   3109        s32i.n  a0, a1, 12
40217515:   3cac        beqz.n  a12, 0x4021753c
40217517:   1c28        l32i.n  a2, a12, 4
40217519:   320b        addi.n  a3, a2, -1
4021751b:   1c39        s32i.n  a3, a12, 4
4021751d:   1b1266          bnei    a2, 1, 0x4021753c
40217520:   0c28        l32i.n  a2, a12, 0
40217522:   2238        l32i.n  a3, a2, 8
40217524:   202cc0          or  a2, a12, a12
40217527:   0003c0          callx0  a3
4021752a:   2c28        l32i.n  a2, a12, 8
4021752c:   320b        addi.n  a3, a2, -1
4021752e:   2c39        s32i.n  a3, a12, 8
40217530:   081266          bnei    a2, 1, 0x4021753c
40217533:   0c28        l32i.n  a2, a12, 0
40217535:   3238        l32i.n  a3, a2, 12
40217537:   0c2d        mov.n   a2, a12
40217539:   0003c0          callx0  a3
4021753c:   3108        l32i.n  a0, a1, 12
4021753e:   21c8        l32i.n  a12, a1, 8
40217540:   10c112          addi    a1, a1, 16
40217543:   f00d        ret.n

 

분기가 세개가 있는데 걸리면 모두 리턴으로 보낸다. 
단순 예외처리 동작인 것 같다. 

 

근데 문제는 a3를 호출한다. 
파일 포인터에 들어있던 헨들러 함수가 아닌가 싶다. 

 

갑자기 난이도가 크게 올라갔다. 
이렇게 되면 헨들러에 뭐가 있는지 알 수가 없으니 open부터 뒤져봐야한다. 

0x4020ce54(a1+40, a13, "/private.der", "r")에서 쓰인 0x4020ce54 주소를 확인해보자. 

 

4020ce53:   c11200          mul16u  a1, a2, a0
4020ce56:   61e2b0          excw
4020ce59:   04ed10          extui   a14, a1, 13, 1
4020ce5c:   0348        l32i.n  a4, a3, 0
4020ce5e:   1261c2          s32i    a12, a1, 72
4020ce61:   1161d2          s32i    a13, a1, 68
4020ce64:   136102          s32i    a0, a1, 76
4020ce67:   02cd        mov.n   a12, a2
4020ce69:   03dd        mov.n   a13, a3
4020ce6b:   052d        mov.n   a2, a5
4020ce6d:   34cc        bnez.n  a4, 0x4020ce74
4020ce6f:   000406          j   0x4020ce83
4020ce72:   420000          excw
4020ce75:   3230c1          l32r    a12, 0x401d9738
4020ce78:   c534c1          l32r    a12, 0x401fe348
4020ce7b:   200af0          or  a0, a10, a15
4020ce7e:   562042          l32i    a4, a0, 0x158
4020ce81:   2d0122          l8ui    a2, a1, 45
4020ce84:   3d0c        movi.n  a13, 3
4020ce86:   014901          l32r    a0, 0x401cd3ac
4020ce89:   1149        s32i.n  a4, a1, 4
4020ce8b:   ffe585          call0   0x4020cce4
4020ce8e:   214b        addi.n  a2, a1, 4
4020ce90:   001086          j   0x4020ced6

 

망했다. 
진짜 망했다. 

해석이 다 엉망으로 돼있다. 

 

 

0x4020ce54부터 잘라서 다시 디스어셈블해보자. 

 

오프셋은 0xbe3c

 

dd if=image2.bin of=decode.bin bs=1 skip=$((0x00000008+0x08+0xbe3c)) count=$((0x0068818-0xbe3c))

xtensa-lx106-elf-objdump -D -b binary -m xtensa -EL --start-address=0x4020ce54 --adjust-vma=0x4020ce54 decode.bin > decode.txt

 

0x4020ce54 이전을 전부 잘랐다. 

 

4020ce54:   b0c112          addi    a1, a1, -80
4020ce57:   1061e2          s32i    a14, a1, 64
4020ce5a:   04ed        mov.n   a14, a4
4020ce5c:   0348        l32i.n  a4, a3, 0
4020ce5e:   1261c2          s32i    a12, a1, 72
4020ce61:   1161d2          s32i    a13, a1, 68
4020ce64:   136102          s32i    a0, a1, 76
4020ce67:   02cd        mov.n   a12, a2
4020ce69:   03dd        mov.n   a13, a3
4020ce6b:   052d        mov.n   a2, a5
4020ce6d:   34cc        bnez.n  a4, 0x4020ce74
4020ce6f:   000406          j   0x4020ce83
4020ce72:   420000          excw
4020ce75:   3230c1          l32r    a12, 0x401d9738
4020ce78:   c534c1          l32r    a12, 0x401fe348
4020ce7b:   200af0          or  a0, a10, a15
4020ce7e:   562042          l32i    a4, a0, 0x158
4020ce81:   2d0122          l8ui    a2, a1, 45
4020ce84:   3d0c        movi.n  a13, 3
4020ce86:   014901          l32r    a0, 0x401cd3ac
4020ce89:   1149        s32i.n  a4, a1, 4
4020ce8b:   ffe585          call0   0x4020cce4
4020ce8e:   214b        addi.n  a2, a1, 4
4020ce90:   001086          j   0x4020ced6

 

불필요한 부분을 자르고 다시 디스어셈블했는데 
앞부분은 괜찮아졌지만 금방 다시 깨진다. 

 

dd if=image2.bin of=decode.bin bs=1 skip=$((0x00000008+0x08+be5c)) count=$((0x0068818-be5c))

xtensa-lx106-elf-objdump -D -b binary -m xtensa -EL --start-address=0x4020ce74 --adjust-vma=0x4020ce74 decode.bin > decode.txt
4020ce74:   8e5c        movi.n  a14, 88
4020ce76:   4c3d12          excw
4020ce79:   bf              .byte 0xbf
4020ce7a:   124000          excw

 

이것도 엉망이다. 

이제 진짜 방법이 없다. 

 

코드가 정상적으로 해석이 안된다. 
디스어셈블을 시도할 다른 방법도 남아있질 않다. 

 

기계어로 분석하는건 말이 안되는데 

진짜 망했다.. 

이제 진짜 어떡하지... 

 

 

'RSW-725R > 분석 자료 (공개용)' 카테고리의 다른 글

8. 후킹2 - 코드 설계 및 어셈블러 제작  (0) 2026.04.22
7. 후킹1 - 펌웨어 변조  (0) 2026.04.22
5. 세그먼트 탐색 및 추출  (0) 2026.04.22
4. 펌웨어 디스어셈블  (0) 2026.04.22
3. 동적 분석  (0) 2026.04.22