NewStar easygui re wp
NewStar easygui re wp
参考题解:NewStar CTF week4-CSDN博客
提示先去看消息机制:深入理解windows 消息机制_⒉消息队列发送消息,消息的标识可以从键盘获取,-CSDN博客大概看下
64位无壳
shift+f12没找到关键字符串,看看函数有个winmain点进去,都是些系统函数
发现sub_140001490不是点进去发现主要逻辑。
gui要注意MessageBox函数,一般这些函数就是解题的关键,比如点击verify就弹出了一个MessageBox
直接靠messagebox x交叉引用定位也行:
sub函数内部:
阅读代码
发现有个反调试器,一般碰见这种,我们需要直接nop或者jz改为jnz…这里我直接nop了
下面的逻辑就是
DlgItem = GetDlgItem(hWndParent, 101);
WindowTextW = GetWindowTextW(DlgItem, String, 100);
if ( WindowTextW <= 44 )
{
sub_140001000(String, (unsigned int)WindowTextW, v93);
v12 = 0;
if ( v93[1] == -57 )
v12 = v93[0] == -33;
v13 = 0;
if ( v93[2] == 77 )
v13 = v12;
可以理解,v93其实就是密文,这个代码那么长其实就是干校验密文
发现一个sub_140001000函数
WindowTextW<=44再对比下面的代码可以推测(也可以看sub_140001000内部推测),WindowTextW是密文长度44位(输入长度)
看sub_140001000内部
这段代码做的是给Src赋值,a1也就是上面的string每两个字节赋值给Src
每个元素占用2个字节,因此
a1 + 2 * i
表示第i
个元素的地址。
然后加载v29
常量
最后将 Src[j]
的值作为索引,从 v29
数组中读取一个字节的数据,并将其存储回 Src[j]
中。
也就是说,我们也可以反过来根据Src去找v29的值,最后可能解密用到
下面是第一次加密:
紧接着两个dowhile
第一个dowhile 我们发现我们所输入的字符串应该是44长度,根本用不了Src[v19+303],这段我们可以先不关注
第二个dowhile 是对v29进行变形
再看到下面:
也就是加密过程是通过Src异或完成的
return (unsigned __int64)memcpy(a3, Src, v7);
我推测是要把Src赋值给a3也就是v93,后面动态调试也验证了
那么如何解出这道题?
我们可以用获取的密文(v93)去动态调试在程序运行到第一个dowhile循环的时候去替代Src
为什么这样做,因为第二次加密是个异或,我们再异或一次就得到第二次解密的完的密文,因为两个dowhile的操作都不影响Src(第一个Src操作影响不到,第二次是对v29的操作),我们可以直接到第一次加密处写解密代码。
下面下几个断点
输入aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
这里有一个坑点就是这个输入框输入的最大长度是42,但我们要输入44位,可以修改寄存器的值
改成2C:
运行到:
替换Src:
这里我用脚本,地址根据你自己的地址来
# 导入IDA Python API
import ida_bytes
# 指定起始地址和字节数组
start_address = 0X000000E3CE76EB50
bytes_to_write = [
0xdf, 0xc7, 0x4d, 0x14, 0xc1, 0xec, 0x08, 0xe4, 0x5f, 0x3f,
0x03, 0xb4, 0x90, 0x4a, 0xb9, 0x8f, 0x8f, 0xfa, 0x71, 0x43,
0xc7, 0xf1, 0x9d, 0xdd, 0x4f, 0xc0, 0x12, 0x44, 0x5c, 0x9d,
0x88, 0x36, 0x2d, 0x16, 0x1d, 0xed, 0xbc, 0xef, 0xbb, 0x5b,
0x9f, 0x77, 0xeb, 0x58
]
# 将字节逐个写入内存
for i, byte in enumerate(bytes_to_write):
ida_bytes.patch_byte(start_address + i, byte)
print("Memory modification completed.")
得到第二次解密后的:
shift+e提出然后到第一次加密的地方写解密脚本
v29 = [
0x31, 0x74, 0x54, 0x20, 0x03, 0x53, 0x78, 0x70, 0x3A, 0x35,
0x65, 0x42, 0x04, 0x6B, 0x1F, 0x43, 0x06, 0x37, 0x00, 0x76,
0x21, 0x08, 0x0B, 0x13, 0x52, 0x4B, 0x2F, 0x1A, 0x59, 0x2C,
0x56, 0x51, 0x7F, 0x3B, 0x0E, 0x05, 0x26, 0x15, 0x25, 0x63,
0x64, 0x7A, 0x3C, 0x29, 0x41, 0x2A, 0x12, 0x17, 0x2E, 0x39,
0x57, 0x3D, 0x66, 0x33, 0x44, 0x6C, 0x6F, 0x47, 0x16, 0x71,
0x5F, 0x1C, 0x14, 0x5A, 0x0C, 0x4F, 0x01, 0x30, 0x1B, 0x68,
0x0F, 0x62, 0x3F, 0x18, 0x69, 0x6D, 0x7E, 0x5D, 0x6A, 0x28,
0x22, 0x5B, 0x55, 0x72, 0x09, 0x5E, 0x02, 0x3E, 0x50, 0x7B,
0x46, 0x45, 0x38, 0x10, 0x48, 0x79, 0x60, 0x36, 0x61, 0x6E,
0x2D, 0x49, 0x7C, 0x2B, 0x34, 0x27, 0x11, 0x7D, 0x0D, 0x0A,
0x77, 0x73, 0x58, 0x5C, 0x4C, 0x32, 0x4D, 0x1E, 0x24, 0x40,
0x67, 0x4A, 0x4E, 0x1D, 0x07, 0x75, 0x19, 0x23, 0xA0, 0xF4,
0x8F, 0xF8, 0x30
]
Src = [
0x6F, 0x81, 0xA6, 0xC5, 0x63, 0xAC, 0x4B, 0xC7, 0x8F, 0x29,
0x87, 0xA4, 0x27, 0xAA, 0xA6, 0x69, 0x4F, 0x27, 0xAE, 0xEC,
0x27, 0x2E, 0xE7, 0xA9, 0x69, 0x87, 0x2E, 0xE5, 0x2F, 0x24,
0xE6, 0x6F, 0x44, 0x87, 0xA9, 0x89, 0x4F, 0x26, 0x47, 0x21,
0xAB, 0x01, 0xA7, 0xAE
]
enc = [
0xdf, 0xc7, 0x4d, 0x14, 0xc1, 0xec, 0x8, 0xe4, 0x5f, 0x3f,
0x3, 0xb4, 0x90, 0x4a, 0xb9, 0x8f, 0x8f, 0xfa, 0x71, 0x43,
0xc7, 0xf1, 0x9d, 0xdd, 0x4f, 0xc0, 0x12, 0x44, 0x5c, 0x9d,
0x88, 0x36, 0x2d, 0x16, 0x1d, 0xed, 0xbc, 0xef, 0xbb, 0x5b,
0x9f, 0x77, 0xeb, 0x58
]
for k in range(0, 44, 4):
a = Src[k]
b = Src[k + 1]
c = Src[k + 2]
d = Src[k + 3]
Src[k + 3] = ((a >> 5) & 0x7) | ((d << 3) & 0xf8)
Src[k + 2] = ((d >> 5) & 0x7) | ((c << 3) & 0xf8)
Src[k + 1] = ((c >> 5) & 0x7) | ((b << 3) & 0xf8)
Src[k] = ((b >> 5) & 0x7) | ((a << 3) & 0xf8)
# 这个上面讲了
for i in range(len(Src)):
print(chr(v29.index(Src[i])),end='')
print()
for i in range(44):
print('a',end='')
这个解密很容易踩坑,我第一次写就踩了
比如Src[k+3] = ((a>>5) & 0x7) | ((d<<3)&0xf8),原来的加密中是
for ( k = 0i64; k < v4; k += 4i64 )
{
v11 = Src[k + 3];
v12 = Src[k + 2];
v13 = ((unsigned __int8)Src[k] >> 3) | (32 * v11);
v14 = 32 * Src[k + 1];
Src[k + 1] = (32 * Src[k]) | ((unsigned __int8)Src[k + 1] >> 3);
v15 = 32 * v12;
Src[k + 2] = v14 | HIBYTE(v15);
Src[k] = v13;
Src[k + 3] = v15 | (v11 >> 3);
}
Src[k+3]由v15和v11组成也就是Src[k+3]和Src[k+2]因此我写出了Src[k+3] = ((c>>5) & 0x7) | ((d<<3)&0xf8)这样的代码,实际是错的
因为其实原来的加密的意思是Src[k+3]由Src[k+2]的低5位和Src[k+3]的高3位组成,Src[k]由Src[k+3]的低5位和Src[k]的高3位组成
那么我们要还原Src[k+3]也就是由rc[k+3]的高3位和Src[k+3]的低5位拼接,这样才是正确的
这里解密写成这样也行:
for k in range(0, 44, 4):
a = Src[k]
b = Src[k + 1]
c = Src[k + 2]
d = Src[k + 3]
Src[k + 3] = ((a >> 5) & 0xff) | ((d << 3) & 0xff)
Src[k + 2] = ((d >> 5) & 0xff) | ((c << 3) & 0xff)
Src[k + 1] = ((c >> 5) & 0xff) | ((b << 3) & 0xff)
Src[k] = ((b >> 5) & 0xff) | ((a << 3) & 0xff)
0x7是低三位的意思,0x8f也差不多
全用0xff因为与的目的其实跟mod 256差不多,因为向高位移位的话如果不与可能会把高位的也搞进去比如
1111 0000 << 3 为 111 1000 0000其实我们最大是8位,但是|的时候不做处理会把高位也放进去
得到也是flag{GU!_r3v3R5e_3nG1n3er1ng_i5_v3ry_s1mpl3}
b >> 5) & 0xff) | ((a << 3) & 0xff)
> ```
> 0x7是低三位的意思,0x8f也差不多
> ```
全用0xff因为与的目的其实跟mod 256差不多,因为向高位移位的话如果不与可能会把高位的也搞进去比如
1111 0000 << 3 为 111 1000 0000其实我们最大是8位,但是|的时候不做处理会把高位也放进去
得到也是flag{GU!_r3v3R5e_3nG1n3er1ng_i5_v3ry_s1mpl3}
**这道题很质量**