graf
6. 복호화 로직 분석 본문
삽질을 좀 하긴 했지만 세그먼트를 모두 확보하는데 성공했다.
이번 분석 목표는 추출한 세그먼트에서 인증서와 개인키의 디코딩 혹은 복호화 루틴의 존재 유무를 찾는거다.
리버싱은 깊게 공부해본 적이 없긴 한데,,
그래도 한번 시도나 해보자.
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 |