国科大软件安全漏洞分析与发现第一次作业key2和key3
来源:互联网 发布:egress软件 编辑:程序博客网 时间:2024/06/08 00:05
题目和源代码
在找key2和key3之前存在几个困惑
- 在IDA中Strings window出现的“?decode@@YAPADPADH@Z”字符串到底表示什么含义,是不是我们要去破解的加密密码?
- 第一个密码输入之后生成的DllU.dll文件和Dll2.dll文件是什么关系,是否还需要使用Dll2.dll文件?
- 在homework1.exe中的main函数是如何调用dll文件的?
针对以上几个问题,开始进行下一步的探索,进而一个个得到了解决
1、首先是第一个问题,根据该标识查到了叫做调用约定的内容,根据带你玩转Visual Studio——调用约定与(动态)库这篇文章,进而得出该字符串的含义为表示一个函数,并不是要破解的加密密码,而表示char __cdecl * decode(char *tem1 , int tem2)这样的一个函数声明,具体原因如下图
2、然后是第二个问题,第二个问题根据从homework1.exe反编译出来的main函数代码中的一段可以看出来是Dll2.dll生成了DllU.dll并且之后在解密过程中使用的是DllU.dll,如下代码,所以之后并不需要使用Dll2.dll,该代码在反编译函数代码文件夹下的homework.c。题目和源代码
v16 = fopen("Dll2.dll", "rb");//读取Dll2.dll文件 fopen("DllU.dll", "wb");//写DllU.dll文件 if ( v16 ) sub_401050(); /*sub_401050()的两个参数是文件指针, 在这个函数里实现了将Dll2.dll解密成 DllU.dll的过程,也就是说之后的key2 和key3解密只需要DllU.dll就可以了*/ v17 = LoadLibraryA("DllU.dll");//加载dll
3、针对第三个问题,如何调用dll,其实在反编译的代码中可以看出来主要使用了LoadLibrary和GetProcAddress函数,但是反编译的代码并不能使用,所以我就实践了一下,自己生成test1.dll并且将其放到test2的main.c文件目录中,在程序中尝试调用,最终了解了整个的过程,该工程在test1文件夹下面,题目和源代码,主要代码如下
test1的myAPI.h
#ifndef _DLL_API #define _DLL_API _declspec(dllexport) #else #define _DLL_API _declspec(dllimport) #endif _DLL_API int printMax(int&a,int&b);
test1的main.cpp
#include "myAPI.h" #include <iostream>using namespace std;int printMax(int&a,int&b){ cout<<"Among ("<<a<<","<<b<<"), the Max Number is "<<(a>b?a:b)<<endl; return a;}
test2的main.cpp
#include <iostream>#include <windows.h>#include <strsafe.h>using namespace std;typedef void(*FUNA)(int&,int&);//定义指向和DLL中相同的函数原型指针void ErrorExit(LPTSTR lpszFunction) { // Retrieve the system error message for the last-error code LPVOID lpMsgBuf; LPVOID lpDisplayBuf; DWORD dw = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); // Display the error message and exit the process lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, (lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR)); StringCchPrintf((LPTSTR)lpDisplayBuf, LocalSize(lpDisplayBuf) / sizeof(TCHAR), TEXT("%s failed with error %d: %s"), lpszFunction, dw, lpMsgBuf); MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK); LocalFree(lpMsgBuf); LocalFree(lpDisplayBuf); ExitProcess(dw); }int main() { typedef int (*printMax)(int&a,int&b); const char* dllName = "test1.dll"; const char* funName = "printMax"; int x(100),y(100); HMODULE hDLL = LoadLibrary(L"test1.dll");//加载dll,将dll放在该工程根路径即可 if(hDLL != NULL) { //FUNA fp = FUNA(GetProcAddress(hDLL,"?printMax@@YAXAAH0@Z"));//获取导入到应用程序中的函数指针,根据方法名取得 //测试不好用啊。 printMax fp = printMax(GetProcAddress(hDLL,"?printMax@@YAHAAH0@Z")); // FUNA fp = FUNA(GetProcAddress(hDLL,MAKEINTRESOURCE(1)));//根据直接使用DLL中函数出现的顺序号//测试ok if(fp != NULL) { cout<<"Input 2 Numbers:"; cin>>x>>y; cout<<fp(x,y)<<endl; } else { //ErrorExit(TEXT("")); cout<<"Cannot Find Function : " <<funName<<endl; } FreeLibrary(hDLL); } else { cout<<"Cannot Find dll : "<<dllName<<endl; } return 1;}
其中函数名为?printMax@@YAHAAH0@Z是由于调用约定导致的,不是简单地printMax,如果这里写
printMax fp = printMax(GetProcAddress(hDLL,"printMax"));`
fp就只是一个空指针,而如何获取printMax函数的调用约定名字呢?要通过VS自带工具dumpbin来获取,具体方法在VS自带工具:dumpbin的使用,使用过后就看到了如下的输出
发现了调用约定名字为?printMax@@YAHAAH0@Z的函数,在查看动态库包含哪些接口函数之前还遇到了一个坑,就是生成dll文件的时候,如果没有我加到文件里的myAPI.h这个头,只有main.cpp的话,dll文件里是不会包含所写的函数的,也就是只会列出来一些summary信息,我之前参考的 C++调用DLL方法文章后面的显式调用代码是有问题的,如果按照它的来写是不可能的出来结果的,这个坑请注意,也可以看这个解释dll的输出函数使用__stdcall调用约定后,客户端用GetProcAddress出现的问题!
在解决了以上三个问题之后,距离找到key2和key3拥有了一定得基础,然后需要思考另外两个问题
- decode函数声明为char __cdecl * decode(char *tem1 , int tem2),调用格式知道了,但是如何实现的key2和key3加密?
- 在key2和key3分别调用decode函数的时候tem1和tem2的值是多少?
如果解决了这两个问题那么也就能得到最终答案了
1、针对第一个问题,我们现在已知decode的反编译代码,同时知道了如何调用dll文件,那么就可以写一个程序去掉用DllU.dll中的decode函数输出返回值,并且阅读decode的反编译代码重写逻辑构造可以运行的c程序,将二者的输出结果进行比对,如果一致说明对于decode程序的理解是正确的,该工程在useDllU文件夹下面,题目和源代码
#include <iostream>#include <windows.h>#include <strsafe.h>#include <stdlib.h>using namespace std;//typedef void(*FUNA)(int&,int&);//定义指向和DLL中相同的函数原型指针char *__cdecl mydecode(char *a1, int a2);/*根据IDA中反编译出来的decode函数,自行理解并且写出的mydecode函数,实际调用与dll调用值基本一致*/int main(){ typedef char* (*pDecode)(char *a, int b); const wchar_t* dllName = L"DllU.dll"; const char* funName = "?decode@@YAPADPADH@Z"; int y; char *x; x=(char *)malloc(11); HMODULE hDLL = LoadLibrary(dllName);//加载dll,将dll放在该工程根路径即可 if(hDLL != NULL) { //FUNA fp = FUNA(GetProcAddress(hDLL,"?decode@@YAPADPADH@Z"));//获取导入到应用程序中的函数指针,根据方法名取得 //FUNA fp = FUNA(GetProcAddress(hDLL,MAKEINTRESOURCE(1)));//根据直接使用DLL中函数出现的顺序号//测试ok pDecode fp = pDecode(GetProcAddress(hDLL,funName));//获取dll中函数指针 if(fp != NULL) { /*调用dll的函数*/ cout<<"Input 1 string(11bit) and 1 number:"; cin>>x>>y; cout<<"dll中函数计算值为:"<<fp(x,y)<<endl; /*自己恢复的decode函数*/ char *tem =(char *)malloc(11); tem=mydecode(x,y); cout<<"自己函数计算值为:"<<tem<<endl; } else { cout<<"Cannot Find Function : " <<funName<<endl; } FreeLibrary(hDLL); } else { cout<<"Cannot Find dll : "<<dllName<<endl; } return 1;} /*根据IDA中反编译出来的decode函数,自行理解并且写出的mydecode函数,实际调用与dll调用值基本一致*/char *__cdecl mydecode(char *a1, int a2){ int i; // [sp+50h] [bp-Ch]@1 int v7=11; // [sp+58h] [bp-4h]@1 char *tem=(char *)malloc(11); i = 0; if ( a2 ) { if ( a2 == 1 ) { for ( i = 0; i < v7; ++i ) tem[i] = a1[i] ^ 0x77; } else if ( a2 == 2 ) { for ( i = 0; i < v7; ++i ) { tem[i] = (((a1[i] & 0xF0) >> 4) & 0xF) + 16 * (a1[i] & 0xF); } } } else { for ( i = 0; i < (signed int)v7; ++i ) a1[i] = a1[i] - 1; } return tem;}
经过验证,key2的加密算法为
string = string ^ 0x77;
key3的加密算法为
string = (((string & 0xF0) >> 4) & 0xF) + 16 * (string & 0xF);
2、根据上述代码分析char __cdecl * decode(char *tem1 , int tem2)中tem2=1时是对key2加密,tem2=2时是对key3加密,那么接下来就是tem1的取值了,对于tem1的取值在homework1.exe反编译出来的main函数可以看到
v21 = ((int (__stdcall *)(_UNKNOWN *, signed int))v19)(&unk_40FA64, 1);//&unk_40FA64是一个地址存储字符串,1是key2的加密方式,调用decode函数v22 = (const char *)sub_401000(v21);//该函数是根据int类型的v21获取了一个地址,该地址存储的就是解码后的key2v23 = ((int (__stdcall *)(_UNKNOWN *, signed int))v20)(&unk_40FA70, 2);//&unk_40FA64是一个地址存储字符串,2是key3的加密方式,调用decode函数v30 = (const char *)sub_401000(v23);//该函数是根据int类型的v21获取了一个地址,该地址存储的就是解码后的key3
所以值就存在了&unk_40FA64和&unk_40FA70中,但是这两个地址在IDA动态调试中会发生变化,而且其中也并没有存值,这个是让我非常不解的地方,没有办法只好进行动态调试,在调试到这两个地址的时候,忽然发现eax出现了有规律的句子,于是异常惊喜的记录了下来,如下图
在输入命令行中发现是正确答案,忽然觉得自己上面一大堆分析好像并没有什么用处,直接动态调试就好了嘛!!!!!
最后得到了正确的结果,但是那两个地址的值得变化是如何跳到这里来的还不是很明确,希望有了解的同学能告诉我原因~~,最后的最后,把所有的反编译的代码粘出来,供大家参考,是不能运行的哟~
/*homework1.exe中使用IDA反编译出来的main函数,无法调用*/int __cdecl main(int argc, const char **argv, const char **envp){ void *v3; // eax@1 void *v4; // esi@1 char *v5; // ecx@1 signed int v6; // edi@1 const char *v7; // edi@3 signed int v8; // ecx@3 int v9; // edx@3 int v10; // esi@4 char v11; // bl@4 const char *v12; // eax@5 signed int v13; // ecx@5 char *v14; // ebx@5 const char *v15; // esi@5 FILE *v16; // edi@9 HMODULE v17; // eax@11 HMODULE v18; // edi@11 FARPROC v19; // eax@12 FARPROC v20; // esi@12 int v21; // eax@13 const char *v22; // edi@13 int v23; // eax@13 int v24; // esi@13 const char *v25; // ebx@13 int v26; // esi@13 const char *v27; // eax@13 DWORD v28; // eax@17 const char *v30; // [sp+Ch] [bp-2Ch]@13 HMODULE hLibModule; // [sp+10h] [bp-28h]@11 char v32[32]; // [sp+14h] [bp-24h]@5 v3 = malloc(0xBu); v4 = v3; *(_DWORD *)v3 = 0; *((_DWORD *)v3 + 1) = 0; *((_WORD *)v3 + 4) = 0; *((_BYTE *)v3 + 10) = 0; v5 = (char *)(&unk_40FA58 - (_UNKNOWN *)v3); v6 = 11; do { *(_BYTE *)v3 = *((_BYTE *)v3 + (_DWORD)v5) - 1; v3 = (char *)v3 + 1; --v6; } while ( v6 ); v7 = (const char *)malloc(0xCu); v8 = 0; v9 = (_BYTE *)v4 - v7; do { v10 = (int)&v7[v8]; v11 = v8 * v8 + *(&v7[v8] + v9) * *(&v7[v8] + v9); ++v8; *(_BYTE *)v10 = v11; } while ( v8 < 11 ); v7[v8] = 0; printf("please input key1: "); scanf("%30s", v32); v12 = (const char *)malloc(0xCu); v13 = 0; v14 = (char *)(v32 - v12); v15 = v12; while ( 1 ) { *v15 = v13 * v13; *v15 = v13 * v13 + v15[(_DWORD)v14] * v15[(_DWORD)v14]; ++v13; ++v15; if ( v13 >= 11 ) break; v14 = (char *)(v32 - v12); } v12[v13] = 0; if ( !strcmp(v7, v12) ) { v16 = fopen("Dll2.dll", "rb");//读取Dll2.dll文件 fopen("DllU.dll", "wb");//写DllU.dll文件 if ( v16 ) sub_401050(); /*sub_401050()的两个参数是文件指针, 在这个函数里实现了将Dll2.dll解密成 DllU.dll的过程,也就是说之后的key2 和key3解密只需要DllU.dll就可以了*/ v17 = LoadLibraryA("DllU.dll");//加载dll v18 = v17; hLibModule = v17; if ( v17 ) { v19 = GetProcAddress(v17, "?decode@@YAPADPADH@Z"); /*获取了类型为char* __cdecl decode(char *a, int b)的函数指针 decode函数里面有两个分支,分别是1和2,1是复杂计算,2是移位计算*/ v20 = v19; if ( v19 ) { v21 = ((int (__stdcall *)(_UNKNOWN *, signed int))v19)(&unk_40FA64, 1); //&unk_40FA64是一个地址存储字符串,1是key2的加密方式,调用decode函数 v22 = (const char *)sub_401000(v21); //该函数是根据int类型的v21获取了一个地址,该地址存储的就是解码后的key2 v23 = ((int (__stdcall *)(_UNKNOWN *, signed int))v20)(&unk_40FA70, 2); //&unk_40FA64是一个地址存储字符串,2是key3的加密方式,调用decode函数 v30 = (const char *)sub_401000(v23); //该函数是根据int类型的v21获取了一个地址,该地址存储的就是解码后的key3 printf("please input key2: "); v24 = (int)malloc(0xBu); scanf("%11s", v24); v25 = (const char *)sub_401000(v24); printf("please input key3: "); v26 = (int)malloc(0xBu); scanf("%11s", v26); v27 = (const char *)sub_401000(v26); if ( !strcmp(v22, v25) && !strcmp(v30, v27) ) { printf("You Win!\n"); v18 = hLibModule; } else { printf("You Failed!\n"); v18 = hLibModule; } } else { v28 = GetLastError(); printf("GetProcAddr failed %d 0x%x!\n", v28, 0); system("pause"); } FreeLibrary(v18); remove("DllU.dll"); } else { printf("Load dll failed!\n"); } } else { printf("key error!\n"); } system("pause"); return 0;}/*DllU.dll中使用IDA反编译出来的decode函数,无法调用*/char *__cdecl decode(char *a1, int a2){ char v3; // [sp+Ch] [bp-50h]@1 int v4; // [sp+4Ch] [bp-10h]@1 int i; // [sp+50h] [bp-Ch]@1 void *v6; // [sp+54h] [bp-8h]@1 size_t v7; // [sp+58h] [bp-4h]@1 memset(&v3, 0xCCu, 0x50u); v7 = 11; v6 = malloc(0xBu); memset(v6, 0, v7); i = 0; v4 = a2; if ( a2 ) { if ( v4 == 1 ) { for ( i = 0; i < (signed int)v7; ++i ) *((BYTE *)v6 + i) = a1[i] ^ 0x77; } else if ( v4 == 2 ) { for ( i = 0; i < (signed int)v7; ++i ) *((BYTE *)v6 + i) = (((a1[i] & 0xF0) >> 4) & 0xF) + 16 * (a1[i] & 0xF); } } else { for ( i = 0; i < (signed int)v7; ++i ) *((BYTE *)v6 + i) = a1[i] - 1; } return (char *)v6;}/*homework1.exe中使用IDA反编译的文件解密函数,无法调用*/void __usercall sub_401050(FILE *a1@<ebx>, FILE *a2@<edi>){ size_t v2; // esi@2 signed int i; // eax@2 char v4[1024]; // [sp+0h] [bp-404h]@2 if ( a1 ) { do { v2 = fread(v4, 1u, 0x400u, a2); for ( i = 0; i < (signed int)v2; ++i ) --v4[i]; fwrite(v4, 1u, v2, a1); } while ( (signed int)v2 > 0 ); fclose(a2); fclose(a1); }}/*homework1.exe中使用IDA反编译的根据int获取字符串函数,无法调用*/void *__cdecl sub_401000(int a1){ void *v1; // edi@1 signed int v2; // ecx@1 int v3; // esi@2 char v4; // bl@2 v1 = malloc(0xCu); v2 = 0; do { v3 = (int)((char *)v1 + v2); v4 = v2*v2 + *((BYTE *)v1 + v2 + a1 - (DWORD)v1) * *((BYTE *)v1 + v2 + a1 - (DWORD)v1); ++v2; *(BYTE *)v3 = v4; } while ( v2 < 11 ); *((BYTE *)v1 + v2) = 0; return v1;}
- 国科大软件安全漏洞分析与发现第一次作业key2和key3
- 国科大软件安全漏洞分析与发现第一次作业key1
- 国科大软件安全与漏洞分析第一次作业
- 算法分析与设计第一次作业
- 算法分析与设计第一次作业
- 微软禇诚云:软件安全漏洞与软件开发
- 软件测试技术第一次作业
- 截获数据分析--第一次作业
- Linux内核分析 第一次作业
- 安全漏洞和安全软件开发讲座
- 安全漏洞和安全软件开发讲座
- 安全漏洞和安全软件开发讲座
- 安全漏洞和安全软件开发讲座
- 既定Map中一组数,如:Map<"key1",11>,Map<"key2",33> Map<"key3",3>,Map<"key4",31>,Map<key5,45> 求出最大值
- Hotmail发现严重安全漏洞
- 数学家发现 Gmail 安全漏洞
- 第一次作业 线程和进程
- 第一次大作业分析【界面篇】
- 年份信息查询程序
- 基类指针和派生类指针
- 分页按页码读取redis数据
- Eclipse怎么汉化?(附汉化包下载链接版)
- CCF2014-09-3字符串匹配
- 国科大软件安全漏洞分析与发现第一次作业key2和key3
- 搬瓦工vps搭建wordpress
- CentOS-7设置开机进入文本界面(不进入图形界面)
- hbase学习2
- js 控制div 居中
- Java中Comparable和Comparator接口区别分析
- Struts2远程代码执行漏洞
- DBCP连接池配置参数说明
- 线程-通信-wait/notify