记录第一次脱壳

记录第一次脱壳,第1张

记录第一次脱壳

第一次脱壳,vmp+xor

前几天的“恶意软件分析”课程作业,附加题是个elf格式的VMP文件,说是装了虚拟机壳,写个反汇编器就可以解出来。目标是输对密码,让它报告congratulations。但是放进我的linux虚拟机就是不运行,查了后是缺头文件,于是装好环境,运行了。
破解的工具是IDA,main函数调用个函数,进去后是一片变量声明,后面就是文档里说的一堆switch,也就是虚拟机壳接收指令集的方式。通过阅读和变量重命名,终于看会了它的原理。真正的代码被放在某个数据段中,这个函数的作用只是把它读出来,然后根据代码中的第一位判断指令,后面几位是对应的参数,基本就是mov ax, bx这类汇编指令的二进制版本。我的初步目标很简单,就是把这些代码翻译出来,至少翻译成逐句的文字版汇编指令。
我这段时间以来用的最多的是C++,考虑到不练就生疏的问题,决定练练python。Python是可以读取二进制文件的,一开始做感觉不适应,代码却很短,也是with open这些,然后写反汇编的时候就发现没有switch,于是费劲写了一堆elif。于是分屏对比着写反汇编码,由于其中有不少跳转指令,所以额外添加了行号的显示,其中4154到7899是从59开始(大概?)往后的代码段地址,我已经做了反xor处理,否则59往后都是乱码。可以看到其中还有很多不完善的地方,不过基本的指令已经可以看出来了,有能力的读者可以从最后硬编码的一些数据中恢复出30位的ascii码密码。

1: mov ds[0], 4154
8: mov ds[1], 7899
15: mov ds[3], ds[0]
19: mov ds[5], 24432450
26: mov ds[4], [ds[3]]
29: ds[4] = ds[5] xor ds[4]
34: mov [ds[3]], ds[4]
37: ds[3] = ds[3] + 4
45: cmp ds[1], ds[3]
49: jge not jumping
54: jmp to 58 (40fa)
59: jmp to 3008 (4c80)
3009: push
3012: push
3015: push
3018: call 2669
2670: mov ds[0], 80
2677: call 63
64: io: put_char
66: ret (2681)
2682: mov ds[0], 108
2689: call 63
64: io: put_char
66: ret (2693)
2694: mov ds[0], 101
2701: call 63
64: io: put_char
66: ret (2705)
2706: mov ds[0], 97
2713: call 63
64: io: put_char
66: ret (2717)
2718: mov ds[0], 115
2725: call 63
64: io: put_char
66: ret (2729)
2730: mov ds[0], 101
2737: call 63
64: io: put_char
66: ret (2741)
2742: mov ds[0], 32
2749: call 63
64: io: put_char
66: ret (2753)
2754: mov ds[0], 101
2761: call 63
64: io: put_char
66: ret (2765)
2766: mov ds[0], 110
2773: call 63
64: io: put_char
66: ret (2777)
2778: mov ds[0], 116
2785: call 63
64: io: put_char
66: ret (2789)
2790: mov ds[0], 101
2797: call 63
64: io: put_char
66: ret (2801)
2802: mov ds[0], 114
2809: call 63
64: io: put_char
66: ret (2813)
2814: mov ds[0], 32
2821: call 63
64: io: put_char
66: ret (2825)
2826: mov ds[0], 121
2833: call 63
64: io: put_char
66: ret (2837)
2838: mov ds[0], 111
2845: call 63
64: io: put_char
66: ret (2849)
2850: mov ds[0], 117
2857: call 63
64: io: put_char
66: ret (2861)
2862: mov ds[0], 114
2869: call 63
64: io: put_char
66: ret (2873)
2874: mov ds[0], 32
2881: call 63
64: io: put_char
66: ret (2885)
2886: mov ds[0], 112
2893: call 63
64: io: put_char
66: ret (2897)
2898: mov ds[0], 97
2905: call 63
64: io: put_char
66: ret (2909)
2910: mov ds[0], 115
2917: call 63
64: io: put_char
66: ret (2921)
2922: mov ds[0], 115
2929: call 63
64: io: put_char
66: ret (2933)
2934: mov ds[0], 119
2941: call 63
64: io: put_char
66: ret (2945)
2946: mov ds[0], 111
2953: call 63
64: io: put_char
66: ret (2957)
2958: mov ds[0], 114
2965: call 63
64: io: put_char
66: ret (2969)
2970: mov ds[0], 100
2977: call 63
64: io: put_char
66: ret (2981)
2982: mov ds[0], 58
2989: call 63
64: io: put_char
66: ret (2993)
2994: mov ds[0], 32
3001: call 63
64: io: put_char
66: ret (3005)
3006: ret (3022)
3023: mov ds[1], 30
3030: mov ds[0], 4
3037: call 128
129: push
132: push
135: mov ds[30], ds[1]
ds[0] = ds[0] * ds[30] mul
144: ds[0] = ds[0] + 8
152: mov ds[29], ds[0]
156: io: return and inc
158: and ds[0], 0
162: jz to 194
167: mov ds[4], ds[29]
171: ds[4] = 3 >> ds[4] right shift
179: mov [ds[0]], ds[4]
182: ds[0] = ds[0] + 8
pop
pop
194: ret (3041)
3042: mov ds[11], ds[0]
3046: jmp to 3055 (4caf)
3056: mov ds[9], 0
3063: jmp to 3430 (4e26)
3431: mov ds[30], ds[9]
3435: mov ds[0], 30
3442: cmp ds[30], ds[0]
3446: jl 3067
3451: call 75
76: io: scan char
78: ret (3455)
3456: mov ds[1], ds[0]
3460: mov ds[30], ds[1]
3464: mov ds[0], 10
3471: cmp ds[30], ds[0]
3475: jnz 3484
3480: jmp to 3774 (4f7e)
3775: mov ds[0], ds[11]
3779: call 681
682: push
685: push
688: mov ds[10], ds[0]
692: mov ds[1], 30
699: mov ds[0], 4
706: call 128
129: push
132: push
135: mov ds[30], ds[1]
ds[0] = ds[0] * ds[30] mul
144: ds[0] = ds[0] + 8
152: mov ds[29], ds[0]
156: io: return and inc
158: and ds[0], 0
162: jz to 194
167: mov ds[4], ds[29]
171: ds[4] = 3 >> ds[4] right shift
179: mov [ds[0]], ds[4]
182: ds[0] = ds[0] + 8
pop
pop
194: ret (710)
711: mov ds[9], ds[0]
715: mov ds[1], 4
722: mov ds[0], 4
729: call 128
129: push
132: push
135: mov ds[30], ds[1]
ds[0] = ds[0] * ds[30] mul
144: ds[0] = ds[0] + 8
152: mov ds[29], ds[0]
156: io: return and inc
158: and ds[0], 0
162: jz to 194
167: mov ds[4], ds[29]
171: ds[4] = 3 >> ds[4] right shift
179: mov [ds[0]], ds[4]
182: ds[0] = ds[0] + 8
pop
pop
194: ret (733)
734: mov ds[5], ds[0]
738: mov ds[30], 253
745: mov [ds[5]], ds[30]
748: mov ds[30], 1
 mul
