使用OD加载crackme,发现有LUA虚拟机,拖入IDA中定位lua_load函数如下
.text:004045FC lua_load_t proc near ; CODE XREF: luaL_loadfilex+125p
.text:004045FC ; sub_405A88+23p ...
.text:004045FC
.text:004045FC var_14 = byte ptr -14h
.text:004045FC arg_0 = dword ptr 8
.text:004045FC arg_4 = dword ptr 0Ch
.text:004045FC arg_8 = dword ptr 10h
.text:004045FC arg_C = dword ptr 14h
.text:004045FC arg_10 = dword ptr 18h
.text:004045FC
.text:004045FC push ebp
.text:004045FD mov ebp, esp
.text:004045FF sub esp, 14h
.text:00404602 cmp [ebp+arg_C], 0
.text:00404606 jnz short loc_40460F
.text:00404608 mov [ebp+arg_C], offset a?_1 ; "?"
.text:0040460F
.text:0040460F loc_40460F: ; CODE XREF: lua_load_t+Aj
.text:0040460F push ebx
.text:00404610 push esi
OD中下断,根据参数找到buffer,将buffer数据导出,如下:
1B 6C 73 11 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
....
发现文件头被修改过,将头结构修复如下:
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 工具将上述数据反编译得到,lua5.3的脚本:
g_strRegSn = " "
g_strRegSnToVerify = ""
userRegister = function(strRegSnIn)
-- function num : 0_0 , upvalues : _ENV
local iRc = -1
g_strRegSn = strRegSnIn
g_strRegSnToVerify = fnGetRegSnToVerify()
g_strRegSn = fnCalcUserInputRegSnAfterEnc(g_strRegSn)
if g_strRegSn == g_strRegSnToVerify then
iRc = 1024
end
g_strRegSnToVerify = ""
return iRc
end
getRegSnAfterCalc = function(strRegSnIn)
-- function num : 0_1 , upvalues : _ENV
return g_strRegSn
end
通过分析可知,fnGetRegSnToVerify和fnCalcUserInputRegSnAfterEnc为关键回调C函数,下面定位这两个函数
使用IDA定位luaD_precall
.text:00409DAD luaD_precall proc near ; CODE XREF: sub_40A1CF+21p
.text:00409DAD ; sub_40A3F6+50p ...
.text:00409DAD
.text:00409DAD arg_0 = dword ptr 8
.text:00409DAD arg_4 = dword ptr 0Ch
.text:00409DAD arg_8 = word ptr 10h
.text:00409DAD
.text:00409DAD push ebp
.text:00409DAE mov ebp, esp
.text:00409DB0 push ebx
.text:00409DB1 push esi
.text:00409DB2 push edi
.text:00409DB3 mov edi, [ebp+arg_4]
.text:00409DB6 mov esi, [ebp+arg_0]
.text:00409DB9 mov eax, [edi+8]
.text:00409DBC and eax, 3Fh
.text:00409DBF cmp eax, 6
OD中下断跟踪关键跳转
00409F04 |. FF55 0C CALL DWORD PTR SS:[EBP+C]
00409F07 |. 8B4E 0C MOV ECX,DWORD PTR DS:[ESI+C]
00409F0A |. 50 PUSH EAX
00409F0B |. C1E0 04 SHL EAX,4
首先来到fnGetRegSnToVerify函数
004019A2 . FF7424 04 PUSH DWORD PTR SS:[ESP+4]
004019A6 . E8 F11C0000 CALL CrackMe.0040369C
004019AB . 85C0 TEST EAX,EAX
004019AD . 59 POP ECX
004019AE . 75 13 JNZ SHORT CrackMe.004019C3
004019B0 . 6A 20 PUSH 20
004019B2 . 68 44D24200 PUSH CrackMe.0042D244
004019B7 . FF7424 0C PUSH DWORD PTR SS:[ESP+C]
004019BB . E8 50220000 CALL <CrackMe.lua_pushlstring>
004019C0 . 83C4 0C ADD ESP,0C
004019C3 > 6A 01 PUSH 1
004019C5 . 58 POP EAX
004019C6 . C3 RETN
紧接着是fnCalcUserInputRegSnAfterEnc函数
004019C7 >/. 55 PUSH EBP ; fnCalcUserInputRegSnAfterEnc
004019C8 |. 8BEC MOV EBP,ESP
004019CA |. 51 PUSH ECX
004019CB |. 51 PUSH ECX
004019CC |. 8365 F8 00 AND DWORD PTR SS:[EBP-8],0
004019D0 |. 8365 FC 00 AND DWORD PTR SS:[EBP-4],0
004019D4 |. 56 PUSH ESI
004019D5 |. 57 PUSH EDI
004019D6 |. 8B7D 08 MOV EDI,DWORD PTR SS:[EBP+8]
004019D9 |. 57 PUSH EDI
004019DA |. E8 BD1C0000 CALL CrackMe.0040369C
004019DF |. 83F8 01 CMP EAX,1
004019E2 |. 59 POP ECX
004019E3 |. 75 51 JNZ SHORT CrackMe.00401A36
004019E5 |. 8D45 FC LEA EAX,DWORD PTR SS:[EBP-4]
004019E8 |. 50 PUSH EAX
004019E9 |. 6A 01 PUSH 1
004019EB |. 57 PUSH EDI
004019EC |. E8 C5410000 CALL CrackMe.00405BB6
004019F1 |. 8BF0 MOV ESI,EAX
fnGetRegSnToVerify每次返回固定数据如下:
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扭杀?
通过lua脚本可知,该数据与fnCalcUserInputRegSnAfterEnc函数的结果对比
分析fnCalcUserInputRegSnAfterEnc的流程为:
strRegSnIn = (sn^t2)^t1;
t1和t2为固定32字节数据,这里有个小技巧可以将fnGetRegSnToVerify的输出作为fnCalcUserInputRegSnAfterEnc的输入即可解密,如下:
020F6BF8 4B 7D 6F 22 BD EA 61 C3 0B E7 B2 D9 2C 6B 41 88 K}o"疥a?绮?kA?
020F6C08 5D 71 27 85 BA 71 F0 B9 23 77 28 6C FC 36 A6 D0 ]q'吅q鸸#w(l?π
将上述结果为逆向即可得序列号,为此可先定位正向加密算法。
通过断点GetWindowTextA可得到输入缓冲区,对缓冲区下范围断点,几次后来到加密函数,如下:
0040315B /$ 55 PUSH EBP
0040315C |. 8BEC MOV EBP,ESP
0040315E |. 83EC 1C SUB ESP,1C
00403161 |. 8365 FC 00 AND DWORD PTR SS:[EBP-4],0
00403165 |. 57 PUSH EDI
00403166 |. 8BF9 MOV EDI,ECX
00403168 |. 837F 08 00 CMP DWORD PTR DS:[EDI+8],0
0040316C |. 0F86 3C010000 JBE CrackMe.004032AE
00403172 |. 53 PUSH EBX
00403173 |. 56 PUSH ESI
00403174 |> 8B75 FC /MOV ESI,DWORD PTR SS:[EBP-4]
00403177 |. 8B87 94000000 |MOV EAX,DWORD PTR DS:[EDI+94]
0040317D |. 0377 04 |ADD ESI,DWORD PTR DS:[EDI+4]
00403180 |. 8D9F 90000000 |LEA EBX,DWORD PTR DS:[EDI+90]
00403186 |. C745 F4 100000>|MOV DWORD PTR SS:[EBP-C],10
0040318D |. 2906 |SUB DWORD PTR DS:[ESI],EAX
0040318F |. 8B87 98000000 |MOV EAX,DWORD PTR DS:[EDI+98]
00403195 |. 2946 08 |SUB DWORD PTR DS:[ESI+8],EAX
00403198 |> 8B4E 08 |/MOV ECX,DWORD PTR DS:[ESI+8]
0040319B |. 8B46 0C ||MOV EAX,DWORD PTR DS:[ESI+C]
0040319E |. DD05 70E24200 ||FLD QWORD PTR DS:[42E270]
004031A4 |. 894E 0C ||MOV DWORD PTR DS:[ESI+C],ECX
004031A7 |. 8B4E 04 ||MOV ECX,DWORD PTR DS:[ESI+4]
004031AA |. 894E 08 ||MOV DWORD PTR DS:[ESI+8],ECX
结合IDA逆向得出代码
i = 0;
do
{
cur_ptr = ptr + i;
s3 = v1 + 144;
count = 16;
cur[0] -= s2[0];
cur[2] -= s2[1];
do
{
v4 = cur[3];
cur[3] = cur[2];
cur[2] = cur[1];
cur[1] = cur[0];
cur[0] = v4;
v10 = ROL(cur[1] * (2 * cur[1] + 1), 5);
v14 = ROL(cur[3] * (2 * cur[3] + 1), 5);
cur[0] = v10 ^ ROR(cur[0] - *(_DWORD *)(s3 - 4), v14&0x1F);
cur[2] = v14 ^ ROR(cur[2] - *(_DWORD *)s3, v10&0x1F);
s3 -= 8;
count--;
}while ( count > 0 );
cur[1] -= s4[0];
cur[3] -= s4[1];
i += 16;
}while ( i < len );
写程序实现逆运算求出序列号,程序代码如下:
unsigned char gs2[] = { 0x6D, 0x07, 0xAF, 0x7F, 0x4C, 0xFA, 0xD7, 0x9B };
unsigned char gs3[] = {
0x7A, 0xDA, 0x48, 0x47, 0xFC, 0x42, 0x43, 0xA4, 0x54, 0x76, 0x72, 0xB2, 0x9F, 0x11, 0xF9, 0xD3,
0x52, 0x4F, 0xF0, 0x8C, 0xBE, 0x64, 0x65, 0x44, 0x2E, 0x0A, 0xD4, 0xB4, 0x67, 0x64, 0x96, 0x02,
0xA5, 0xBA, 0xF2, 0xA3, 0x40, 0x30, 0xD9, 0x89, 0x8C, 0x36, 0x4B, 0xDC, 0xAB, 0x2F, 0x4D, 0x45,
0xD7, 0x95, 0x07, 0xC4, 0x3B, 0xFD, 0x98, 0xE1, 0x02, 0x2F, 0x7D, 0x2F, 0xDB, 0xAA, 0x09, 0x37,
0xD2, 0x2B, 0x88, 0xAC, 0xF5, 0x9B, 0x55, 0x20, 0xF6, 0x01, 0xB5, 0x69, 0x98, 0x4F, 0xD1, 0xA9,
0x70, 0x40, 0x9E, 0xDC, 0x2B, 0x7D, 0xB9, 0x1F, 0x1F, 0xB2, 0xA0, 0x14, 0x5F, 0x49, 0xE1, 0xEA,
0x50, 0x1E, 0x41, 0xD8, 0xEA, 0x22, 0xD6, 0x94, 0xE5, 0x8F, 0x56, 0xCD, 0x36, 0x63, 0x10, 0x32,
0x1F, 0xF0, 0xF7, 0x09, 0xD9, 0xF6, 0x5C, 0x5E, 0xA0, 0x25, 0x2F, 0xBE, 0x92, 0x63, 0x9C, 0x2E,
0xD1, 0x6D, 0xEA, 0xBB
};
unsigned char gs4[] = { 0x37, 0x66, 0xF7, 0x5B, 0x7A, 0xDA, 0x48, 0x47 };
int ROL(unsigned int a1, char a2)
{
return (a1 << a2) | (a1 >> (32 - a2));
}
int ROR(unsigned int a1, char a2)
{
return (a1 >> a2) | (a1 << (32 - a2));
}
int encode(unsigned char *ptr, int len)
{
unsigned long *s2 = (unsigned long *)gs2;
unsigned long *s4 = (unsigned long *)gs4;
unsigned long *cur = 0;
int i = 0;
do{
cur = (unsigned long *)(ptr + i);
unsigned char *s3 = gs3+(sizeof(gs3)-4);
int count = 16;
cur[0] -= s2[0];
cur[2] -= s2[1];
do
{
unsigned long v4 = cur[3];
cur[3] = cur[2];
cur[2] = cur[1];
cur[1] = cur[0];
cur[0] = v4;
unsigned long v10 = ROL(cur[1] * (2 * cur[1] + 1), 5);
unsigned long v14 = ROL(cur[3] * (2 * cur[3] + 1), 5);
cur[0] -= *(unsigned long *)(s3 - 4);
cur[0] = ROR(cur[0], v14 & 0x1F);
cur[0] ^= v10;
cur[2] -= *(unsigned long *)s3;
cur[2] = ROR(cur[2], v10 & 0x1F);
cur[2] ^= v14;
s3 -= 8;
count--;
} while (count > 0);
cur[1] -= s4[0];
cur[3] -= s4[1];
i += 16;
} while (i < len);
return 0;
}
int decode(unsigned char *ptr, int len)
{
unsigned long *s2 = (unsigned long *)gs2;
unsigned long *s4 = (unsigned long *)gs4;
unsigned long *cur = 0;
int i = 0;
do{
cur = (unsigned long *)(ptr + i);
unsigned char *s3 = gs3;
int count = 16;
cur[1] += s4[0];
cur[3] += s4[1];
do
{
s3 += 8;
unsigned long v10 = ROL(cur[1] * (2 * cur[1] + 1), 5);
unsigned long v14 = ROL(cur[3] * (2 * cur[3] + 1), 5);
cur[0] ^= v10;
cur[0] = ROL(cur[0], v14 & 0x1F);
cur[0] += *(unsigned long *)(s3 - 4);
cur[2] ^= v14;
cur[2] = ROL(cur[2], v10 & 0x1F);
cur[2] += *(unsigned long *)s3;
unsigned long v4 = cur[0];
cur[0] = cur[1];
cur[1] = cur[2];
cur[2] = cur[3];
cur[3] = v4;
count--;
} while (count > 0);
cur[0] += s2[0];
cur[2] += s2[1];
i += 16;
} while (i < len);
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
unsigned char sn[] = { "11111111111111111111111111111111111111111111111111111111111111111" };
encode(sn, 32);
decode(sn, 32);
unsigned char sr[] = {
0x4B, 0x7D, 0x6F, 0x22, 0xBD, 0xEA, 0x61, 0xC3, 0x0B, 0xE7, 0xB2, 0xD9, 0x2C, 0x6B, 0x41, 0x88,
0x5D, 0x71, 0x27, 0x85, 0xBA, 0x71, 0xF0, 0xB9, 0x23, 0x77, 0x28, 0x6C, 0xFC, 0x36, 0xA6, 0xD0
};
decode(sr, 32);
return 0;
}
最终序列号为:
+ sr 0x001efde0 "stK5CKpBsw7TPF45" unsigned char[0x00000020]
[峰会]看雪.第八届安全开发者峰会10月23日上海龙之梦大酒店举办!
上传的附件: