首页
社区
课程
招聘
[原创]看雪 2017 CTF 第二题 RandDigit (imul9)^2
2017-6-5 16:16 5222

[原创]看雪 2017 CTF 第二题 RandDigit (imul9)^2

HHHso 活跃值
22
2017-6-5 16:16
5222

亮点是十进制大数类(自带数据隐藏机制、校验阶段的特定调试检测),

校验算法主要是大数的幂结果特征(检验中也引入调试校验,而是时间差,致使限制在低次幂如平方的有效校验)

main函数规定ikey长度为[8,20]

要求(ikey*9)的n次幂的结果的中心位数值等于ikey[0],高位等于ikey[1:iken.len-1],且低位数字关于中心位与高位对称。

例如 ikey=12345679,(ikey*9)的平方为,12345678987654321,其各位数值满足上述条件 1234567 8-9-8 7654321

通过下述python代码,可以在短时间内(1秒左右)得到高位数匹配的ikey数集,

并得到上述的ikey=12345679为有效注册key,输入顺序低位在前,即 key = 97654321

#------- ------- ------- ------- ------- ------- -------

def eln(L=8,n=2):

  f = sum([pow(10,r) for r in range(0,L)])+1

  t = int((pow(pow(10,2*(L+1)-1),0.5)/9))

  print("---"*3,"len:{}, n:{} from:{} to:{}".format(L,2,f,t),"---"*3)

  for i in range(f,t,s):

    istr = "{:d}".format(i)

    m92str = "{:d}".format(int(pow(i*9,n)))

    if istr[:istr.__len__()-1]==m92str[:istr.__len__()-1]:

      print(istr[:istr.__len__()-1],m92str[:istr.__len__()-1],istr,m92str)

for L in range(8,21):

  eln(L)

#------- ------- ------- ------- ------- ------- -------

高位数匹配的ikey数集输出结果

--------- len:8, n:2 from:11111112 to:35136418 ---------

1234567 1234567 12345675 12345670987655625

1234567 1234567 12345676 12345672987655056

1234567 1234567 12345677 12345674987654649

1234567 1234567 12345678 12345676987654404

1234567 1234567 12345679 12345678987654321  #----------------------命中满足所有校验条件的key

1234568 1234568 12345680 12345680987654400

1234568 1234568 12345681 12345682987654641

1234568 1234568 12345682 12345684987655044

1234568 1234568 12345683 12345686987655609

1234568 1234568 12345684 12345688987656336

--------- len:9, n:2 from:111111112 to:351364184 ---------

12345678 12345678 123456786 1234567818765433476

12345678 12345678 123456787 1234567838765432889

12345678 12345678 123456788 1234567858765432464

12345678 12345678 123456789 1234567878765432201

12345679 12345679 123456791 1234567918765432161

12345679 12345679 123456792 1234567938765432384

12345679 12345679 123456793 1234567958765432769

12345679 12345679 123456794 1234567978765433316

攻击过程

Ctrl+R,cmd执行exe程序样例,随机输入如1111111111回车,

得到提示信息"WRONG KEY...",

上IDA,SubView|String 索引到关联业务逻辑,实际是main函数中

.text:00401231 push    offset aWrongKey___ ; "WRONG KEY...\n"

.text:00401236 call    Hi_printf_sub_401BE0

.text:0040123B add     esp, 4

.text:0040123E loc_40123E:                             ;

.text:0040123E lea     ecx, [esp+4138h+loc_RandDigitNkey_xkey]

.text:00401245 mov     [esp+4138h+var_4], 0FFFFFFFFh

.text:00401250 call    Hi_RandDigitN_dtor_sub_401390

.text:00401255 jmp     short loc_401288

.text:00401257 loc_401257:                             ;

.text:00401257 push    offset aWellDone ; "WELL DONE!\n"

.text:0040125C call    Hi_printf_sub_401BE0

.text:00401261 add     esp, 4

.text:00401264 lea     ecx, [esp+4138h+loc_RandDigitNkey_ikey]

.text:0040126B mov     byte ptr [esp+4138h+var_4], 0

.text:00401273 call    Hi_RandDigitN_dtor_sub_401390

整个判断逻辑在main函数中,其中许多自定义函数,

事实上这些自定义函数集是大数类的成员方法实现,用于实现大数运算。

对相关和所有自定义函数的全逆向分析重命名如下,大数的类定义参考RanDigitN

其中包括RanDigitN的内存布局的类成员,和下述成员函数,意义参考函数名

Hi_output_sub_4027B7 

Hi_printf_sub_401BE0  

Hi_u31_Pokemon_rand_sub_401A00 通用伪随机数产出器,随机因子为输入参数

Hi_RandDigitN_2008xrand_rand_1024_sub_4019E0  大数伪随机数产出器,随机因子为大数的随机因私xrand

Hi_RandDigitN_init_1008SRTbl_0008SRTblR_with_2008xrand_sub_401A60  根据大数的随机因子 初始化大数位权索引表和位权基数值表初始化

Hi_RandDigitN_imul_P1Nlt0A_sub_4016E0   大数与小于10的自然数相乘

Hi_RandDigitN_getRadix_forP1BitTh_sub_4013B0  获取大数相应位权的基数值

Hi_RandDigitN_getBitNum_sub_4013A0  获取大数的位长

Hi_RandDigitN_dtor_sub_401390    大数类析构函数

Hi_RandDigitN_ctor_lpszN_sub_401350   大数构造函数,重构初始化输入值为字符串表示的大数

Hi_RandDigitN_ctor_N_sub_401310       大数构造函数,重构初始化输入值为自然数

Hi_RandDigitN_ctor_1008SRTbl_0008SRTblR_with_CurTickCountAs2008xrandsub_4012C0  产生新的大数随机因子 并初始化大数位权索引表和位权基数值表初始化

Hi_RandDigitN_cmp_P1iRandDigitN_P2isPos_P3rsPos_P4cmpLen_P5bTailOrHead_sub_4013E0 比较两大数的特定位置特定长度的位权基数,可选比较大数的高位还是低位

Hi_RandDigitN_carry_sub_401970         大数进位操作,在执行大数加或乘等运算后需要处理进位

Hi_RandDigitN_assign_lpszN_sub_4014E0       大数赋值 输入值为字符串表示的大数

Hi_RandDigitN_assign_RandDigitN_sub_401540  大数赋值 输入值为大数

Hi_RandDigitN_assign_P1N_sub_4014A0         大数赋值 输入值为自然数

Hi_RandDigitN_add_RandDigitN_sub_4015E0     大数加运算 输入值为大数

Hi_RandDigitN_add_P1INT_sub_401580          大数加运算 输入值为自然数

Hi_RandDigitN_ShiftUp_P1Nbit_sub_401660     大数向上移位运算 输入值为移位个数 如 1234 向上移动一位 12340,移动两位123400

Hi_RandDigitN_EQ_IMUL_anyN_sub_401730       大数乘运算 输入值为自然数

Hi_RandDigitN_EQ_IMUL_RandDigitN_sub_401840 大数乘运算 输入值为大数

#define RAND_DIGIT_N_MAX_BIT_LENGTH 1024

//1024   is 0x400

//1024-1 is 0x3FF

