RE29_wp

#看雪2016CTF一道题的wp

在0x41ADB6处有代码对this[3] = 1;这里this的地址与后面0x41AF2E处判断的this是一样的,在窗口弹出之前,this[3] = 1;就被调用了,可见是到后面才被修改成0的,可以下内存写入断点。
​ 第一次跟踪this[3],下内存写入但是没有断下,this[3]一直等于1,通过了0x41AF2E的判断。经过几次测试,这个可能是反调试标志位,使用sharp od后一直是1,否则一直是0,程序还有调用IsDebuggerPresent,给IsDebuggerPresent下断,返回值手动改为1,之后this[3]判断时又变成0了。

​ 经过0x41AF2E处判断后,程序申请了1024字节的空间,并且复制和解码了这段数据,其实是lua脚本,(lua是一种解释性语言,Python也是这种,.lua文件需要lua解释器才能运行)

02246AE0  1B 6C 73 11 00 19 93 0D 0A 1A 0A 04 04 04 08 08  ls.?..
02246AF0  78 56 00 00 00 00 00 00 00 00 00 00 00 28 77 40  xV...........(w@
02246B00  01 0E 40 54 61 73 6B 42 65 67 69 6E 2E 6C 73 00  @TaskBegin.ls.
02246B10  00 00 00 00 00 00 00 00 02 02 07 00 00 00 08 40  ...........@
02246B20  40 80 08 C0 40 81 2C 00 00 00 08 00 00 82 2C 40  @€繞?.....?@
02246B30  00 00 08 00 80 82 26 00 80 00 06 00 00 00 04 0B  ...€?.€....
02246B40  67 5F 73 74 72 52 65 67 53 6E 04 02 20 04 13 67  g_strRegSn g
02246B50  5F 73 74 72 52 65 67 53 6E 54 6F 56 65 72 69 66  _strRegSnToVerif
02246B60  79 04 01 04 0D 75 73 65 72 52 65 67 69 73 74 65  y.userRegiste
02246B70  72 04 12 67 65 74 52 65 67 53 6E 41 66 74 65 72  rgetRegSnAfter
02246B80  43 61 6C 63 01 00 00 00 01 00 02 00 00 00 00 05  Calc........
02246B90  00 00 00 10 00 00 00 01 00 04 11 00 00 00 41 00  ..........A.
02246BA0  00 00 08 00 80 80 86 C0 40 00 A4 80 80 00 08 80  ...€€喞@.€.€
02246BB0  00 81 86 00 41 00 C6 40 40 00 A4 80 00 01 08 80  .亞.A.艪@..€
02246BC0  80 80 86 40 40 00 C6 80 40 00 1F C0 00 01 1E 00  €€咢@.苺@.?.
02246BD0  00 80 41 40 01 00 08 80 41 81 66 00 00 01 26 00  .€A@.€A乫..&.
02246BE0  80 00 07 00 00 00 13 FF FF FF FF FF FF FF FF 04  €....
02246BF0  0B 67 5F 73 74 72 52 65 67 53 6E 04 13 67 5F 73  g_strRegSng_s
02246C00  74 72 52 65 67 53 6E 54 6F 56 65 72 69 66 79 04  trRegSnToVerify
02246C10  13 66 6E 47 65 74 52 65 67 53 6E 54 6F 56 65 72  fnGetRegSnToVer
02246C20  69 66 79 04 1D 66 6E 43 61 6C 63 55 73 65 72 49  ifyfnCalcUserI
02246C30  6E 70 75 74 52 65 67 53 6E 41 66 74 65 72 45 6E  nputRegSnAfterEn
02246C40  63 13 00 04 00 00 00 00 00 00 04 01 01 00 00 00  c..........
02246C50  00 00 00 00 00 00 11 00 00 00 06 00 00 00 07 00  .............
02246C60  00 00 08 00 00 00 08 00 00 00 08 00 00 00 09 00  .............
02246C70  00 00 09 00 00 00 09 00 00 00 09 00 00 00 0A 00  ................
02246C80  00 00 0A 00 00 00 0A 00 00 00 0A 00 00 00 0B 00  ...............
02246C90  00 00 0D 00 00 00 0F 00 00 00 10 00 00 00 02 00  .............
02246CA0  00 00 0B 73 74 72 52 65 67 53 6E 49 6E 00 00 00  ..strRegSnIn...
02246CB0  00 11 00 00 00 04 69 52 63 01 00 00 00 11 00 00  ....iRc.....
02246CC0  00 01 00 00 00 05 5F 45 4E 56 00 13 00 00 00 15  ...._ENV....
02246CD0  00 00 00 01 00 02 03 00 00 00 46 00 40 00 66 00  .......F.@.f.
02246CE0  00 01 26 00 80 00 01 00 00 00 04 0B 67 5F 73 74  .&.€....g_st
02246CF0  72 52 65 67 53 6E 01 00 00 00 00 00 00 00 00 00  rRegSn.........
02246D00  03 00 00 00 14 00 00 00 14 00 00 00 15 00 00 00  ............
02246D10  01 00 00 00 0B 73 74 72 52 65 67 53 6E 49 6E 00  ...strRegSnIn.
02246D20  00 00 00 03 00 00 00 01 00 00 00 05 5F 45 4E 56  ........._ENV
02246D30  07 00 00 00 01 00 00 00 02 00 00 00 10 00 00 00  ............
02246D40  05 00 00 00 15 00 00 00 13 00 00 00 15 00 00 00  ............
02246D50  00 00 00 00 01 00 00 00 05 5F 45 4E 56 00 00 00  ......._ENV...
02246D60  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02246D70  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02246D80  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02246D90  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02246DA0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02246DB0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02246DC0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02246DD0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02246DE0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02246DF0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02246E00  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02246E10  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02246E20  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02246E30  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02246E40  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02246E50  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02246E60  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02246E70  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02246E80  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02246E90  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02246EA0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02246EB0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
02246EC0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

​ 将上述数据使用LordPe转储内存,然后存到dumped.lua文件里,再用下面这段数据替换掉上面这段的头部分(修复lua的文件头),下面这段数据比原来多一个字节,需要插入。

1B 4C 75 61 53 00 19 93 0D 0A 1A 0A 04 04 04 08
08 78 56 00 00 00 00 00 00 00 00 00 00 00 28 77
40 01 0E 40 54 61 73 6B 42 65 67 69 6E 2E 6C 73
00 00 00 00 00 00 00 00 00 02 02 07 00 00 00 08
40

​ 然后使用luadec.exe将修复后的文件输出,用编译器打开就能看到下面的lua脚本

-- Decompiled using luadec 2.2 rev:  for Lua 5.3 from https://github.com/viruscamp/luadec
-- Command line: dumped2.lua 

-- params : ...
-- function num : 0 , upvalues : _ENV
g_strRegSn = " "											-->定义了两个字符串
g_strRegSnToVerify = ""										-->看名字就知道干什么用的
userRegister = function(strRegSnIn)
  -- function num : 0_0 , upvalues : _ENV
  local iRc = -1
  g_strRegSn = strRegSnIn
  g_strRegSnToVerify = fnGetRegSnToVerify()					-->应该是获取加密后的sn码
  g_strRegSn = fnCalcUserInputRegSnAfterEnc(g_strRegSn)		-->输入的字符串
  if g_strRegSn == g_strRegSnToVerify then
    iRc = 1024												-->对比成功返回1024
  end
  g_strRegSnToVerify = ""									-->否则返回-1
  return iRc
end

getRegSnAfterCalc = function(strRegSnIn)
  -- function num : 0_1 , upvalues : _ENV
  return g_strRegSn
end

后面对比成功返回1024,在0x41B07A处就会判断是不是等于1024,等于就显示注册成功。

​ 上面的fnGetRegSnToVerify()和fnCalcUserInputRegSnAfterEnc()函数都是c函数,而lua解释器需要调用luaD_precall函数来使用c函数,由于没有针对lua的sig文件,ida无法快速识别出来lua,只能人肉搜索。通过查看lua解释器的源码,里面有一个switch判断值是否为6,22,38这三个数,根据此特征找到sub_409DAD,经过比对确认是luaD_precall函数。

​ luaD_precall函数会先判断需要的函数是不是c函数,c函数对应的值是22,然后跳转过去执行c函数。od动调先弹出对话框随便输入一个长16字节的字符串,然后在0x409DAD处下断点再回车,经过两次断下(判断出是lua函数,不是目标),第三次跳到c函数,确认是fnGetRegSnToVerify,位置在sub_4019A2,用ida可以找到此函数,里面还调用了一个函数,返回的字符串存在0x42D244,长32字节

0042D244  A4 47 98 0C 9E 40 D7 F6 EB 76 6E 6D 7E A3 3E EB  ?濦做雟nm~??
0042D254  D5 51 30 06 7D C0 FB 6C C2 7A 43 C5 A4 C9 B1 FD  誕0}利l聑C扭杀?

