菜鸟对AspectC++ Add-In for Microsoft® Visual C++®插件的破解过程
AspectC++ Add-In for Microsoft® Visual C++®
http://www.pure-systems.com/fileadmin/downloads/acaddin/Aspectc-AddIn-Setup-010101.exe
我用的是VS2003环境
调试目标:C:\Program Files\pure-systems\AspectC++ Add-In\bin\CPPAddin.dll
工具:用OD调试,IDA+Hex-rays分析
重点集中在以下几个地址
0x10008e60 弹许可文件询问的对话框
0x10017a20 解析xml格式的许可文件
0x10017730 貌似许可检查的地方
这个是
0x10008e60的由Hex-ray分析出来的伪代码,
代码:
char __usercall sub_1000E860<al>(int a1<ebx>) { int ST14_4_0; // ST14_4@0 int v2; // ST10_4@1 const CHAR *v3; // eax@3 const CHAR *v4; // eax@10 int v6; // eax@2 char v7; // bl@2 char v8; // al@9 int v9; // [sp+Ch] [bp-9Ch]@1 signed int v10; // [sp+A4h] [bp-4h]@1 char v11; // [sp+10h] [bp-98h]@1 char v12; // [sp+48h] [bp-60h]@2 char v13; // [sp+2Ch] [bp-7Ch]@2 LPCSTR lpText; // [sp+14h] [bp-94h]@3 unsigned int v15; // [sp+28h] [bp-80h]@3 char v16; // [sp+64h] [bp-44h]@15 BYTE3(v9) = 0; sub_10016AA0(); v10 = 0; std__basic_string_char_std__char_traits_char__std__allocator_char____basic_string_char_std__char_traits_char__std__allocator_char__(&v11); LOBYTE(v10) = 1; v2 = a1; while ( 1 ) { sub_100169B0(16, (int)&v12); LOBYTE(v10) = 2; v6 = std__operator_(&v13, v6, "\\etc\\licence"); LOBYTE(v10) = 3; v7 = sub_10017A20((int)&unk_10024460, v6, (int)&v11) == 0; LOBYTE(v10) = 2; std__basic_string_char_std__char_traits_char__std__allocator_char_____basic_string_char_std__char_traits_char__std__allocator_char__(&v13); LOBYTE(v10) = 1; std__basic_string_char_std__char_traits_char__std__allocator_char_____basic_string_char_std__char_traits_char__std__allocator_char__(&v12); if ( !v7 ) break; std__basic_string_char_std__char_traits_char__std__allocator_char____operator__( &v11, "\nThe add-in will not work without a valid licence.\n\nInstall the licence file now?", v2); v3 = lpText; if ( v15 < 0x10 ) v3 = (const CHAR *)&lpText; if ( MessageBoxA(0, v3, "AspectC++ Add-In for Visual Studio .NET", 0x24u) != 6 ) break; if ( !sub_1000E690() ) { MessageBoxA(0, "Installation of licence file failed.", "AspectC++ Add-In", 0x40u); break; } } if ( byte_10024465 ) { sub_10017730((int)&unk_10024460, 16, (int)&v11); if ( v8 && (unsigned __int8)sub_10017520(&v11) != 1 ) { BYTE3(v9) = 1; } else { std__basic_string_char_std__char_traits_char__std__allocator_char____operator__( &v11, "\nThe add-in will not work.", ST14_4_0); v4 = lpText; if ( v15 < 0x10 ) v4 = (const CHAR *)&lpText; MessageBoxA(0, v4, "AspectC++ Add-In", 0x40u); } } LOBYTE(v10) = 0; std__basic_string_char_std__char_traits_char__std__allocator_char_____basic_string_char_std__char_traits_char__std__allocator_char__(&v11); v10 = -1; sub_10016910((int)&v16); return BYTE3(v9); }
基本上可以得出默认是分析
C:\Program Files\pure-systems\AspectC++ Add-In\etc\licence 许可文件
分析过程应该在0x10017a20里
代码:
char __thiscall sub_10017A20(int this, int a2, int a3) { int v3; // ebp@1 int v4; // esi@1 int v5; // eax@2 int v6; // eax@4 int v7; // edi@5 char result; // al@8 int v9; // ecx@1 int v10; // eax@5 int v11; // eax@6 int v12; // ST04_4@6 char v13; // bl@6 char v14; // [sp+Ch] [bp-28h]@6 signed int v15; // [sp+30h] [bp-4h]@6 v3 = a3; v4 = this; v9 = this + 96; *(_DWORD *)v9 = 0; *(_DWORD *)(v9 + 4) = 0; *(_DWORD *)(v9 + 8) = 0; *(_DWORD *)(v9 + 12) = 0; *(_BYTE *)(v4 + 5) = 0; std__basic_string_char_std__char_traits_char__std__allocator_char____operator_(v3, "Loading licence file failed."); if ( *(_DWORD *)(a2 + 24) < 0x10u ) v5 = a2 + 4; else v5 = *(_DWORD *)(a2 + 4); v6 = xmlParseFile(v5); *(_DWORD *)v4 = v6; if ( v6 && (v10 = xmlDocGetRootElement(v6), v7 = v10, v10) && (v11 = std__basic_string_char_std__char_traits_char__std__allocator_char____basic_string_char_std__char_traits_char__std__allocator_char__( &v14, "licence"), v12 = *(_DWORD *)(v7 + 8), v15 = 0, v13 = std__operator__(v11, v12), v15 = -1, std__basic_string_char_std__char_traits_char__std__allocator_char_____basic_string_char_std__char_traits_char__std__allocator_char__(&v14), v13) && sub_10017930(v7) ) { *(_BYTE *)(v4 + 5) = 1; sub_10017730(v4, v3, v3); result = 1; } else { result = 0; } return result; }
这是0x10017730的过程
代码:
void __userpurge sub_10017730(int this<ecx>, int ebp0<ebp>, int a2) { int ST18_4_0; // ST18_4@0 int ST1C_4_0; // ST1C_4@0 int v5; // esi@1 signed int v6; // eax@4 int v7; // ST18_4@6 int v8; // eax@20 int v9; // eax@4 int v10; // ST1C_4@12 int v11; // ST1C_4@18 int r; // [sp+60h] [bp+0h]@1 int v13; // [sp+50h] [bp-10h]@1 signed int v14; // [sp+5Ch] [bp-4h]@3 char v15; // [sp+34h] [bp-2Ch]@4 char v16; // [sp+18h] [bp-48h]@4 int v17; // [sp+1Ch] [bp-44h]@12 unsigned int v18; // [sp+30h] [bp-30h]@20 int v19; // [sp+2Ch] [bp-34h]@22 v5 = this; v13 = r ^ dword_100242D0; std__basic_string_char_std__char_traits_char__std__allocator_char____operator_(a2, "Invalid licence file found."); if ( *(_BYTE *)(v5 + 5) && !*(_BYTE *)(v5 + 4) ) { sub_10017B20((int)&a2, ebp0); v14 = 0; if ( !sub_1001A050((int)&a2) ) { LABEL_23: v14 = -1; sub_1001A060(&a2); goto LABEL_24; } v9 = std__operator_(&v15, v5 + 12, v5 + 40); LOBYTE(v14) = 1; std__operator_(&v16, v9, v5 + 68); LOBYTE(v14) = 3; std__basic_string_char_std__char_traits_char__std__allocator_char_____basic_string_char_std__char_traits_char__std__allocator_char__(&v15); v6 = *(_DWORD *)(v5 + 8); if ( v6 || !*(_DWORD *)(v5 + 96) ) { if ( v6 == 1 && *(_DWORD *)(v5 + 96) && *(_DWORD *)(v5 + 100) && *(_DWORD *)(v5 + 104) && *(_DWORD *)(v5 + 108) ) { std__basic_string_char_std__char_traits_char__std__allocator_char____operator__(&v16, "evaluation", ST18_4_0); std__basic_string_char_std__char_traits_char__std__allocator_char____operator__( &v17, *(_DWORD *)(v5 + 96), ST1C_4_0); std__basic_string_char_std__char_traits_char__std__allocator_char____operator__( &v17, *(_DWORD *)(v5 + 100), v10); std__basic_string_char_std__char_traits_char__std__allocator_char____operator__( &v17, *(_DWORD *)(v5 + 104), ST1C_4_0); v7 = *(_DWORD *)(v5 + 108); } else { if ( v6 != 2 || !*(_DWORD *)(v5 + 96) || !*(_DWORD *)(v5 + 100) || !*(_DWORD *)(v5 + 104) || !*(_DWORD *)(v5 + 108) ) goto LABEL_20; std__basic_string_char_std__char_traits_char__std__allocator_char____operator__(&v16, "user", ST18_4_0); std__basic_string_char_std__char_traits_char__std__allocator_char____operator__( &v17, *(_DWORD *)(v5 + 96), ST1C_4_0); std__basic_string_char_std__char_traits_char__std__allocator_char____operator__( &v17, *(_DWORD *)(v5 + 100), v11); std__basic_string_char_std__char_traits_char__std__allocator_char____operator__( &v17, *(_DWORD *)(v5 + 104), ST1C_4_0); v7 = *(_DWORD *)(v5 + 108); } } else { std__basic_string_char_std__char_traits_char__std__allocator_char____operator__(&v16, "beta", ST18_4_0); v7 = *(_DWORD *)(v5 + 96); } std__basic_string_char_std__char_traits_char__std__allocator_char____operator__(&v17, v7, ST1C_4_0); LABEL_20: v8 = v17; if ( v18 < 0x10 ) v8 = (int)&v17; *(_BYTE *)(v5 + 4) = sub_10019E30(v5 + 112, v8, v19, (int)&a2); LOBYTE(v14) = 0; std__basic_string_char_std__char_traits_char__std__allocator_char_____basic_string_char_std__char_traits_char__std__allocator_char__(&v16); goto LABEL_23; } LABEL_24: sub_1001A7B0( r ^ v13, ebp0); }
昨天我自己调试的时候也犯了一个错误,就是调试CppAddin.dll很痛苦,浪费不少时间和分散很多的精力,中间老是受到Va_X.dll的干扰,而且msdev.exe在CppAddin没加载时没法直接下断点,必须得在OD中设置一个新模块加载的断点(选项->调试选项->事件->中断于新模块),这样下来,每次调试失败后重启的过程很是漫长,浪费了不少时间,发帖子的求助的时候,我为了大大们给我能方便的解决问题,尽量把非技术问题都试验了一下,以免大大们被这些问题困住了,一怒之下不给我管我了。但是这样一来,也为我自己解决和发现问题加快了速度。
总结一下:
1.解决问题要东方不亮,西方亮,不要将自己精力消耗在远离问题本身的地方
2.坚持就是胜利,哪怕是菜鸟,不懂多少汇编(看代码还得靠伟大、光荣、正确的F5),也能取得成绩
下面写写分析过程:
其实昨天已经找到了关键的过程,那就是0x10017a20,这里面就是分析xml格式的许可文件
这里面有两个调用,sub_10017930()和 sub_10017730()
大概可以看得出来,sub_10017930参与了xml格式的许可数据的解析过程,它调用成功之后,才调用10017730对数据进行验证,要分析许可数据格式,必须研究10017930
代码:
char __stdcall sub_10017930(int a1) { int v1; // ebp@1 int v2; // esi@1 int v3; // esi@3 char result; // al@7 int v5; // eax@1 int v6; // eax@2 char v7; // bl@2 char v8; // [sp+Ch] [bp-28h]@2 signed int v9; // [sp+30h] [bp-4h]@2 v1 = a1; v5 = xmlGetProp(a1, "version"); v2 = v5; if ( v5 && (v6 = std__basic_string_char_std__char_traits_char__std__allocator_char____basic_string_char_std__char_traits_char__std__allocator_char__( &v8, L"1"), v9 = 0, v7 = std__operator__(v6, v2), v9 = -1, std__basic_string_char_std__char_traits_char__std__allocator_char_____basic_string_char_std__char_traits_char__std__allocator_char__(&v8), v7) ) { v3 = *(_DWORD *)(v1 + 12); result = (unsigned __int8)sub_10016D60(*(_DWORD *)(v1 + 12)) && (unsigned __int8)sub_10016E70(v3) && (unsigned __int8)sub_10017570(v3) && (unsigned __int8)sub_100172C0(v3); } else { result = 0; } return result; }
在这里面取了xml的一个属性值version,并且还有四个都不能失败的过程:
10016d60 取product 元素
10016e70 取creation 元素
10017570 取type元素
100172c0 取signature元素
为了熟悉libxml,我特意编写了一段典型libxml用法,并分析了其反向工程后的代码,
代码:
xmlDocPtr doc = NULL; xmlNodePtr cur = NULL; string filename("test.xml"); doc = xmlParseFile(filename.c_str()); cur = xmlDocGetRootElement(doc); if (xmlStrcmp(cur->name, BAD_CAST "licence")) { fprintf(stderr,"document of the wrong type, root node != mail"); xmlFreeDoc(doc); /*return -1; */ } cur = cur->xmlChildrenNode; xmlNodePtr propNodePtr = cur; while(cur != NULL) { //¨¨?3??¨²¦Ì??D¦Ì??¨²¨¨Y if ((!xmlStrcmp(cur->name, (const xmlChar *)"product"))) { string name; string version; xmlChar* szAttr = xmlGetProp(cur,BAD_CAST "name"); name=(char*)szAttr; szAttr=xmlGetProp(cur,BAD_CAST "version"); version=(const char *)szAttr; cout<<"Name:"<<name<<" Version:"<<version<<endl; xmlFree(szAttr); } cur=cur->next; } xmlFreeDoc(doc); /*return 0;*/
得出的结论是,凡是属性值的获取,都会调用xmlGetProp,而子元素的获取则是通过直接访问内存结构或者类似c++的函数访问,不会看到函数名称的引用,根据这一结论和具体的调试,大致推出来licence文件的结构如下:
代码:
<?xml version="1.0" encoding="UTF-8"?> <licence version="1"> <product name="aspect" version="1.0"/> <product name="pointcut" version="1.0" /> <product name="advice" version="1.0" /> <product name="slice" version="1.0" /> <creation date="2009-8-13"/> <type name="user"> <data code="xxxx" name="jans2002" email="[email protected]" regnr="ccccc"/> </type> <signature sign="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" /> </licence>
有了这个licence文件,把它放到
C:\Program Files\pure-systems\AspectC++ Add-In\etc\目录下
一切问题就变得简单了,顺利执行完了0x10017930,一路杀到0x10017730
代码:
int __thiscall sub_10017730(int this, int a2) { int v2; // esi@1 signed int v3; // eax@4 int v4; // ST14_4@6 int v5; // eax@20 int v7; // eax@4 int r; // [sp+4Ch] [bp+0h]@1 int v9; // [sp+3Ch] [bp-10h]@1 signed int v10; // [sp+48h] [bp-4h]@3 char v11; // [sp+20h] [bp-2Ch]@4 char v12; // [sp+4h] [bp-48h]@4 int v13; // [sp+8h] [bp-44h]@20 unsigned int v14; // [sp+1Ch] [bp-30h]@20 int v15; // [sp+18h] [bp-34h]@22 v2 = this; v9 = r ^ dword_100242D0; std__basic_string_char_std__char_traits_char__std__allocator_char____operator_(a2, "Invalid licence file found."); if ( *(_BYTE *)(v2 + 5) && !*(_BYTE *)(v2 + 4) ) { sub_10017B20(); v10 = 0; if ( !(unsigned __int8)sub_1001A050() ) { LABEL_23: v10 = -1; sub_1001A060(); return sub_1001A7B0(); } v7 = std__operator_(&v11, v2 + 12, v2 + 40); LOBYTE(v10) = 1; std__operator_(&v12, v7, v2 + 68); LOBYTE(v10) = 3; std__basic_string_char_std__char_traits_char__std__allocator_char_____basic_string_char_std__char_traits_char__std__allocator_char__(&v11); v3 = *(_DWORD *)(v2 + 8); if ( v3 || !*(_DWORD *)(v2 + 96) ) { if ( v3 == 1 && *(_DWORD *)(v2 + 96) && *(_DWORD *)(v2 + 100) && *(_DWORD *)(v2 + 104) && *(_DWORD *)(v2 + 108) ) { std__basic_string_char_std__char_traits_char__std__allocator_char____operator__(&v12, "evaluation"); std__basic_string_char_std__char_traits_char__std__allocator_char____operator__(&v12, *(_DWORD *)(v2 + 96)); std__basic_string_char_std__char_traits_char__std__allocator_char____operator__(&v12, *(_DWORD *)(v2 + 100)); std__basic_string_char_std__char_traits_char__std__allocator_char____operator__(&v12, *(_DWORD *)(v2 + 104)); v4 = *(_DWORD *)(v2 + 108); } else { if ( v3 != 2 || !*(_DWORD *)(v2 + 96) || !*(_DWORD *)(v2 + 100) || !*(_DWORD *)(v2 + 104) || !*(_DWORD *)(v2 + 108) ) goto LABEL_20; std__basic_string_char_std__char_traits_char__std__allocator_char____operator__(&v12, "user"); std__basic_string_char_std__char_traits_char__std__allocator_char____operator__(&v12, *(_DWORD *)(v2 + 96)); std__basic_string_char_std__char_traits_char__std__allocator_char____operator__(&v12, *(_DWORD *)(v2 + 100)); std__basic_string_char_std__char_traits_char__std__allocator_char____operator__(&v12, *(_DWORD *)(v2 + 104)); v4 = *(_DWORD *)(v2 + 108); } } else { std__basic_string_char_std__char_traits_char__std__allocator_char____operator__(&v12, "beta"); v4 = *(_DWORD *)(v2 + 96); } std__basic_string_char_std__char_traits_char__std__allocator_char____operator__(&v12, v4); LABEL_20: v5 = v13; if ( v14 < 0x10 ) v5 = (int)&v13; *(_BYTE *)(v2 + 4) = sub_10019E30(v2 + 112, v5, v15, (int)&a2); LOBYTE(v10) = 0; std__basic_string_char_std__char_traits_char__std__allocator_char_____basic_string_char_std__char_traits_char__std__allocator_char__(&v12); goto LABEL_23; } return sub_1001A7B0(); }
由于有了正确格式的licence,前面执行的都很顺利,一直到了sub_10019E30,跟进去一看,一切了然了:
代码:
bool __thiscall sub_10019E30(int this, int a2, int a3, int a4) { int v5; // esi@1 int v6; // eax@1 int v7; // eax@1 char v8; // [sp+4h] [bp-10h]@1 v5 = this; v6 = EVP_sha1(); EVP_DigestInit(&v8, v6); EVP_DigestUpdate(&v8, a2, a3); v7 = sub_1001A030(); return EVP_VerifyFinal(&v8, v5, *(_DWORD *)(v5 + 4096), v7) == 1;
}
前面一定都是加密验证的,但是函数最后来了一句:
代码:
EVP_VerifyFinal(&v8, v5, *(_DWORD *)(v5 + 4096), v7) == 1;
嘿嘿,看名字估计就是这个了,看看这个函数的返回值,1表示成功,其他表示失败,那就是只要让此函数永远返回1,就大功告成了,该到汇编代码了:
代码:
004066F0 /$ 83EC 10 sub esp, 10 ; 检查数字签名部分 004066F3 |. 56 push esi 004066F4 |. 8BF1 mov esi, ecx 004066F6 |. E8 81270000 call <jmp.&LIBEAY32.#333> 004066FB |. 50 push eax 004066FC |. 8D4424 08 lea eax, dword ptr [esp+8] 00406700 |. 50 push eax 00406701 |. E8 70270000 call <jmp.&LIBEAY32.#268> 00406706 |. 8B4C24 24 mov ecx, dword ptr [esp+24] 0040670A |. 8B5424 20 mov edx, dword ptr [esp+20] 0040670E |. 51 push ecx 0040670F |. 52 push edx 00406710 |. 8D4424 14 lea eax, dword ptr [esp+14] 00406714 |. 50 push eax 00406715 |. E8 56270000 call <jmp.&LIBEAY32.#269> 0040671A |. 8B4C24 34 mov ecx, dword ptr [esp+34] 0040671E |. 83C4 14 add esp, 14 00406721 |. E8 2A100000 call 00407750 00406726 |. 8B8E 00100000 mov ecx, dword ptr [esi+1000] 0040672C |. 50 push eax 0040672D |. 51 push ecx 0040672E |. 8D5424 0C lea edx, dword ptr [esp+C] 00406732 |. 56 push esi 00406733 |. 52 push edx 00406734 |. E8 49270000 call <jmp.&LIBEAY32.#290> 00406739 |. 83C4 10 add esp, 10 0040673C |. 48 dec eax 0040673D |. F7D8 neg eax 0040673F 1BC0 sbb eax, eax 00406741 90 inc eax 00406742 5E pop esi 00406743 83C4 10 add esp, 10 00406746 \. C2 0C00 retn 0C
一般来讲,函数返回值都会用EAX,不管三七二十一了,先吧EAX做了再说,把上面那个黑体的
inc eax 改成nop(呵呵,nop是大家的最爱,机器码是90了,这是改过的了)
保存到文件。
大功告成哈!!
身为菜鸟,能取得这样的结果,真是让人兴奋,谢谢各位阅读,还请各位高手不吝赐教。