public RandDigitN{

  private:

    unsigned int mBitLength; //this.04hww

    unsigned int RandBitTable[RAND_DIGIT_N_MAX_BIT_LENGTH]; //this.08h cbSize:4*1024

    unsigned int BitWeightIndexInRandBitTable[RAND_DIGIT_N_MAX_BIT_LENGTH];//this.1008h cbSize:4*1024

    unsigned int xrand;//this.2008hww

    unsigned int srand;//this.200Chww

  public:

    RandDigitN::RandDigitN();//Hi_RandDigitN_ctor_1008SRTbl_0008SRTblR_with_CurTickCountAs2008xrandsub_4012C0

    RandDigitN::RandDigitN(unsigned int N);//Hi_RandDigitN_ctor_N_sub_401310  

    void RandDigitN::initialize_RandTables_with_srand()//Hi_RandDigitN_init_1008SRTbl_0008SRTblR_with_2008xrand_sub_401A60

    unsigned int RandDigitN::get_rand();//Hi_RandDigitN_2008xrand_rand_1024_sub_4019E0

    void RandDigitN::eq_imul(RandDigitN);//Hi_RandDigitN_EQ_IMUL_RandDigitN_sub_401840  eg. objN *= objM; is euqal to objN = objN * objM;

    void RandDigitN::eq_imul(unsigned int N);//Hi_RandDigitN_EQ_IMUL_anyN_sub_401730

    void RandDigitN::ShiftUp(unsigned int bitNum);//Hi_RandDigitN_ShiftUp_P1Nbit_sub_401660  eg. objN *= pow(10,bitNum)

    void RandDigitN::add(RandDigitN ins);//Hi_RandDigitN_add_RandDigitN_sub_4015E0 

    void RandDigitN::assign(unsigned int N);//Hi_RandDigitN_assign_P1N_sub_4014A0  

    void RandDigitN::assign(RandDigitN ins);//Hi_RandDigitN_assign_RandDigitN_sub_401540 

    void RandDigitN::assign(char* lpszN);//Hi_RandDigitN_assign_lpszN_sub_4014E0 

    void RandDigitN::cmp(RandDigitN ins,

      unsigned int ins_startPos,unsigned int self_startPos,

      unsigned int cmp_length,bool bTailOrHead);//Hi_RandDigitN_cmp_P1iRandDigitN_P2isPos_P3rsPos_P4cmpLen_P5bTailOrHead_sub_4013E0 

    ~RandDigitN();//Hi_RandDigitN_dtor_sub_401390   

    unsigned int RandDigitN::get_mBitLength();//Hi_RandDigitN_getBitNum_sub_4013A0

    unsigned int RandDigitN::getBitRadix(unsigned int BitWeightIndex);// Hi_RandDigitN_getRadix_forP1BitTh_sub_4013B0 eg. RandDigitN(9687).getBitRadix(1) is 8

    void RandDigitN::eq_imul(unsigned char littleN);//Hi_RandDigitN_imul_P1Nlt0A_sub_4016E0 

    void RandDigitN::add(unsigned int N);//Hi_RandDigitN_add_P1INT_sub_401580 

    void RandDigitN::carry();//Hi_RandDigitN_carry_sub_401970 call after all,imul,etc. operator to ajust the bit-weight-radix

}

public RandDigitN::RandDigitN(){

  //Hi_RandDigitN_ctor_1008SRTbl_0008SRTblR_with_CurTickCountAs2008xrandsub_4012C0

  this.srand = GetTickCount();

  this.xrand = this.srand;

  initialize_RandTables_with_srand();

}

public RandDigitN::initialize_RandTables_with_srand(){

  //Hi_RandDigitN_init_1008SRTbl_0008SRTblR_with_2008xrand_sub_401A60

  int i,t,r;

  //

  for(i = 0;i < RAND_DIGIT_N_MAX_BIT_LENGTH;i++){

    this.BitWeightIndexInRandBitTable[i] = i & (RAND_DIGIT_N_MAX_BIT_LENGTH-1);

  }

  //randing BitWeightIndexInRandBitTable 使得位权基数索引表随机化

  //大数RandDigitN的第i位(从低到高)的基数值存储在RandBitTable[BitWeightIndexInRandBitTable[i]]中

  //即大数RandDigitN的各位上的基数值在RandBitTable表中随机存储,而不是我们常见的顺序存储

  for(i = 0;i < RAND_DIGIT_N_MAX_BIT_LENGTH;i++){

    t = this.BitWeightIndexInRandBitTable[i];

    r = get_rand();

    this.BitWeightIndexInRandBitTable[i] = r;

    this.BitWeightIndexInRandBitTable[r] = t;

  }

  //

  for(i = 0;i < RAND_DIGIT_N_MAX_BIT_LENGTH;i++){

    this.RandBitTable[i] = this.BitWeightIndexInRandBitTable[RAND_DIGIT_N_MAX_BIT_LENGTH-i];

  }

}