​ 再F9一次,又断下来到了fnCalcUserInputRegSnAfterEnc,位置在sub_4019C7,用ida找过去看了一会,再回到od继续调试,跟进这个函数。经过分析,这里使用的是对称加密RC5或者RC6,在里面调用的sub_405BB6返回的值指向的就是输入字符串加密后得密文,尝试将上面得到的32字节替换密文,经过sub_41B270解码得到下面字符串。

02376C20  4B 7D 6F 22 BD EA 61 C3 0B E7 B2 D9 2C 6B 41 88  K}o"疥a?绮?kA?
02376C30  5D 71 27 85 BA 71 F0 B9 23 77 28 6C FC 36 A6 D0  ]q'吅q鸸#w(l?π

​ 在前面还有一个sub_403097对输入字符串加密了一次(16位原文变成32位字节密文),如果是正确flag输入,那么经过它加密后就是上面这个字符串了,由于不是对称加密,不能用刚才的方式解码了,需要逆算法。

​ 算法概述:将输入的16字节加密扩展成32字节,有一个table码表,此表在调用加密函数之前由sub_403438函数生成而且是固定的,大小为36个unsigned int整型(一共144字节);加密函数内部有两个循环外层循环2次,内层循环16次,主要在内层将字符串的16字节当成4个整型,调换顺序然后使用旋转循环移位的方法加异或的办法进行加密,也就是一次加密16字节,而后面扩展的32字节里,后16位是没有内容的,所以可以只解码前面16字节。以下是对照着写出来的加密函数。