763: mov ds[0], ds[5]
767: ds[30] = ds[0] + ds[30]
772: mov ds[1], ds[30]
776: mov ds[30], 14
783: mov [ds[1]], ds[30]
786: mov ds[30], 1
 mul
801: mov ds[0], ds[5]
805: ds[30] = ds[0] + ds[30]
810: mov ds[1], ds[30]
814: mov ds[30], 99
821: mov [ds[1]], ds[30]
824: mov ds[30], 1
 mul
839: mov ds[0], ds[5]
843: ds[30] = ds[0] + ds[30]
848: mov ds[1], ds[30]
852: mov ds[30], 79
859: mov [ds[1]], ds[30]
862: mov ds[30], 141
869: mov [ds[9]], ds[30]
872: mov ds[30], 1
 mul
887: mov ds[0], ds[9]
891: ds[30] = ds[0] + ds[30]
896: mov ds[1], ds[30]
900: mov ds[30], 111
907: mov [ds[1]], ds[30]
910: mov ds[30], 1
 mul
925: mov ds[0], ds[9]
929: ds[30] = ds[0] + ds[30]
934: mov ds[1], ds[30]
938: mov ds[30], 0
945: mov [ds[1]], ds[30]
948: mov ds[30], 1
 mul
963: mov ds[0], ds[9]
967: ds[30] = ds[0] + ds[30]
972: mov ds[1], ds[30]
976: mov ds[30], 36
983: mov [ds[1]], ds[30]
986: mov ds[30], 1
 mul
1001: mov ds[0], ds[9]
1005: ds[30] = ds[0] + ds[30]
1010: mov ds[1], ds[30]
1014: mov ds[30], 152
1021: mov [ds[1]], ds[30]
1024: mov ds[30], 1
 mul
1039: mov ds[0], ds[9]
1043: ds[30] = ds[0] + ds[30]
1048: mov ds[1], ds[30]
1052: mov ds[30], 124
1059: mov [ds[1]], ds[30]
1062: mov ds[30], 1
 mul
1077: mov ds[0], ds[9]
1081: ds[30] = ds[0] + ds[30]
1086: mov ds[1], ds[30]
1090: mov ds[30], 16
1097: mov [ds[1]], ds[30]
1100: mov ds[30], 1
 mul
1115: mov ds[0], ds[9]
1119: ds[30] = ds[0] + ds[30]
1124: mov ds[1], ds[30]
1128: mov ds[30], 16
1135: mov [ds[1]], ds[30]
1138: mov ds[30], 1
 mul
1153: mov ds[0], ds[9]
1157: ds[30] = ds[0] + ds[30]
1162: mov ds[1], ds[30]
1166: mov ds[30], 156
1173: mov [ds[1]], ds[30]
1176: mov ds[30], 1
 mul
1191: mov ds[0], ds[9]
1195: ds[30] = ds[0] + ds[30]
1200: mov ds[1], ds[30]
1204: mov ds[30], 96
1211: mov [ds[1]], ds[30]
1214: mov ds[30], 1
 mul
1229: mov ds[0], ds[9]
1233: ds[30] = ds[0] + ds[30]
1238: mov ds[1], ds[30]
1242: mov ds[30], 7
1249: mov [ds[1]], ds[30]
1252: mov ds[30], 1
 mul
1267: mov ds[0], ds[9]
1271: ds[30] = ds[0] + ds[30]
1276: mov ds[1], ds[30]
1280: mov ds[30], 16
1287: mov [ds[1]], ds[30]
1290: mov ds[30], 1
 mul
1305: mov ds[0], ds[9]
1309: ds[30] = ds[0] + ds[30]
1314: mov ds[1], ds[30]
1318: mov ds[30], 139
1325: mov [ds[1]], ds[30]
1328: mov ds[30], 1
 mul
1343: mov ds[0], ds[9]
1347: ds[30] = ds[0] + ds[30]
1352: mov ds[1], ds[30]
1356: mov ds[30], 99
1363: mov [ds[1]], ds[30]
1366: mov ds[30], 1
 mul
1381: mov ds[0], ds[9]
1385: ds[30] = ds[0] + ds[30]
1390: mov ds[1], ds[30]
1394: mov ds[30], 16
1401: mov [ds[1]], ds[30]
1404: mov ds[30], 1
 mul
1419: mov ds[0], ds[9]
1423: ds[30] = ds[0] + ds[30]
1428: mov ds[1], ds[30]
1432: mov ds[30], 16
1439: mov [ds[1]], ds[30]
1442: mov ds[30], 1
 mul
1457: mov ds[0], ds[9]
1461: ds[30] = ds[0] + ds[30]
1466: mov ds[1], ds[30]
1470: mov ds[30], 156
1477: mov [ds[1]], ds[30]
1480: mov ds[30], 1
 mul
1495: mov ds[0], ds[9]
1499: ds[30] = ds[0] + ds[30]
1504: mov ds[1], ds[30]
1508: mov ds[30], 96
1515: mov [ds[1]], ds[30]
1518: mov ds[30], 1
 mul
1533: mov ds[0], ds[9]
1537: ds[30] = ds[0] + ds[30]
1542: mov ds[1], ds[30]
1546: mov ds[30], 7
1553: mov [ds[1]], ds[30]
1556: mov ds[30], 1
 mul
1571: mov ds[0], ds[9]
1575: ds[30] = ds[0] + ds[30]
1580: mov ds[1], ds[30]
1584: mov ds[30], 16
1591: mov [ds[1]], ds[30]
1594: mov ds[30], 1
 mul
1609: mov ds[0], ds[9]
1613: ds[30] = ds[0] + ds[30]
1618: mov ds[1], ds[30]
1622: mov ds[30], 133
1629: mov [ds[1]], ds[30]
1632: mov ds[30], 1
 mul
1647: mov ds[0], ds[9]
1651: ds[30] = ds[0] + ds[30]
1656: mov ds[1], ds[30]
1660: mov ds[30], 97
1667: mov [ds[1]], ds[30]
1670: mov ds[30], 1
 mul
1685: mov ds[0], ds[9]
1689: ds[30] = ds[0] + ds[30]
1694: mov ds[1], ds[30]
1698: mov ds[30], 17
1705: mov [ds[1]], ds[30]
1708: mov ds[30], 1
 mul
1723: mov ds[0], ds[9]
1727: ds[30] = ds[0] + ds[30]
1732: mov ds[1], ds[30]
1736: mov ds[30], 60
1743: mov [ds[1]], ds[30]
1746: mov ds[30], 1
 mul
1761: mov ds[0], ds[9]
1765: ds[30] = ds[0] + ds[30]
1770: mov ds[1], ds[30]
1774: mov ds[30], 162
1781: mov [ds[1]], ds[30]
1784: mov ds[30], 1
 mul
1799: mov ds[0], ds[9]
1803: ds[30] = ds[0] + ds[30]
1808: mov ds[1], ds[30]
1812: mov ds[30], 97
1819: mov [ds[1]], ds[30]
1822: mov ds[30], 1
 mul