public RandDigitN::get_rand(){

  //Hi_RandDigitN_2008xrand_rand_1024_sub_4019E0

  return Hi_u31_Pokemon_rand_sub_401A00(&this.xrand)&0x3FF;

}

// friendly function 

unsigned int Hi_u31_Pokemon_rand_sub_401A00(unsigned int* pxrand){

  //while call this function Pokemon? 

  //just try to search the const num 0x41C64E6D on the websize.

  unsigned int xrand,xrand_high_11bit,xrand_midlle_10bit,xrand_low_10bit;

  xrand = *pxrand;

  xrand = (xrand * 0x41C64E6D) + 0x3039; //use higher 11bit

  xrand_high_11bit = xrand;

  xrand = (xrand * 0x41C64E6D) + 0x3039; //use midlle 10bit

  xrand_midlle_10bit = xrand;

  xrand = (xrand * 0x41C64E6D) + 0x3039; //use lower  10bit

  xrand_low_10bit = xrand;

  *pxrand = xrand; //update the xrand

  xrand_high_11bit = (xrand_high_11bit >> 0x10) & 0x7FF; //get the 11 bits

  xrand_midlle_10bit = (xrand_midlle_10bit >> 0x10) & 0x3FF;//get the 10 bits

  xrand_low_10bit = (xrand_low_10bit >> 0x10) & 0x3FF;//get the 10 bits

  return ((xrand_high_11bit<<20) | (xrand_midlle_10bit<<10) | (xrand_low_10bit));

}

main函数规定ikey长度为[8,20]

main函数的校验逻辑如下

//------- ------- ------- ------- -------

bOK = True

ikey

xkey0 = ikey*9

xkey = xkey0

while True:

  xkey *= xkey

  if xkey.len % 2 == 0: //要求xkey=(ikey*9)^n的幂位数为奇数,即关于中心数字对称

    continue

  if xkey[xkey.len/2]!=ikey[0]: //要求xkey=(ikey*9)^n的幂中心数字等于ikey最低位

    continue

  for i = 1..(ikey.len-1):

    if xkey[xkey.len-(ikey.len-1)-1+i]!=ikey[i]: //要求xkey=(ikey*9)^n的幂高位等于ikey除最低位之外的高位数,

      bOK = False                                //如ikey=12345679,则要求xkey最高位数字为1234567

      break

    if xkey[xkey.len-1-i]!=ikey[i]: //要求xkey=(ikey*9)^n的幂低位等于ikey除低位数之外的高位数的反序

      bOK = False                   //如ikey=12345679,则要求xkey低位数字为7654321

      break

  if bOK:

    "WELL DONE!\n"

//------- ------- ------- ------- -------

简单描述校验算法为:

要求(ikey*9)的n次幂的结果的中心位数值等于ikey[0],高位等于ikey[1:iken.len-1],且低位数字关于中心位与高位对称。

例如 ikey=12345679,(ikey*9)的平方为,12345678987654321,其各位数值满足上述条件 1234567 8-9-8 7654321

ikey.len 取值范围 8..20

幂指数n取值大于2, 2..inf

xkey = xkey0 = ikey*9

(1)设xkey.len=8,n=2

  八位数最小为 10000000 = pow(10,xkey.len-1),其平方为15位数(奇数位数符合测试条件)

  当八位数大于 31622776 = int(pow(pow(10,xkey.len*2-1),0.5))时,其平方为16位数(不满足测试条件,会使得幂指数n递增,超出设定讨论条件n=2)

  

  即此时,要求ikey=xkey/9 = [pow(10,xkey.len-1),31622776/9],将致使ikey.len < 8, 则xkey.len 应 > 8

(2)设xkey.len=9,n=2,同理

  九位数最小为 100000000 = pow(10,xkey.len-1),其平方为17位数(奇数位数符合测试条件)

  当九位数大于 316227766 = int(pow(pow(10,xkey.len*2-1),0.5))时,其平方为16位数(不满足测试条件,会使得幂指数n递增,超出设定讨论条件n=2)

  

  即此时,要求ikey=xkey/9 = [100000000/9,316227766/9] = [11111111.111111112,35136418.44444445], 上下取整为=[11111112,35136418]

  即得到ikey.len=8时的枚举区间[11111112,35136418]

  

(3)以此类推,可通用化,得到不同长度ikey.len=L时,ikey的枚举区间

#------- ------- ------- ------- ------- ------- -------

def eln_zone(L=8,n=2):

  f = sum([pow(10,r) for r in range(0,L)])+1

  t = int((pow(pow(10,2*(L+1)-1),0.5)/9))

  print("---"*3,"len:{}, n:{} from:{} to:{}".format(L,2,f,t),"---"*3)

for L in range(8,21):

  eln_zone(L)

#------- ------- ------- ------- ------- ------- -------

('---------', 'len:8, n:2 from:11111112 to:35136418', '---------')

('---------', 'len:9, n:2 from:111111112 to:351364184', '---------')

('---------', 'len:10, n:2 from:1111111112 to:3513641844', '---------')

('---------', 'len:11, n:2 from:11111111112 to:35136418446', '---------')

('---------', 'len:12, n:2 from:111111111112 to:351364184463', '---------')

('---------', 'len:13, n:2 from:1111111111112 to:3513641844631', '---------')

('---------', 'len:14, n:2 from:11111111111112 to:35136418446315', '---------')

('---------', 'len:15, n:2 from:111111111111112 to:351364184463153', '---------')

('---------', 'len:16, n:2 from:1111111111111112 to:3513641844631532', '---------')

('---------', 'len:17, n:2 from:11111111111111112 to:35136418446315328', '---------')

('---------', 'len:18, n:2 from:111111111111111112 to:351364184463153280', '---------')

('---------', 'len:19, n:2 from:1111111111111111112 to:3513641844631532544', '---------')

('---------', 'len:20, n:2 from:11111111111111111112 to:35136418446315327488', '---------')

所以可设计上述python枚举代码得到相应的结果,

算法只匹配高位条件(忽略中位数和低位)出于运行效率和简约考虑,也可以在高位集合条继续添加检测,如

def eln(L=8,n=2):

  f = sum([pow(10,r) for r in range(0,L)])+1

  t = int((pow(pow(10,2*(L+1)-1),0.5)/9))

  print("---"*3,"len:{}, n:{} from:{} to:{}".format(L,2,f,t),"---"*3)

  for i in range(f,t,s):

    istr = "{:d}".format(i)

    m92str = "{:d}".format(int(pow(i*9,n)))

    if istr[:istr.__len__()-1]==m92str[:istr.__len__()-1]:

      if 满足中位数条件:

        if 满足低位数条件:

          print(istr[:istr.__len__()-1],m92str[:istr.__len__()-1],istr,m92str)

  

  

MORE?

在下述大数比较成员函数中

    void RandDigitN::cmp(RandDigitN ins,

      unsigned int ins_startPos,unsigned int self_startPos,

      unsigned int cmp_length,bool bTailOrHead);//Hi_RandDigitN_cmp_P1iRandDigitN_P2isPos_P3rsPos_P4cmpLen_P5bTailOrHead_sub_4013E0 

.text:00401477 mov     eax, [ikey+200Ch]

.text:0040147D mov     esi, [xkey+200Ch]

.text:00401483 mov     ecx, [esp+0Ch+loc_statu_should_be_0]

.text:00401487 xor     eax, esi

.text:00401489 shr     eax, 7

.text:0040148C pop     edi

.text:0040148D add     eax,ecx

比对成果要求((ikey.srand ^ xkey.srand)>>7 + statu)==0,

即要求 ikey.srand == xkey.srand

.text:004010B3 call    Hi_RandDigitN_ctor_1008SRTbl_0008SRTblR_with_CurTickCountAs2008xrandsub_4012C0 //init xkey.srand=GetTickCount()

.text:004010B8 lea     ecx, [esp+4138h+loc_key]

.text:004010BC mov     [esp+4138h+var_4], 0

.text:004010C7 push    ecx

.text:004010C8 lea     ecx, [esp+413Ch+loc_RandDigitNkey_xkey]

.text:004010CF call    Hi_RandDigitN_assign_lpszN_sub_4014E0

.text:004010D4 lea     ecx, [esp+4138h+loc_RandDigitNkey_xkey]

.text:004010DB call    nullsub_1

.text:004010E0 push    9

.text:004010E2 lea     ecx, [esp+413Ch+loc_RandDigitNkey_xkey]

.text:004010E9 call    Hi_RandDigitN_EQ_IMUL_anyN_sub_401730

.text:004010EE lea     ecx, [esp+4138h+loc_RandDigitNkey_xkey]

.text:004010F5 call    nullsub_1

.text:004010FA loc_4010FA: 

.text:004010FA lea     edx, [esp+4138h+loc_key]

.text:004010FE lea     ecx, [esp+4138h+loc_RandDigitNkey_ikey]

.text:00401105 push    edx

.text:00401106 call    Hi_RandDigitN_ctor_lpszN_sub_401350 //init ikey.srand=GetTickCount()

即要求 xkey.srand=GetTickCount() 与 ikey.srand=GetTickCount() 之间不允许有任何导致GetTickCount差异的操作如断点,或者过高次幂计算

停顿断点或者高次幂运算都会使得ikey.srand和xkey.srand不同而校验不通过(即使满足数字特征)。



[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

收藏
点赞1
打赏
分享
打赏 + 1.00雪花
打赏次数 1 雪花 + 1.00
 
赞赏  CCkicker   +1.00 2017/06/06
最新回复 (8)
雪    币: 187
活跃值: (551)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
Loopher 2017-6-5 22:50
2
0
确实好帖,大牛厉害了~这个脚本会不会跑死我的电脑=_=||
雪    币: 459
活跃值: (334)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
木瓜枫叶 2 2017-6-6 09:43
3
0
跪舔膝盖
雪    币: 287
活跃值: (51)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
枫凌 2017-6-6 10:13
4
0
膜拜
雪    币: 6103
活跃值: (1207)
能力值: (RANK:30 )
在线值:
发帖
回帖
粉丝
CCkicker 2017-6-6 10:26
5
0
雪    币: 7300
活跃值: (3758)
能力值: (RANK:1130 )
在线值:
发帖
回帖
粉丝
海风月影 22 2017-6-6 14:36
6
0
全是Python大神啊
雪    币: 1412
活跃值: (4209)
能力值: ( LV13,RANK:240 )
在线值:
发帖
回帖
粉丝
IamHuskar 4 2017-6-6 15:10
7
0
海风月影 全是Python大神啊
惊现大牛。紧紧地抱住大腿舔。舔到骨折为止。
雪    币: 7300
活跃值: (3758)
能力值: (RANK:1130 )
在线值:
发帖
回帖
粉丝
海风月影 22 2017-6-6 18:51
8
0
IamHuskar 惊现大牛。紧紧地抱住大腿舔。舔到骨折为止。
雪    币: 598
活跃值: (282)
能力值: ( LV13,RANK:330 )
在线值:
发帖
回帖
粉丝
Fpc 4 2017-6-8 09:20
9
0
python大神
游客
登录 | 注册 方可回帖
返回