unsigned int table[37] = {0x5bf76637,
	0x4748da7a,0xa44342fc,0xb2727654,0xd3f9119f,
	0x8CF04F52,0x446564BE,0xB4D40A2E,0x02966467,
	0xA3F2BAA5,0x89D93040,0xDC4B368C,0x454D2FAB,
	0xC40795D7,0xE198FD3B,0x2F7D2F02,0x3709AADB,
	0xAC882BD2,0x20559BF5,0x69B501F6,0xA9D14F98,
	0xDC9E4070,0x1FB97D2B,0x14A0B21F,0xEAE1495F,
	0xD8411E50,0x94D622EA,0xCD568FE5,0x32106336,
	0x09F7F01F,0x5E5CF6D9,0xBE2F25A0,0x2E9C6392,
	0xBBEA6DD1, 0x7FAF076D, 0x9BD7FA4C,00
	};												//码表

	int limit_5bit(const int &num)					//取低5位bit返回
	{
		return num & 0x1F;
	}

	unsigned int __ROL__(unsigned int val, char count)	//旋转循环左移
	{
		count %= 32;
		return (val << count) | (val >> (32 - count));
	}

	unsigned int __ROR__(unsigned int val, char count)	//旋转循环右移
	{
		count %= 32;
		return (val >> count) | (val << (32 - count));
	}

	unsigned int encode(unsigned int* Intstr)			//加密函数
	{
		unsigned int i = 0,j = 0;			//j用来给里面的循环计数
		unsigned int movStop = 0;			//移动位数
		unsigned int temp = 0, temp1 = 0, moveNumber1 = 0,moveNumber2 = 0;	//用来转移数据
		unsigned int* enStr = Intstr;		//指向要加密的字符串
		unsigned int* table_ = nullptr;		//循环开始时指向table[33]的指针
		for (i = 0; i < 32;i += 16)
		{
			enStr = (unsigned int*)((int)Intstr + i);
			table_ = &table[33];
			enStr[0] -= table[34];
			enStr[2] -= table[35];

			for(j = 16;j >= 1;j--)
			{
				temp = enStr[3];
				enStr[3] = enStr[2];
				enStr[2] = enStr[1];
				temp1 = enStr[0];
				enStr[0] = temp;
				enStr[1] = temp1;			//整体右移,将最后的一个int插入到前面补上

				moveNumber1 = __ROL__(temp1 * (2 * temp1 + 1), 5);
				temp = enStr[3];
				moveNumber2 = __ROL__(temp * (2 * temp + 1), 5);
				movStop = limit_5bit(moveNumber2);
				enStr[0] = moveNumber1 ^ __ROR__(enStr[0] - *(table_ - 1), movStop);
				
				movStop = limit_5bit(moveNumber1);
				enStr[2] = moveNumber2 ^ __ROR__(enStr[2] - *table_, movStop);
				table_ -= 2;

			}
			
			enStr[1] -= table[0];
			enStr[3] -= table[1];
		}
		return 1;		//加密成功返回1
	}

将字符串地址输入函数即可加密,对应着写出解密函数就能得到flag。逆算法为

unsigned int decode(unsigned int* Outstr)
	{
		unsigned int movStop = 0;
		unsigned int i = 0, j = 0, moveNumber1 = 0, moveNumber2 = 0, temp = 0, temp1 = 0;
		unsigned int* deStr = Outstr;
		unsigned int* table_ = nullptr;

		for (i = 0; i < 32; i += 16)
		{
			deStr += i / 4;
			deStr[1] += table[0];
			deStr[3] += table[1];
			table_ = table;

			for (j = 16; j >= 1; j--)
			{
				table_ += 2;
				temp = deStr[3];
				temp1 = deStr[1];
				moveNumber1 = __ROL__(temp1 * (2 * temp1 + 1), 5);
				moveNumber2 = __ROL__(temp * (2 * temp + 1), 5);

				movStop = limit_5bit(moveNumber2);
				deStr[0] = __ROL__(deStr[0] ^ moveNumber1, movStop) + table_[0];

				movStop = limit_5bit(moveNumber1);
				deStr[2] = __ROL__(deStr[2] ^ moveNumber2, movStop) + table_[1];

				temp = deStr[0];
				deStr[0] = deStr[1];
				deStr[1] = deStr[2];
				deStr[2] = deStr[3];
				deStr[3] = temp;			//将数据归位
			}
			deStr[0] += table[34];
			deStr[2] += table[35];
		}
		return 1;
	}
-->stK5CKpBsw7TPF45			//flag
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值