1、工具:IDA、Python、RSATool2v17.exe、BigInt.exe
2、密码学知识:RSA、AES、MD5、SHA512
假设用户输入name、sn
nameHash = MD5(SHA512(name+'EFBEEDDE'))
nameHash =
MD5(SHA512(
nameHash
+'9E3779B9'))
【 EFBEEDDE 、 9E3779B9 实际运算需要转置】【摘要长度0x10】
middleHash = AES( sn )
snHash = RSA(
middleHash
)
【对称密钥的解密办法就在程序里,修改参数即可获取】
【RSA是256bit,必须进行暴力破解,大概20分钟以内】
【摘要长度0x10】
以上是软件验证思路的简要过程。里面涉及到细节会在下面两个章节中进行阐述。
那么逆向软件验证思路,即可构造出合法的用户名及序列号
发现有字符串“Enter your name:”
使用IDA加载软件,View->Open subviews->Strings,或者Shift+F12,打开String视图
Ctrl+F搜索上述发现的字符串,双击定位变量处,按X键跳转到代码处,按F5键,即可生成伪代码
int __cdecl main(int argc, const char **argv, const char **envp) { time_t v3; // eax unsigned int lenName; // kr04_4 int result; // eax char Name; // [esp+8h] [ebp-12Ch] char v7; // [esp+9h] [ebp-12Bh] __int16 v8; // [esp+69h] [ebp-CBh] char v9; // [esp+6Bh] [ebp-C9h] char Sn; // [esp+6Ch] [ebp-C8h] char v11; // [esp+6Dh] [ebp-C7h] __int16 v12; // [esp+131h] [ebp-3h] char v13; // [esp+133h] [ebp-1h] Name = 0; Sn = 0; memset(&v7, 0, 0x60u); v8 = 0; v9 = 0; memset(&v11, 0, 0xC4u); v12 = 0; v13 = 0; v3 = time(0); sub_411AD8(v3); sub_411A90(aEnterYourName); scanf(aS, &Name); lenName = strlen(&Name) + 1; if ( lenName - 1 < 3 || lenName - 1 > 0x14 ) // 用户名长度在3-20之间 { sub_411A90(aBadName); result = -1; } else { sub_411A90(aEnterYourSn); scanf(aS, &Sn); if ( strlen(&Sn) == 64 ) // 序列号是64位 { sub_401380((int)&Name, lenName - 1, (int)&Sn, 0x40);// 关键验证代码 result = 0; } else { sub_411A90(aBadSn); result = -1; } } return result; }
从中找到关键方法:sub_401380,双击进入
void __cdecl sub_401380(int name, unsigned int lenName, int sn, int num64) { char nameHash; // [esp+4h] [ebp-70h] char returnValueB; // [esp+14h] [ebp-60h] char v6; // [esp+15h] [ebp-5Fh] char v7; // [esp+23h] [ebp-51h] char snHash; // [esp+24h] [ebp-50h] char intSN; // [esp+34h] [ebp-40h] char returnValueA; // [esp+54h] [ebp-20h] if ( lenName >= 3 && lenName <= 0x14 && num64 == 64 ) { if ( myAtoi((char *)sn, 64, &intSN) != 0x20 // 字符串转16进制整数:即'123456'会转为0x123456 || (sub_4010F0( (int)&intSN, 0x20, (int)&returnValueA, (int)&unk_4190D0, 0x80, 0), // 序列号:对称加密方法 sub_401210( (int)&returnValueA, 32, (int)&returnValueB), // 序列号:RSA加密方法 returnValueB) || v6 != 2 || v7 ) { sub_411A90(aBadSn); // 错误 } else { sub_401190((const void *)name, lenName, (int)&nameHash);// 用户名:生成摘要 if ( !memcmp(&nameHash, &snHash, 0x10u) ) // 比较16个字节 sub_411A90(aCongratulation); // 正确 } } }
(1)、处理sn生成Hash,同时会进行返回值验证
myAtoi:将sn字符串转成对应的十六进制大整数,输出为256位整数A
sub_4010F0:实施对称加密,输出为256位整数B
sub_401210:实施RSA加密,输出为256位整数C,其中后128位,即0x10个字节为snHash值
但是对C有三个判断语句:
第0个字节等于00:对应于 returnValueB
第1个字节等于02:对应于 v6 != 2
第F个字节等于00:对应于 v7
C的结构如:0002【13个字节,随机】00 + snHash
群里大佬提到的多解,估计问题出在这儿,作者应该锁定13个字节,或者再加一道判断语句,基本上就能限定解的唯一性。
sn--->Hash:如果破解了RSA,基本上就能进行逆操作。Hash--->sn
以下是该步骤的相关代码,以及判断的思路、验证操作等
myAtoi: 相对简单,可以在调试时进行验证
int __cdecl myAtoi(char *sn, int num64, char *a3) { char *v3; // ebp int v4; // esi int result; // eax char *v6; // edi signed int i; // edx unsigned __int8 v8; // cl char v9; // cl if ( !sn ) return 0; if ( num64 % 2 ) return 0; v3 = a3; if ( !a3 ) return 0; v4 = 0; result = num64 / 2; if ( num64 / 2 > 0 ) { v6 = sn; do { LOWORD(sn) = *(_WORD *)v6; i = 0; do { v8 = *((_BYTE *)&sn + i); if ( v8 < 0x30u || v8 > 0x39u ) // 非数字 { if ( v8 < 0x61u || v8 > 0x66u ) // 非小写字母 { if ( v8 < 0x41u || v8 > 0x46u ) // 非大写字母 return 0; v9 = v8 - 0x37; // 大写字母:转16进制 } else { v9 = v8 - 0x57; // 小写字母:转16进制 } } else { v9 = v8 - 0x30; // 数字转16进制 } *((_BYTE *)&sn + i++) = v9; } while ( i < 2 ); // 一次处理两个字符 v6 += 2; v3[v4++] = BYTE1(sn) & 0xF | 16 * (_BYTE)sn; } while ( v4 < result ); } return result; }
sub_4010F0:实施对称加密,输出为256位整数B ,这个相对麻烦
首先观察两处疑点:
最右边的参数可以取值0或1,从而进入不同的处理方法内,在sub_404FB0里也会进行判断,极度怀疑是加密函数和解密函数
0x80h在AES解密环节中很多,有时候也会出现在hash运算中。
验证操作:
先记录经过sub_4010F0处理后:intSN和returnValue的数值是多少,他们都是0x20字节长,等长,初步验证是对称加密
然后将 returnValue作为用户验证码输入,在 sub_4010F0处暂停,修改其调用函数处的参数值 int0:0改为1
执行完成之后,观察返回值,经过对比,验证了对称加密,也可以再次实施逆运算。
int __cdecl sub_4010F0(int intSN, int int20h, int returnValue, int constValue, int int80h, int int0) { int intSNCopy; // esi int v7; // ebx char middleValue; // [esp+4h] [ebp-F4h] if ( int0 == 1 ) sub_404FE0((_DWORD *)constValue, int80h, (unsigned int *)&middleValue); else sub_4053A0(constValue, int80h, (int)&middleValue);// 执行 if ( int20h / 16 <= 0 ) return int20h; intSNCopy = intSN; v7 = int20h / 16; do { sub_404FB0(intSNCopy, returnValue - intSN + intSNCopy, (int)&middleValue, int0); intSNCopy += 16; --v7; } while ( v7 ); return int20h; }
sub_401210:实施RSA加密,输出为256位整数C,其中后128位,即0x10个字节为snHash值 ;这个处理起来比较麻烦
进入函数之内:
里面有: sub_4068D0((int)v5, 0x10001);0x10001这是明显的RSA中的e值,很多RSA中的e都使用0x10001,极度怀疑这是一个RSA算法
那么如何来判断,那个是n了?既然是RSA算法,那么里面肯定有一个n,这个不可能由用户来带入,恰好V10是0x20字节长度,基本确定是一个RSA256算法,RSA256算法的加密强度太低,可以通过因式分解获取pq、进而得出ψ(n),再算出d,那么就可以做出解密算法来。
signed int __cdecl sub_401210(int a1, int int20h, int a3) { bignum_st *v4; // esi bignum_st *v5; // edi bignum_st *v6; // ebx bignum_st *v7; // ebp signed int v8; // eax _DWORD *lpMem; // [esp+0h] [ebp-24h] char v10[32]; // [esp+4h] [ebp-20h] _BYTE *int20ha; // [esp+2Ch] [ebp+8h] v10[0] = 0x69; v10[8] = 0x39; v10[31] = 0x39; v10[1] = 0x82u; v10[2] = 0x30; v10[3] = 0x28; v10[4] = 0x57; v10[5] = 0x74; v10[6] = 0x65; v10[7] = 0xABu; v10[9] = 0x91u; v10[10] = 0xDFu; v10[11] = 4; v10[12] = 0x51; v10[13] = 0x46; v10[14] = 0xF9u; v10[15] = 0x1D; v10[16] = 0x55; v10[17] = 0x6D; v10[18] = 0xEEu; v10[19] = 0x88u; v10[20] = 0x70; v10[21] = 0x84u; v10[22] = 0x5D; v10[23] = 0x8Eu; v10[24] = 0xE1u; v10[25] = 0xCDu; v10[26] = 0x3C; v10[27] = 0xF7u; v10[28] = 0x7E; v10[29] = 0x4A; v10[30] = 0xC; if ( int20h != 0x20 ) return 0; lpMem = sub_406170(); v4 = sub_406650(); // 初始化 v5 = sub_406650(); v6 = sub_406650(); v7 = sub_406650(); sub_406930((unsigned __int8 *)a1, 0x20, (LPVOID *)v7);// a1转换成大整数v7 sub_4068D0((int)v5, 0x10001); // 0x10001转换成大整数v5 sub_406930((unsigned __int8 *)v10, 0x20, (LPVOID *)v4);// v10转换成大整数v4 sub_406D30(v6, v7, v5, v4, (int)lpMem); // v6存放结果:这是RSA加解密函数 v8 = 0; int20ha = (_BYTE *)(v6->d + 0x1F); do *(_BYTE *)(++v8 + a3 - 1) = *int20ha--; while ( v8 < 0x20 ); // 将计算的结果倒序存放 sub_4065F0(v4); // 复原 sub_4065F0(v5); sub_4065F0(v6); sub_4065F0(v7); sub_4061D0(lpMem); return 0x20; }
RSA算法验证中:
m = 0x13981882F9F8B823387C6EF9D5009E9F01675D5A3719A44696336E639FCD2778 e = 0x10001 n = 0x69823028577465AB3991DF045146F91D556DEE8870845D8EE1CD3CF77E4A0C39 c = m ** e % n print('%02X'%c)
计算结果与调试结果一直:25D343CED2E5A3CD5FE94CEA15700B8D7725F7715A6A547A2BF0E8373705E
还需要补充000,即为:00025D343CED2E5A3CD5FE94CEA15700B8D7725F7715A6A547A2BF0E8373705E
也可以通过看雪中的Big Integer Calculator v1.13 带入计算,感谢readyu大神
RSA算法破解中:使用RSATool2v17.exe
对n进行因式分解
n = 0x69823028577465AB3991DF045146F91D556DEE8870845D8EE1CD3CF77E4A0C39
大约20分钟以内,分解结果:
p = 0x979BE0C9EECE7426C9FD28C2D6E7772B
q = 0xB22831D15714EB91CD83340B4837182B
计算d
d = 0x390A684CB713378FFD5CCE8C4000B5D6A2BB9F29B63D395E6BE6E9DD941527BD
【有大佬提d到 http://www.factordb.com/,经过测试,不成功。 https://www.wolframalpha.com/ 也不成功,老老实实分解中】
(2)、处理name生成Hash
signed int __cdecl sub_401190(const void *name, unsigned int lenName, int returnMD5) { char v4; // [esp+8h] [ebp-DCh] int v5; // [esp+18h] [ebp-CCh] char v6; // [esp+1Ch] [ebp-C8h] qmemcpy(&v6, name, lenName); *(int *)((char *)&v5 + lenName + 4) = dword_41D038;// v5在v6前面4个字节,v5+4相当于定位到v6,再加上lenName,也就是在用户字符串后面。实际上在用户字符串后面加上4个字节 sub_4010B0((int)&v6, lenName + 4, (int)&v4); // 结果存在v4里面,实际上是 SHA512+MD5计算摘要 v5 = dword_41D030; // v5在v4的后面,也就是在v4后面追加了4个字节 sub_4010B0((int)&v4, 20, returnMD5); return 16; }
首先在用户名后面追加4个字节,进行hash组合计算
然后在上面的结果后面再追加4个字节,进行hash组合计算
那么如何知道是hash组合了?我们接着深入到sub_4010B0
signed int __cdecl sub_4010B0(int a1, int a2, int a3) { char v4; // [esp+0h] [ebp-40h] sub_4019B0(a1, a2, &v4); // SHA512 sub_401560((int)&v4, 64, (void *)a3); // MD5 return 16; }
有两个方法:sub_4019B0计算结果存到v4; sub_401560计算结果存到a3,最后会作为hash返回值给到上层调用方法
我们继续深入到这两个方法
sub_4019B0 :里面有特征值,到此就没必要在继续跟下去了,如果对加密不太了解,可以百度搜索特征值。
_BYTE *__cdecl sub_4019B0(int a1, int a2, void *a3) { _BYTE *v3; // esi int v5; // [esp+4h] [ebp-D8h] int v6; // [esp+8h] [ebp-D4h] int v7; // [esp+Ch] [ebp-D0h] int v8; // [esp+10h] [ebp-CCh] int v9; // [esp+14h] [ebp-C8h] int v10; // [esp+18h] [ebp-C4h] int v11; // [esp+1Ch] [ebp-C0h] int v12; // [esp+20h] [ebp-BCh] int v13; // [esp+24h] [ebp-B8h] int v14; // [esp+28h] [ebp-B4h] int v15; // [esp+2Ch] [ebp-B0h] int v16; // [esp+30h] [ebp-ACh] int v17; // [esp+34h] [ebp-A8h] int v18; // [esp+38h] [ebp-A4h] int v19; // [esp+3Ch] [ebp-A0h] int v20; // [esp+40h] [ebp-9Ch] int v21; // [esp+44h] [ebp-98h] int v22; // [esp+48h] [ebp-94h] int v23; // [esp+4Ch] [ebp-90h] int v24; // [esp+50h] [ebp-8Ch] int v25; // [esp+D4h] [ebp-8h] int v26; // [esp+D8h] [ebp-4h] v3 = a3; if ( !a3 ) v3 = &unk_41FB18; v21 = 0; v22 = 0; v23 = 0; v24 = 0; v25 = 0; v5 = 0xF3BCC908; v6 = 0x6A09E667; v7 = 0x84CAA73B; v8 = 0xBB67AE85; v9 = 0xFE94F82B; v10 = 0x3C6EF372; v11 = 0x5F1D36F1; v12 = 0xA54FF53A; v13 = 0xADE682D1; v14 = 1359893119; v15 = 0x2B3E6C1F; v16 = 0x9B05688C; v17 = 0xFB41BD6B; v18 = 0x1F83D9AB; v19 = 0x137E2179; v20 = 0x5BE0CD19; v26 = 64; sub_401890(&v5, (char *)a1, a2); sub_4015D0(v3, (int)&v5); sub_407E50(&v5, 0xD8u); return v3; }
百度搜索:0xF3BCC908:即可判断是 SHA512
深入到sub_401560
void *__cdecl sub_401560(int a1, int a2, void *a3) { void *v3; // esi void *result; // eax int v5[23]; // [esp+4h] [ebp-5Ch] v3 = a3; if ( !a3 ) v3 = &unk_41FAD8; result = (void *)sub_4080F0(v5); if ( result ) { sub_407EB0(v5, (char *)a1, a2); sub_407FB0((int)v3, v5); sub_407E50(v5, 0x5Cu); result = v3; } return result; }
初步看,没啥信息,我们再深入到sub_4080F0
特征值出现,百度0x67452301;,这个是 MD5的内容
signed int __cdecl sub_4080F0(_DWORD *a1) { *a1 = 0x67452301; a1[1] = 0xEFCDAB89; a1[2] = 0x98BADCFE; a1[3] = 0x10325476; a1[4] = 0; a1[5] = 0; a1[22] = 0; return 1; }
以上都是猜测,还需要验证,可以借助python来判断。
import hashlib import binascii #定义方法 def GetMD5(hex_string): m = hashlib.md5() byteArr = bytes().fromhex(hex_string) m.update(byteArr) md5 = m.hexdigest().upper() #print('md5:%s\n'%(md5)) return md5 def GetSHA512(hex_string): sha512 = hashlib.sha512() byteArr = bytes().fromhex(hex_string) sha512.update(byteArr) sha512hash = sha512.hexdigest().upper() #print('sha512hash:%s\n'%(sha512hash)) return sha512hash #转换DWord并反转 def GetHexValueByHexString(hex_string): valueA = bytes().fromhex(hex_string) valueA = ''.join(['%02X'%i for i in valueA][::-1]) return valueA #执行: print('第一次:') name = 'B1AC71D22D82EBB1' valueA = GetHexValueByHexString('EFBEEDDE') nameBytes =''.join(['%02X'%(ord(i)) for i in name]) print("nameBytes:\n%s"%nameBytes) hex_string = nameBytes +'DEEDBEEF' print("hex_string:\n%s"%hex_string) sha512hash = GetSHA512(hex_string) print('sha512hash:\n%s\n'%(sha512hash)) md5hash = GetMD5(sha512hash) print('md5hash:\n%s\n'%(md5hash)) print('*'*100) print('第二次:') valueA = GetHexValueByHexString('9E3779B9') hex_string = md5hash + valueA print("hex_string:\n%s"%hex_string) sha512hash = GetSHA512(hex_string) print('sha512hash:\n%s\n'%(sha512hash)) md5hash = GetMD5(sha512hash) print('md5hash:\n%s\n'%(md5hash)) print('\n用户名MD5模拟计算完成')
计算结果如下:
第一次: nameBytes: 42314143373144323244383245424231 hex_string: 42314143373144323244383245424231DEEDBEEF sha512hash: F42472903D3172F04D40333D610B08E5B7887EC45BDA8DAA6E38FC6C2CBDEDBD9C6CC5F29448D407C710140F1529958E2DE2670FC9422E6A91721E2718A2E99B md5hash: 8E4890A34FA0383690081CB68564A973 **************************************************************************************************** 第二次: hex_string: 8E4890A34FA0383690081CB68564A973B979379E sha512hash: 1774F7037032D7888EF0404BC05DD2EEE958580FC093D7E800E49CB9252B1D0B4CA54B9678D6C7A6B786F421D3F1985A39FC38364C82BDCC39741067B55DC315 md5hash: B8D7725F7715A6A547A2BF0E8373705E
使用IDA进行调试【我用的是IDA64,不清楚IDA为啥不行】,经过验证,此时得到的用户名摘要的确是与python计算一致
附加密算法特征值:
引用地址:https://blog.csdn.net/Memroy/article/details/88077167 MD5 *v1 = 0x67452301; v1[1] = 0xEFCDAB89; v1[2] = 0x98BADCFE; v1[3] = 0x10325476; SHA1 *v1 = 0x67452301; v1[1] = 0xEFCDAB89; v1[2] = 0x98BADCFE; v1[3] = 0x10325476; v1[4] = 0xC3D2E1F0; SHA256 *v1 = 0x6A09E667; v1[1] = 0xBB67AE85; v1[2] = 0x3C6EF372; v1[3] = 0xA54FF53A; v1[4] = 0x510E527F; v1[5] = 0x9B05688C; v1[6] = 0x1F83D9AB; v1[7] = 0x5BE0CD19; SHA224 *v1 = 0xC1059ED8; v1[1] = 0x367CD507; v1[2] = 0x3070DD17; v1[3] = 0xF70E5939; v1[4] = 0xFFC00B31; v1[5] = 0x68581511; v1[6] = 0x64F98FA7; v1[7] = 0xBEFA4FA4; SHA512 *a1 = 0xF3BCC908; a1[1] = 0x6A09E667; a1[2] = 0x84CAA73B; a1[3] = 0xBB67AE85; a1[4] = 0xFE94F82B; a1[5] = 0x3C6EF372; a1[6] = 0x5F1D36F1; a1[7] = 0xA54FF53A; a1[8] = 0xADE682D1; a1[9] = 0x510E527F; a1[10] = 0x2B3E6C1F; a1[11] = 0x9B05688C; a1[12] = 0xFB41BD6B; a1[13] = 0x1F83D9AB; a1[14] = 0x137E2179; a1[15] = 0x5BE0CD19; SHA384 *a1 = 0xC1059ED8; a1[1] = 0xCBBB9D5D; a1[2] = 0x367CD507; a1[3] = 0x629A292A; a1[4] = 0x3070DD17; a1[5] = 0x9159015A; a1[6] = 0xF70E5939; a1[7] = 0x152FECD8; a1[8] = 0xFFC00B31; a1[9] = 0x67332667; a1[10] = 0x68581511; a1[11] = 0x8EB44A87; a1[12] = 0x64F98FA7; a1[13] = 0xDB0C2E0D; a1[14] = 0xBEFA4FA4; a1[15] = 0x47B5481D; RC4 有2个256次数的循环 AES 秘钥不需要单独初始化 并且里面有很多复杂的xor 一般情况下秘钥都是16字节(可能) 如果是CBC的话则还会拥有一个16字节的IV DES 秘钥需要单独初始化 秘钥长度固定8字节 每8字节分段加密 RSA 秘钥需要单独初始化 秘钥长度一般很长, 存储格式一般为base64文本 ECC 秘钥需要单独初始化 秘钥的首字节一般为0x04或0x03, 并且长度是这种17或31这种没有4字节对齐的数据
(3)、两者相等,即可通过
if ( !memcmp(&nameHash, &snHash, 0x10u) ) // 比较16个字节
sub_411A90(aCongratulation); // 正确
因为尚未实现对对称密钥的python代码化,以及利用大整数计算工具计算大数运算比python要快,python应该有相应模块,我就不找了,我将解密分为五部分来完成,由3个工具辅助实现
import hashlib import binascii #定义方法 def GetMD5(hex_string): m = hashlib.md5() byteArr = bytes().fromhex(hex_string) m.update(byteArr) md5 = m.hexdigest().upper() #print('md5:%s\n'%(md5)) return md5 def GetSHA512(hex_string): sha512 = hashlib.sha512() byteArr = bytes().fromhex(hex_string) sha512.update(byteArr) sha512hash = sha512.hexdigest().upper() #print('sha512hash:%s\n'%(sha512hash)) return sha512hash def GetHash(hex_string): sha512hash = GetSHA512(hex_string) md5hash = GetMD5(sha512hash) return md5hash def GetHexValueByHexString(hex_string): valueA = bytes().fromhex(hex_string) valueA = ''.join(['%02X'%i for i in valueA][::-1]) return valueA #第一阶段:根据用户名生成Hash #name = 'B1AC71D22D82EBB1' name = 'KCTF' print('第一阶段:根据用户名生成Hash.....\n') print('用户名:%s'%name) print('第一次:') valueA = GetHexValueByHexString('EFBEEDDE') nameBytes =''.join(['%02X'%(ord(i)) for i in name]) nameHash = GetHash(nameBytes + valueA) print('nameHash:%s'%(nameHash)) #print('*'*100) print('第二次:') valueA = GetHexValueByHexString('9E3779B9') nameHash = GetHash(nameHash + valueA) print('nameHash:%s\n'%(nameHash)) print('根据用户名生成Hash完成\n\n')
输出结果为:14AF58AD4D76D59D8D2171FFB4CA2231
snHash = 00025D343CED2E5A3CD5FE94CEA1570014AF58AD4D76D59D8D2171FFB4CA2231
【 snHash = 00+02+ 5D343CED2E5A3CD5FE94CEA157 +00+nameHash】
使用 BigInt.exe进行大数计算
输出结果为12A1758C9A9AACE82BDFA5ED5190DEBC0CAF522F2785E92E6CACBC6B0E3220B3
运行程序,用户名输入KCTF,序列号输入为 12A1758C9A9AACE82BDFA5ED5190DEBC0CAF522F2785E92E6CACBC6B0E3220B3
在运行到.text:004013C8处,将代码改为 push 1,或者在执行之后,修改堆栈里的对应数据为1
.text:004013C8 push 0 .text:004013CA push 80h .text:004013CF lea edx, [esp+7Ch+returnValueA] .text:004013D3 push offset unk_4190D0 .text:004013D8 push edx .text:004013D9 push eax .text:004013DA lea eax, [esp+88h+intSN] .text:004013DE push eax .text:004013DF call sub_4010F0 .text:004013E4 lea ecx, [esp+8Ch+returnValueB] .text:004013E8 lea edx, [esp+8Ch+returnValueA] .text:004013EC push ecx .text:004013ED push 20h .text:004013EF push edx .text:004013F0 call sub_401210 .text:004013F5 mov al, [esp+98h+returnValueB]
执行完.text:004013DF call sub_4010F0 之后,查看 0x19FDCC处【有的电脑不一样,这个可以通过记住之前压入堆栈中的edx值来跟踪】
获取值为:6ED8BC1F04D0C360567FB579398265FEEC8B48DC4B804904FEB1AB538C823270
name:KCTF
sn: 6ED8BC1F04D0C360567FB579398265FEEC8B48DC4B804904FEB1AB538C823270
最后于 1天前 被htg编辑 ,原因: