首页
论坛
专栏
课程

[调试逆向] [原创]一次逆向fb寻找密码的记录及还原相关算法

2018-7-19 11:52 3585

[调试逆向] [原创]一次逆向fb寻找密码的记录及还原相关算法

2018-7-19 11:52
3585
嗯.开始写了。首先用peid查壳。
一个ASProtect v1.23RC1 *的壳,然后我到网上搜了下有没有相关的脱壳教程,在这里发现了一篇https://bbs.pediy.com/thread-207728.htm 但是他的脱壳方法有些麻烦,而且很多步骤都没有说明原因,只能自己脱了,首先用od载入程序,单步了几下,发现一个pushad
然后用esp定律,f9 断在这个地方
接着单步几下,发现没有oep特征,由此可知这个加了几层壳,继续单步,发现单步很久都没发现啥,如果你有耐心,会发现到后面还会出现几次pushad ,大概要单步几千次吧,,于是我又用了几次esp定律,继续单步,发现这玩意一直带着我在几个函数之间转圈圈,,我当时调这个应该是调了一天,,这么一直转下去不是办法,只能用点特殊的办法,让他帮我们还原到最接近oep的地方,我对esp有特征的下硬件断点,然后记录f9的次数,和啥时候该下断点,
这是当时调试记录的笔记=-=。倒数第四行那里我发现,f9 71次时代码已经被还原。于是就从70次开始单步,本来以为离oep已经很近了的,结果一单步下来又是几千条指令,单步了几个小时还是没发现oep。指令还是在几个函数间转圈圈,本来我打算继续用之前的办法再缩小到oep的距离的。可这样下去不是办法,于是,尝试直接f9,从堆栈中倒着来找oep,选了几个堆栈返回地址,
在这个地址往上翻了翻就翻到了oep。一个典型的vc++的oep,然后开始脱壳分析。用od载入脱完壳的程序。搜索所有字符串
看见了这几个网址,点进去,再用ida打开,找到相关函数f5。
signed int __thiscall sub_406540(void *this, int a2)
{
  int v2; // ebx@1
  int v3; // esi@1
  int v4; // ebp@2
  int v5; // edx@2
  const char **v6; // edi@3
  int v7; // ebp@11
  const char **v8; // edi@12
  int v9; // edx@17
  DWORD v10; // eax@20
  const char *v11; // eax@21
  bool v12; // bl@22
  bool v13; // bl@23
  int v14; // ebp@26
  int v15; // edx@26
  const char **v16; // edi@27
  wchar_t *v18; // eax@54
  const char *v19; // eax@56
  bool v20; // bl@57
  bool v21; // bl@58
  int v22; // ebp@61
  int v23; // edx@61
  const char **v24; // edi@62
  const char *v25; // [sp+28h] [bp-C74h]@1
  int i; // [sp+2Ch] [bp-C70h]@1
  LPVOID ppv; // [sp+30h] [bp-C6Ch]@1
  DWORD dwIndex; // [sp+34h] [bp-C68h]@3
  int v29; // [sp+38h] [bp-C64h]@1
  DWORD cchValueName; // [sp+3Ch] [bp-C60h]@1
  DWORD cbData; // [sp+40h] [bp-C5Ch]@12
  char v32; // [sp+44h] [bp-C58h]@1
  int v33; // [sp+48h] [bp-C54h]@3
  int v34; // [sp+4Ch] [bp-C50h]@1
  int v35; // [sp+58h] [bp-C44h]@23
  int v36; // [sp+5Ch] [bp-C40h]@21
  HKEY phkResult; // [sp+60h] [bp-C3Ch]@18
  int v38; // [sp+64h] [bp-C38h]@52
  char v39; // [sp+68h] [bp-C34h]@52
  LPCWSTR lpWideCharStr; // [sp+6Ch] [bp-C30h]@54
  CHAR ValueName; // [sp+90h] [bp-C0Ch]@20
  BYTE Data; // [sp+490h] [bp-80Ch]@20
  int v43; // [sp+C98h] [bp-4h]@1

  cchValueName = (DWORD)this;
  v25 = (const char *)dword_5FAB30;
  v43 = 0;
  v29 = 0;
  ppv = 0;
  LOBYTE(v43) = 2;
  sub_5810E6(&v32);
  LOBYTE(v43) = 3;
  CStringArray::SetAtGrow((CStringArray *)&v32, v34, "http://www.facebook.com");
  CStringArray::SetAtGrow((CStringArray *)&v32, v34, "http://www.facebook.com/");
  CStringArray::SetAtGrow((CStringArray *)&v32, v34, "https://login.facebook.com");
  CStringArray::SetAtGrow((CStringArray *)&v32, v34, "https://login.facebook.com/");
  CStringArray::SetAtGrow((CStringArray *)&v32, v34, "http://www.facebook.com/login.php");
  CStringArray::SetAtGrow((CStringArray *)&v32, v34, "http://www.facebook.com/index.php");
  CStringArray::SetAtGrow((CStringArray *)&v32, v34, "https://www.google.com/accounts/servicelogin");
  CStringArray::SetAtGrow((CStringArray *)&v32, v34, "https://www.regnow.com/vendorpriv/");
  CStringArray::SetAtGrow((CStringArray *)&v32, v34, "https://www.regnow.com/affiliatepriv/");
  CStringArray::SetAtGrow((CStringArray *)&v32, v34, "http://127.0.0.1/phpmyadmin/");
  CStringArray::SetAtGrow((CStringArray *)&v32, v34, "http://www.yandex.ru/");
  v2 = a2;
  v3 = 0;
  for ( i = 0; v3 < v34; i = v3 )
  {
    v4 = *(_DWORD *)(v2 + 8);
    v5 = 0;
    if ( v4 > 0 )
    {
      v6 = *(const char ***)(v2 + 4);
      dwIndex = *(_DWORD *)(v33 + 4 * v3);
      do
      {
        if ( !strcmp((const char *)dwIndex, *v6) )
          break;
        ++v5;
        ++v6;
      }
      while ( v5 < v4 );
      v3 = i;
      v2 = a2;
    }
    if ( v5 == v4 )
      sub_581340(v2, v4, v33 + 4 * v3);
    ++v3;
  }
  i = 0;
  if ( *(_DWORD *)(cchValueName + 8) > 0 )
  {
    do
    {
      v7 = *(_DWORD *)(v2 + 8);
      dwIndex = 0;
      if ( v7 > 0 )
      {
        v8 = *(const char ***)(v2 + 4);
        cbData = *(_DWORD *)(*(_DWORD *)(cchValueName + 4) + 4 * i);
        do
        {
          if ( !strcmp((const char *)cbData, *v8) )
            break;
          ++v8;
          ++dwIndex;
        }
        while ( (signed int)dwIndex < v7 );
      }
      if ( dwIndex == v7 )
        sub_581340(v2, v7, *(_DWORD *)(cchValueName + 4) + 4 * i);
      v9 = *(_DWORD *)(cchValueName + 8);
      ++i;
    }
    while ( i < v9 );
  }
  if ( !RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Internet Explorer\\TypedURLs", 0, 1u, &phkResult) )// 打开注册表
  {
    dwIndex = 0;
    while ( 1 )
    {
      v10 = dwIndex++;
      Data = 0;
      ValueName = 0;
      cbData = 2048;
      cchValueName = 1024;
      if ( RegEnumValueA(phkResult, v10, &ValueName, &cchValueName, 0, 0, &Data, &cbData) )// 遍历注册表,第一个参数是打开的注册表句柄,第二个参数是遍历的index,第三个参数是接收注册表键名的缓冲区,第四个参数指定第三个缓冲区的大小,倒数第二个是键值的缓冲区,最后一个参数指定前一个的大小
        break;
      CString::operator=((CString *)&v25, (char *)&Data);// 把键值赋值给v25
      v11 = *(const char **)sub_57F15A(&v25, (int)&v36, 4u);
      LOBYTE(v43) = 4;
      if ( !strcmp(v11, "http")
        || (v12 = strcmp(*(const char **)sub_57F15A(&v25, (int)&i, 3u), (const char *)&dword_5F882C) == 0,
            sub_58643C(&i),
            v12)
        || (v13 = strcmp(*(const char **)sub_57F15A(&v25, (int)&v35, 3u), (const char *)&off_5F8828) == 0,
            sub_58643C(&v35),
            v13) )
      {
        v13 = 1;
      }
      LOBYTE(v43) = 3;
      sub_58643C(&v36);
      if ( v13 )
      {
        v14 = 0;
        v15 = *(_DWORD *)(a2 + 8);
        if ( v15 > 0 )
        {
          v16 = *(const char ***)(a2 + 4);
          do
          {
            if ( !strcmp(v25, *v16) )           // 如果键值和a2偏移4的那个变量不相等,则跳出循环
              break;
            ++v14;
            ++v16;                              // 跟下一条字符串进行比较
          }
          while ( v14 < v15 );
        }
        if ( v14 == v15 )                       // 若没找到fb官网的键值
          sub_581340(a2, v15, (int)&v25);       // 调用这个函数
      }
    }
    RegCloseKey(phkResult);
  }
  if ( ppv )
    (*(void (__stdcall **)(LPVOID))(*(_DWORD *)ppv + 8))(ppv);// 释放对象
  ppv = 0;
  if ( CoCreateInstance(&rclsid, 0, 1u, &riid, &ppv) < 0 )// 创建一个com对象
  {
    LOBYTE(v43) = 2;
    CStringArray::~CStringArray((CStringArray *)&v32);
    LOBYTE(v43) = 1;
    if ( ppv )
      (*(void (__stdcall **)(LPVOID))(*(_DWORD *)ppv + 8))(ppv);// 释放对象
    LOBYTE(v43) = 0;
    if ( v29 )
      (*(void (__stdcall **)(int))(*(_DWORD *)v29 + 8))(v29);
LABEL_49:
    v43 = -1;
    sub_58643C(&v25);
    return 0;
  }
  if ( v29 )
    (*(void (__stdcall **)(int))(*(_DWORD *)v29 + 8))(v29);
  v29 = 0;
  if ( !ppv )
    sub_449BF9(-2147467261);
  if ( (*(int (__stdcall **)(LPVOID, int *))(*(_DWORD *)ppv + 28))(ppv, &v29) < 0 )// 调用进程中的某个方法,得到一个新的函数地址
  {
    LOBYTE(v43) = 2;
    CStringArray::~CStringArray((CStringArray *)&v32);
    LOBYTE(v43) = 1;
    if ( ppv )
      (*(void (__stdcall **)(LPVOID))(*(_DWORD *)ppv + 8))(ppv);// 释放对象
    LOBYTE(v43) = 0;
    if ( v29 )
      (*(void (__stdcall **)(int))(*(_DWORD *)v29 + 8))(v29);
    goto LABEL_49;
  }
  while ( 1 )
  {
    if ( !v29 )
      sub_449BF9(-2147467261);
    if ( (*(int (__stdcall **)(int, signed int, char *, int *))(*(_DWORD *)v29 + 12))(v29, 1, &v39, &v38) < 0 || !v38 )// 得到ie最近的历史记录,
      break;
    v18 = wcschr(lpWideCharStr, 0x3Fu);
    if ( v18 )
      *v18 = 0;
    CString::operator=((CString *)&v25, lpWideCharStr);
    v19 = *(const char **)sub_57F15A(&v25, (int)&cbData, 4u);
    LOBYTE(v43) = 5;
    if ( !strcmp(v19, "http")
      || (v20 = strcmp(*(const char **)sub_57F15A(&v25, (int)&v36, 3u), (const char *)&dword_5F882C) == 0,
          sub_58643C(&v36),
          v20)
      || (v21 = strcmp(*(const char **)sub_57F15A(&v25, (int)&v35, 3u), (const char *)&off_5F8828) == 0,
          sub_58643C(&v35),
          v21) )
    {
      v21 = 1;
    }
    LOBYTE(v43) = 3;
    sub_58643C(&cbData);
    if ( v21 )
    {
      v22 = 0;
      v23 = *(_DWORD *)(a2 + 8);
      if ( v23 > 0 )                            // 遍历他,对比程序前面的几个http链接
      {
        v24 = *(const char ***)(a2 + 4);
        do
        {
          if ( !strcmp(*v24, v25) )
            break;
          ++v22;
          ++v24;
        }
        while ( v22 < v23 );
      }
      if ( v22 == v23 )
        sub_581340(a2, v23, (int)&v25);
    }
  }
  LOBYTE(v43) = 2;
  CStringArray::~CStringArray((CStringArray *)&v32);
  LOBYTE(v43) = 1;
  if ( ppv )
    (*(void (__stdcall **)(LPVOID))(*(_DWORD *)ppv + 8))(ppv);
  LOBYTE(v43) = 0;
  if ( v29 )
    (*(void (__stdcall **)(int))(*(_DWORD *)v29 + 8))(v29);
  v43 = -1;
  sub_58643C(&v25);
  return 1;
}
分析之后我们发现他是从注册表的 这个注册表里存着的是ie收藏的url。
查找比对url,然后再调用ieframe.dll里面的某个函数来获取历史记录,至于是什么函数目前我还没办法知道。ida也分析不出来..可能只能去调那个dll?看功能来分析?不过我后来获取了一下ieframe的导出表。查看了一下导出函数,想到可能可以从基址偏移来看调用的是哪个函数。他调用那个函数取出来的也是一些url。是ie的历史url缓存,这个函数大概的意思就是把url都取出来然后push到一个类似vector的结构里面存着。解密的关键并不在这里。在字符串搜索里我们还看见了一串注册表的位置,
打开相应位置看看,
发现是一些加密数据,可能关键就在这里了,往下翻,发现调用了这个函数
具体代码请自行分析,跟进
继续跟进,
发现在这个函数里调用了关键函数,CryptUnprotectData,其中第一个参数是加密数据的结构体,图中a2便是加密数据的地址,a3是加密时的附加嫡,为url的登陆链接的字节码,解密完了的数据在pDataout结构体里,用od调试看看,
这个是最后那个解密完数据存放的地址,
解密之前,
解密之后,前四个字节是此解密数据的大小,后四个字节是解密数据的地址,

大概就是这些数据了,我们可以很明显的看到在最后面存放着我们需要的密码,test123,密码是我自己测试设置的,
然后此函数处理这些解密数据,得到密码
跟进

#define _CRT_SECURE_NO_WARNINGS
#pragma comment(lib,"Crypt32.lib")
#include <Windows.h>
#include <dpapi.h>
#include <iostream>
#include <cwchar>
#include <atlstr.h>
unsigned int  convert(int* a1, unsigned int a5);
int main() {
	DATA_BLOB DataIn;
	std::cout << sizeof(DATA_BLOB) << std::endl;
	DATA_BLOB DataOut;
	DATA_BLOB pOptionalEntropy;
	char  dest[100] = {0};
	BYTE bpkey[310]
	{
		0x01,0x00,0x00,0x00,0xD0,0x8C,0x9D,0xDF,0x01,0x15,0xD1,0x11,0x8C,0x7A,0x00,0xC0,
		0x4F,0xC2,0x97,0xEB,0x01,0x00,0x00,0x00,0xC0,0x6E,0xDE,0xB1,0x74,0x52,0xCB,0x46,
		0x9F,0xF5,0x92,0x73,0x39,0x9D,0x52,0x72,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,
		0x00,0x00,0x10,0x66,0x00,0x00,0x00,0x01,0x00,0x00,0x20,0x00,0x00,0x00,0x03,0x9F,
		0x65,0xE0,0x95,0xEC,0x94,0xE1,0x55,0x78,0x8D,0x0F,0x7F,0xF6,0x79,0x98,0x16,0xEF,
		0xB3,0xB8,0x11,0x2A,0x6E,0x9B,0xD1,0xCC,0x37,0xC5,0xFB,0x36,0x4F,0x7F,0x00,0x00,
		0x00,0x00,0x0E,0x80,0x00,0x00,0x00,0x02,0x00,0x00,0x20,0x00,0x00,0x00,0x0C,0x92,
		0xA1,0xB9,0x50,0x34,0x45,0xE9,0xF4,0xEC,0x1D,0x8E,0xF2,0x51,0x6D,0xA1,0x95,0x1A,
		0xB9,0xE5,0x93,0x27,0xEB,0x44,0x61,0x37,0xAD,0xD4,0x73,0xB4,0x04,0xC6,0x60,0x00,
		0x00,0x00,0xA1,0x60,0x4E,0xA4,0x84,0x8A,0x7A,0x21,0x3A,0xC9,0x32,0x94,0xD0,0x0E,
		0x94,0x77,0x22,0x2B,0x05,0x42,0xB5,0xE7,0x3B,0x4B,0x2F,0x7E,0x1B,0x8A,0x77,0x79,
		0x30,0x98,0xFD,0xBB,0x06,0x22,0xAE,0x8C,0x0F,0xAA,0x9D,0xA8,0x27,0x57,0x79,0x18,
		0x8E,0x6A,0x8E,0xBD,0x4C,0x43,0xEF,0xF8,0x5B,0x7C,0xED,0x8E,0x1A,0xA0,0x63,0x8D,
		0x9E,0xBA,0xE2,0x60,0xA8,0x99,0xCE,0xFC,0xE7,0xE8,0x80,0xC0,0xFA,0x71,0x58,0x6A,
		0xC2,0x08,0x9A,0x4F,0x1B,0xFC,0x47,0x88,0x56,0x0D,0xE4,0x06,0x1A,0x53,0x66,0xFC,
		0x70,0x7C,0x40,0x00,0x00,0x00,0x40,0xEB,0xD0,0x8F,0xE7,0x12,0xF6,0x7F,0xAB,0x9A,
		0x92,0x00,0xDA,0xA1,0x1B,0xB4,0xF2,0x66,0x54,0xE7,0x92,0x10,0xFD,0xB0,0xC0,0x6B,
		0xEC,0xDF,0x20,0x11,0x63,0xB1,0xA0,0x08,0xE0,0x31,0xE5,0x4A,0x05,0xF8,0x59,0x61,
		0x91,0x2D,0x93,0xC0,0x90,0xB0,0x41,0xBF,0x14,0x53,0xEA,0x65,0x1E,0xC5,0x95,0x98,
		0xFA,0x5A,0xB8,0x70,0xFA,0x64
	};


	BYTE url[66]{
		0x68,0x00,0x74,0x00,0x74,0x00,0x70,0x00,0x3A,0x00,0x2F,0x00,0x2F,0x00,0x77,0x00,
		0x77,0x00,0x77,0x00,0x2E,0x00,0x33,0x00,0x76,0x00,0x62,0x00,0x6F,0x00,0x6F,0x00,
		0x6B,0x00,0x73,0x00,0x2E,0x00,0x63,0x00,0x6F,0x00,0x6D,0x00,0x2F,0x00,0x6C,0x00,
		0x6F,0x00,0x67,0x00,0x69,0x00,0x6E,0x00,0x2E,0x00,0x70,0x00,0x68,0x00,0x70,0x00
	};
		
	DataIn.pbData = (byte*)bpkey;
	DataIn.cbData = 310;
	DataOut.cbData = 0;
	DataOut.pbData = 0;
	pOptionalEntropy.pbData = (BYTE*)url;
	pOptionalEntropy.cbData = 66;
	if (CryptUnprotectData(
		&DataIn,
		0,
		&pOptionalEntropy,                 // Optional entropy
		NULL,                 // Reserved
		NULL,                 // Here, the optional
							  // prompt structure is not
							  // used.
		1,
		&DataOut))
	{
		
		convert((int*)DataOut.pbData, DataOut.cbData);
		//printf("%s", result);
		LocalFree(DataOut.pbData);

	}
	else
	{
		printf("%d", GetLastError());
		printf("Decryption error!");
	}
	system("pause");
	return 0;
}

unsigned int  convert(int* a1, unsigned int a5)
{
	int *v5; // esi@1
	unsigned int result; // eax@1
	int v7; // edi@1
	BYTE *  v8; // eax@2
	size_t v9; // eax@3
	int v10; // ebx@3
	WCHAR* v13;
	size_t v11; // edi@4
	WCHAR* v12; // [sp+8h] [bp-4h]@3
	v5 = (int *)(a1 + 1);
	printf("%x\t%x\n", v5,a1);
	int v6 =  *v5 + *a1;
	if (v6 + 2 < a5) {
		v8 = (BYTE*)a1 + a5;
		//printf("%x", v8);
		*(v8 - 1) = 0;
		*(v8 - 2) = 0;
		//do
		//{
		printf("%x\n", v6);
		v12 = (WCHAR*)(v6/4 + a1);
		printf("%ls\n", v12);
		v9 = wcslen(v12);
		v10 = v6 + 2 * v9 + 2;
		result = v10+2;
		if (result > a5)
			return 1;
		printf("%x\n", v10);
		v13 = (WCHAR*)(v10 + (BYTE*)a1);
		printf("%ls", v13);
		v11 = 2 * wcslen((const wchar_t *)(v10/4 + a1));
		//(**a2)(a3, a4, v12, v10 + v5);
		//} while (v6 + 2 < a5);
	}
	
	return 0;
}

上面是我还原的关键算法,
运行结果:

另外吐槽一下od的udd保存的文件注释那些竟然只看文件名,不看路径,,我在od里写的注释在载入其他程序的时候全都没了,心塞。。。




[公告][征集寄语] 看雪20周年年会 | 感恩有你,一路同行

最后于 2018-7-19 14:19 被wx_clay编辑 ,原因:
最新回复 (11)
zyhxhw 2018-7-21 22:38
2
0
那些程序还看不懂?c或c++写的?
uvbs 2018-7-22 10:41
3
0
楼主写的分析很专业, 加油
kanxue 8 2018-7-22 22:11
4
0
精华鼓励支持!
wx_clay 1 2018-7-22 22:56
5
0
kanxue 精华鼓励支持!
谢谢版主大大,我一定会继续努力的(。・∀・)ノ
xie风腾 2018-7-25 08:20
6
0

真有耐心的楼主哟
shayi 9 2018-7-25 18:00
7
0
LZ能解释下“esp定律”么?
wx_clay 1 2018-7-26 15:57
8
0
shayi LZ能解释下“esp定律”么?
不敢在大佬们面前班门弄斧
严启真 2018-8-8 10:38
9
0
楼主太厉害了,受教了…
app流量 2018-8-10 10:28
10
0
逆向wx,拿密码更好玩
游客
登录 | 注册 方可回帖
返回