亮点是十进制大数类(自带数据隐藏机制、校验阶段的特定调试检测),
校验算法主要是大数的幂结果特征(检验中也引入调试校验,而是时间差,致使限制在低次幂如平方的有效校验)
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虚拟机自动化脱壳的方法