1837: mov ds[0], ds[9]
1841: ds[30] = ds[0] + ds[30]
1846: mov ds[1], ds[30]
1850: mov ds[30], 11
1857: mov [ds[1]], ds[30]
1860: mov ds[30], 1
 mul
1875: mov ds[0], ds[9]
1879: ds[30] = ds[0] + ds[30]
1884: mov ds[1], ds[30]
1888: mov ds[30], 16
1895: mov [ds[1]], ds[30]
1898: mov ds[30], 1
 mul
1913: mov ds[0], ds[9]
1917: ds[30] = ds[0] + ds[30]
1922: mov ds[1], ds[30]
1926: mov ds[30], 144
1933: mov [ds[1]], ds[30]
1936: mov ds[30], 1
 mul
1951: mov ds[0], ds[9]
1955: ds[30] = ds[0] + ds[30]
1960: mov ds[1], ds[30]
1964: mov ds[30], 119
1971: mov [ds[1]], ds[30]
1974: mov ds[3], 0
1981: jmp to 1990 (4886)
1991: mov ds[1], 0
1998: mov ds[2], 0
2005: jmp to 2144 (4920)
2145: mov ds[3], ds[1]
2149: mov ds[30], ds[2]
2153: mov ds[0], 30
2160: cmp ds[30], ds[0]
2164: jl 2009
2010: mov ds[30], ds[2]
 mul
2022: mov ds[0], ds[10]
2026: ds[30] = ds[0] + ds[30]
2031: mov ds[3], ds[30]
2035: mov ds[4], [ds[3]]
reg64 / reg32, hi: 4, lo: 2
2047: mov ds[30], ds[3]
 mul
2059: mov ds[0], ds[5]
2063: ds[30] = ds[0] + ds[30]
2068: mov ds[3], ds[30]
2072: mov ds[3], [ds[3]]
2075: ds[4] = ds[3] xor ds[4]
2080: mov ds[30], ds[2]
 mul
2092: mov ds[0], ds[9]
2096: ds[30] = ds[0] + ds[30]
2101: mov ds[3], ds[30]
2105: mov ds[3], [ds[3]]
2108: mov ds[30], ds[4]
2112: mov ds[0], ds[3]
2116: cmp ds[30], ds[0]
2120:

然后再去反汇编,发现总共很短(读取到0或29自动退出了),复杂的判断输出字符串逻辑肯定没这么短。于是再读前面的逻辑,做了大半天才在字里行间读出:xor。它居然在“真正的代码段”做xor,我又过了好久才意识到这是对代码做了第二次加密。这时候反汇编器要修改原代码了,但是发现python读取文件的a+模式居然强制加在最后,没法修改前面,w模式只能覆写,于是不得已用w模式逐字复制出新文件再改。结果写xor程序又搞心态,python无法对二进制读出来的byte数据类型直接做xor,于是先转成int再转回去。
手忙脚乱做了去壳,后面的指令又读不出来了,原来有一堆跳转,我这个程序是按顺序扫描指令的,我希望跟着它的程序执行,于是加了动态化的处理,指针跟着程序的运行去扫指令。不过分支其实很多,于是另一边开了IDA远程连接虚拟机去实际运行VMP。
其中绕了很多弯路,因为代码不dry(感觉是个小项目就侥幸了),后来做忙了就觉得一堆问题。总之分析出来关键的打印、读取字符串后终于发现密码是30位的……输了30位后,用IDA跟踪,由于没打算写IDA脚本,所以想要在A代码块断下来,不想在前面B代码块的循环里转,就要看哪个指令是A里有而B里没有的,然后在那里打个断点,太蠢了,但是人陷进去了就是不想磨刀。
核心的逻辑我最后差不多是连猜带蒙。前面的字符串输出时候我心惊肉跳地看着程序硬编码了一堆数字,然后从那些数字代表的偏移里取出了字符,数字是不连续的,所以没有对照表和程序硬编码的内容中的一样,就根本没办法看出字符。我有点担心密码也这么编,结果更加离谱。我沿着程序往下读,又读出一堆硬编码来,我(幸好)全都记下来了。它们分成3个位置存,事后数分别是4、1、29个,实际上后面两组就是密码。我又确定了一下关键的比较语句里对比的是什么,于是发现我输入的明文被做了xor,和30个里面的第1个做了对比。所以甚至程序中就完全没有存明文。我手工在计算器上做xor,然后发现前面的4个数字是循环的密钥,1号作用在1、5、9个字符,2号作用在2、6、10,以此类推。好不容易在ascii表里查找到对应的密码。
其他的内容我就不贴了,主要是因为在破解过程中没有留好截图和中间结果,现在想来有些遗憾。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5625341.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-15
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存