显然a2/v1是长度,限制了16,a1/&String是我们输入的字符串,看起来也有限制,但不知道也不影响做题。
那么修改一下参数名,来到核心方法4012A9
显然最终对比的是v6和text,v6就是加密后的结果,v8看起来像种子,根据种子用sub_40110D生成key,再执行sub_4011A6(text, 16, key)进行加密。
我们断0040133E call sub_4011A6记录一下key。
这个key是固定的68位,前64位作为密码表盘,65-67作为标志位指向表盘中的某个密码,刚开始为均为0x00。
接下来就是看sub_4011A6的算法了,这里要自己动调,对着汇编一行一行看,然后修改参数名和增加注释。
加密算法比较简单,核心其实就是v7/v8/v10。
v7=&key[i]
v8=&key[i2]
v10=&key[i3]
所以就是搞清楚i/i2/i3是怎么来的。第一轮循环中i=flag_1自增也就是1,
i2=(key[i] + flag_2) & 0x3f
由于flag_2第一轮为0,所以其实就是
i2=key[i] & 0x3f
i3=(i + key[i2]) & 0x3f
也就是说,i2跟key[i]有关,i3跟key[i2]有关,都是依次根据密码表盘上的某个位置的值算出来的。
还有这两处代码,它们实际上就是key[i]和key[i2]调换位置,key[i]和key[i3]调换位置,也就是说密码表盘是活动的。
在v7/v8/v10的生成过程当中,全程没有text参与。生成之后,执行了如下代码。
text[0] = text[0] – ((v7 + v8 + v10) & 0x3F)
那么加密流程就出来了。
1,在key[i]/key[i2]/key[i3]三处获得v7/v8/v10,i为自增,i2根据i生成,i3根据i和i2生成。
2,交换key[i]/key[i2]/key[i3]三处的位置
3,用v7+v8+v10加密text[0]
4,重复16次。
虽然密码表盘在变动,但变动的规律实际上是固定的,不受我们输入的text影响,也就是这题可以简化成,用16个key对text[0]-text[15]进行加密。这样我们就可以用一个笨办法来获得正向加密。
那就是将16次循环中的v7/v8/v10都找出来,很容易找到断点处。(*v7 + *v8 + *v10)对应的汇编。
可以看到第一次循环[edi]=0x19,[esi]=0x3e, [eax]=0x1e,和ida注释中动调的结果一样。
F9 16次,记录下每次的v7/v8/v10,写出正函数代码。
PS:这里更简单的做法是直接断dl寄存器里的值,也就是v7+v8+v10的结果。
def enc_k3(i, k1, k2, k3):
return hex(i - ((k1 + k2 + k3) & 0x3f))
def enc(text):
str = enc_k3(text[0], 0x19,0x3e,0x1e)
str += enc_k3(text[1], 0x12,0x27,0x14)
str += enc_k3(text[2], 0x0f,0x3d,0x02)
str += enc_k3(text[3], 0x37,0x0d,0x36)
str += enc_k3(text[4], 0x1f,0x28,0x03)
str += enc_k3(text[5], 0x11,0x06,0x28)
str += enc_k3(text[6], 0x08,0x34,0x04)
str += enc_k3(text[7], 0x2e,0x1d,0x13)
str += enc_k3(text[8], 0x15,0x0a,0x3f)
str += enc_k3(text[9], 0x0a,0x00,0x12)
str += enc_k3(text[10], 0x33,0x31,0x3e)
str += enc_k3(text[11], 0x03,0x1c,0x08)
str += enc_k3(text[12], 0x31,0x24,0x33)
str += enc_k3(text[13], 0x3f,0x20,0x33)
str += enc_k3(text[14], 0x08,0x03,0x11)
str += enc_k3(text[15], 0x04,0x3b,0x0a)
return str
text = bytearray(b'A' * 16)
print(enc(text))
对比后发现结果一样,那么反函数就非常简单了,带入v6的值可以得到flag。
def dec_k3(i, k1, k2, k3):
return chr(i + ((k1 + k2 + k3) & 0x3f))
def dec(text):
str = dec_k3(text[0], 0x19,0x3e,0x1e)
str += dec_k3(text[1], 0x12,0x27,0x14)
str += dec_k3(text[2], 0x0f,0x3d,0x02)
str += dec_k3(text[3], 0x37,0x0d,0x36)
str += dec_k3(text[4], 0x1f,0x28,0x03)
str += dec_k3(text[5], 0x11,0x06,0x28)
str += dec_k3(text[6], 0x08,0x34,0x04)
str += dec_k3(text[7], 0x2e,0x1d,0x13)
str += dec_k3(text[8], 0x15,0x0a,0x3f)
str += dec_k3(text[9], 0x0a,0x00,0x12)
str += dec_k3(text[10], 0x33,0x31,0x3e)
str += dec_k3(text[11], 0x03,0x1c,0x08)
str += dec_k3(text[12], 0x31,0x24,0x33)
str += dec_k3(text[13], 0x3f,0x20,0x33)
str += dec_k3(text[14], 0x08,0x03,0x11)
str += dec_k3(text[15], 0x04,0x3b,0x0a)
return str
v6 = bytearray([
46, 103, 88, 41, 101, 37, 101, 70,
20, 20, 15, 18, 40, 36, 21, 45
])
print(dec(v6))
原文始发于微信公众号(珂技知识分享):web选手入门reverse(2)——crackme_easy
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论