180822 逆向-网鼎杯(2-1)
Reverse
martricks
main函数中接收输入以后将input和一个字符串异或一下存入savedregs中
这里7*(i_23/7)+i_23%7
这种写法实际上还是i_23,只不过表示成了第x行y列的形式(7个元素/行)
两个数组分别存放在了savedregs-192和savedregs-128中
下面两层嵌套循环,中间进行了一个累加的运算
关键是箭头所指向的积的累加
实际上就是矩阵相乘
相乘结果异或后与数组6010a0比较
理解各个变量以后也可以通过逆矩阵的方式求解,不过矩阵归根结底还是线性方程,用Z3照抄跑一下就能出来,更省事XD
from z3 import *
t1 = [115, 111, 109, 101, 32, 108, 101, 103, 101, 110, 100, 115, 32, 114, 32, 116, 111, 108, 100, 44, 32, 115, 111, 109, 101, 32, 116, 117, 114, 110, 32, 116, 111, 32, 100, 117, 115, 116, 32, 111, 114, 32, 116, 111, 32, 103, 111, 108, 100]
t2 = [170, 122, 36, 10, 168, 188, 60, 252, 130, 75, 81, 82, 94, 28, 130, 31, 121, 186, 181, 227, 67, 4, 253, 172, 16, 181, 99, 189, 141, 231, 53, 217, 211, 232, 66, 109, 113, 90, 9, 84, 233, 159, 76, 220, 162, 175, 17, 135, 148]
flag = [BitVec("flag%d"%i, 8) for i in range(49)]
m1 = [0 for i in range(49)]
m2 = [0 for i in range(49)]
i_23 = 23
for i in range(49):
m1[i_23] = flag[i]^i_23
m2[i] = t1[i_23]^i
i_23 = (i_23+13)%49
a = 3
b = 4
c = 5
d = 41
s = Solver()
for i in range(7):
for j in range(7):
sum = 0
for k in range(7):
sum += m2[7*c+b]*m1[7*a+c]
c = (c+5)%7
# print(sum)
s.add(sum==t2[d]^d)
d = (d+31)%49
b = (b+4)%7
a = (a+3)%7
c = (s.check())
f = ""
if(c==sat):
m=s.model()
# print("flag",end='\n')
for i in range(49):
f += (chr(m[flag[i]].as_long()))
print(f)
give_a_try
GUI程序就不能直接跟着main函数走,而是要找到按钮的点击函数
按照MFC的套路 CreateDialogParamA(dword_404060, 1000, 0, sub_401223, 0);
这玩意儿的第四个参数就是回调函数拉
if ( (unsigned __int16)a3 == 1001 )
{
GetDlgItemTextA(a1, 1002, &Str, 255);
sub_401103(&Str);
}
然后找到取输入的地方,看下一个处理输入的函数sub_401103
判断长度以后,将输入的ASCII累加与某个变量异或作为种子
然后逐字符运算求解
刚开始被哈希吓到了,后来想了一想,求和的抗碰撞性极差,实际上对于42个字符的累加和穷举空间只有32*42~127*42=3990
个字节,对于计算机来说非常小
因此应该通过已知flag的前4个字符来穷举种子
异或的那个数初值为0,查看交叉引用发现在TLS_callback里
可以看到结构完全被破坏,显然是大量花指令的功劳
取其中一段为例分析
pizza:004020CA call loc_4020D0
pizza:004020CA ; ---------------------------------------------------------------------------
pizza:004020CF db 5
pizza:004020D0 ; ---------------------------------------------------------------------------
pizza:004020D0
pizza:004020D0 loc_4020D0: ; CODE XREF: pizza:004020CA↑j
pizza:004020D0 add dword ptr [esp], 6
pizza:004020D4 retn
pizza:004020D5 ; ---------------------------------------------------------------------------
pizza:004020D5 cmp dword_40406C, 0
call到4020d0后,会在栈中压入一个ret address指向4020ca–下一条指令的地址
然后4020d0对这个ret address加了6个字节,于是移到了4020cf+6=4020d5,即跳过了花指令
由于retn和call的组合会使得IDA无法分析函数结构,因此抗静态分析的效果还是不错的
尤其是如果像OD使用线性反汇编,会完全被那些脏字节干扰,使得整片区域的反汇编都错误
对于去花可以直接用IDC脚本,运行即可呈现出干净的指令
#include <idc.idc>
static matchBytes(StartAddr, Match)
{
auto Len, i, PatSub, SrcSub;
Len = strlen(Match);
while (i < Len)
{
PatSub = substr(Match, i, i+1);
SrcSub = form("%02X", Byte(StartAddr));
SrcSub = substr(SrcSub, i % 2, (i % 2) + 1);
if (PatSub != "?" && PatSub != SrcSub)
{
return 0;
}
if (i % 2 == 1)
{
StartAddr++;
}
i++;
}
return 1;
}
static main()
{
auto Addr, Start, End, Condition, junk_len, i;
Start = 0x402000;
End = 0x4020f4;
Condition = "E801000000??83042406C3";
junk_len = 11;
for (Addr = Start; Addr < End; Addr++)
{
if (matchBytes(Addr, Condition))
{
for (i = 0; i < junk_len; i++)
{
PatchByte(Addr, 0x90);
MakeCode(Addr);
Addr++;
}
}
}
AnalyzeArea(Start, End);
Message("Clear Fake-Jmp Opcode Ok ");
}
这里面还做了两个反调试:
1. NtSetInformationThread
这个函数,当第三个参数为0x11(ThreadHideFromDebugger)时,可以去掉所有调试器
2. NtQueryInformationProcess
通过第三个参数0x7,可以返回调试端口到某个变量中。之后可以通过判断该变量是否为0来反调。
本题中,如果该变量非0则不会将第二个tls_callback挂上SEH链,使得后续过程不会触发
该变量为0则会将0x4020f7赋给0x404042,即seh链的第二个触发函数
再过去看该函数,同样一大堆花
里面做的事也很简单,通过NtQueryInformationProcess
来反调,未被调试器附加时则会将0x40406c赋为0x31333337,然后对entry point–0x401000处取了一个字节来异或校验,本意应该是反调试器的断点(会将该字节改为0xcc)
所以最终0x40406c的值就是0x31333359
静态分析这么复杂,其实完全可以直接挂上调试器去看该内存的值
由于这两个反调试都仅在TLS中触发,所以挂上调试器的瞬间是没有影响的
得到值以后即可进行**种子
得到种子3681以后,就通过随机数向后逐字节**flag啦
#include <stdio.h>
#include <stdlib.h>
int cal(long long ori)
{
int i;
unsigned long long b=ori;
for(i=0;i<16;i++)
{
//printf("%x\n", b);
b = (b * b)%0xFAC96621;
}
return (ori*b)%0xFAC96621;
}
int t[] = {0x63b25af1, 0xc5659ba5, 0x4c7a3c33, 0xe4e4267, 0xb611769b, 0x3de6438c, 0x84dba61f, 0xa97497e6, 0x650f0fb3, 0x84eb507c, 0xd38cd24c, 0xe7b912e0, 0x7976cd4f, 0x84100010, 0x7fd66745, 0x711d4dbf, 0x5402a7e5, 0xa3334351, 0x1ee41bf8, 0x22822ebe, 0xdf5cee48, 0xa8180d59, 0x1576dedc, 0xf0d62b3b, 0x32ac1f6e, 0x9364a640, 0xc282dd35, 0x14c5fc2e, 0xa765e438, 0x7fcf345a, 0x59032bad, 0x9a5600be, 0x5f472dc5, 0x5dde0d84, 0x8df94ed5, 0xbdf826a6, 0x515a737a, 0x4248589e, 0x38a96c20, 0xcc7f61d9, 0x2638c417, 0xd9beb996};
int main()
{
int i, seed;
int r;
char ch;
//3681
/*for(seed=0;seed<=5292;seed++)
{
srand(seed^0x31333359);
r = cal('f'*rand());
//printf("%x\n", r);
if(r==0x63B25AF1){
printf("%d\n", seed);
printf("%x\n", cal('l'*rand()));
break;
}
}
*/
srand(3681^0x31333359);
for(i=0;i<42;i++)
{
r = rand();
//printf("%x\n", r);
for(ch=32;ch<127;ch++)
{
if(cal(ch*r)==t[i])
{
printf("%c", ch);
break;
}
}
if(ch==127){printf("error");break;}
}
return 0;
}
上一篇: 手工水饺与速冻水饺的区别是什么?
下一篇: net/http webserver学习