0x00 日常查壳
无壳32位
0x01 花指令
简单看了下main函数,会发现两个关键函数都有花指令,现在写个取花的脚本
都是诸如此类的花,观测一下ida分析发现解析的还不一样,但都有的特征是call后面有add和retn的都是算花的,于是由此写了个去花的脚本(我仍未找到地址解析机器码的idc函数)
1 2 3 4 5 6 7 8 9 10
| .text:0040147E call loc_401486 .text:0040147E ; --------------------------------------------------------------------------- .text:00401483 db 0E8h .text:00401484 ; --------------------------------------------------------------------------- .text:00401484 jmp short loc_40148B .text:00401486 ; --------------------------------------------------------------------------- .text:00401486 .text:00401486 loc_401486: ; CODE XREF: sub_401460+1E↑j .text:00401486 add [esp+3Ch+var_3C], 1 .text:0040148A retn
|
运行在ida7.5版本上,就是把这段全部nop掉就没事了,设置一下起始和结束地址即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| st = 0x00401460 end = 0x00401630
def patchNop(start, end): for i in range(start, end): ida_bytes.patch_byte(i, 0x90)
def nextInstr(addr): return addr + idc.get_item_size(addr)
addr = st while ( addr < end ): next = nextInstr(addr) c = nextInstr(next) jnext = c + 1 anext = nextInstr(jnext) rnext = nextInstr(anext) if idc.print_insn_mnem(next) == "call" and idc.print_insn_mnem(anext) == "add" and idc.print_insn_mnem(rnext) == "retn": addr = nextInstr(rnext) patchNop(next, next + idc.get_item_size(next)) patchNop(c, c + 1) patchNop(jnext, jnext + idc.get_item_size(jnext)) patchNop(anext, anext + idc.get_item_size(anext)) patchNop(rnext, rnext + idc.get_item_size(rnext)) print("Patch %x" %next) else: addr = next
|
于是通杀了这片花,先u再c再p即可(各种api都找到了!!就是找不到地址解析机器码的)
但后面的花又长的不一样了!!因为在buu上做,所以看到源码我也意识到不太对劲,好像多去掉了些是,于是究极懒人版2.0!
(用完后注意要把整个函数段全部按u成全部未定义,再c再p,就可以完全复原,要不然ida会部分解析成其他函数)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| st = 0x00401460 end = 0x00401630
def patchNop(start, end): for i in range(start, end): ida_bytes.patch_byte(i, 0x90)
def nextInstr(addr): return addr + idc.get_item_size(addr)
addr = st while ( addr < end ): next = nextInstr(addr) addnext = idc.get_operand_value(next, 0)
if idc.print_insn_mnem(next) == "call" and idc.print_insn_mnem(addnext) == "add": retnext = nextInstr(addnext) addr = nextInstr(retnext) patchNop(next, addr) print("Patch: %X" %next) else: addr = next
|
4012F0的函数就完全复原了,把这个用在401460上会发现也完全复原了
然后分析一下401460函数是个初始化值的函数,和我们的输入并没有什么关系,关键还是4012F0函数
0x02 反调试
于是只要动调即可,但这题是有反调试的,通过找对main的引用下断点调试,发现每次断到这
直接打开程序
选择本地,下个断点,再附加程序,直接search找到shit即可
按F9跑起来,再输入一个长度为24的字符串,跑到这按F7进去,
在这通过修改标志位过去
现在回忆一下我们需要的是什么数据
然后一个F4到这check函数
去翻下数据即可拿到这串key
0x03 GetFlag!
分析一下加密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| char __cdecl sub_4812F0(const char *a1) { int v1; int v3; int i; int v5;
v3 = 0; for ( i = 0; i < strlen(a1); i += 4 ) { v1 = __ROR4__(a1[i + 3] | (a1[i + 2] << 8) | (a1[i + 1] << 16) | (a1[i] << 24), byte_485034[i / 4]); v5 = ((v1 << 16) | (unsigned __int16)~HIWORD(v1)) ^ (1 << byte_485034[i / 4]); if ( i > 0 ) v5 ^= v3; v3 = v5; if ( v5 != dword_485018[i / 4] ) return 0; } return 1; }
|
EXP:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include <stdio.h> #include <stdlib.h>
int key[7]={ 0x3, 0x10, 0xD, 0x4, 0x13, 0xB }; unsigned int ks[6]={0x8c2c133a,0xf74cb3f6,0xfedfa6f2,0xab293e3b,0x26cf8a2a,0x88a1f279};
int main(void) { unsigned int k = 0, bk = 0; int i, j; for ( i = 5; i >= 0; i-- ) if ( i > 0 ) ks[i] ^= ks[i - 1]; for ( i = 0; i < 24; i += 4 ) { k = ks[i / 4]; k = (1 << key[i / 4]) ^ k; k = ((k >> 16)) | ((~(k << 16)) & 0xffff0000); k = ((k << key[i / 4])) | (k >> (32 - key[i / 4]));
for ( j = 0; j < 4; j++ ) printf("%c", k >> (24 - j * 8) & 0xFF); } return 0; }
|
GetFlag!