Advertisement
wocle

DMMRip Source (2012/10/07)

Oct 7th, 2012
6,715
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 14.20 KB | None | 0 0
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <math.h>
  4.  
  5. #ifndef WIN32_LEAN_AND_MEAN
  6.     #define WIN32_LEAN_AND_MEAN
  7. #endif//WIN32_LEAN_AND_MEAN
  8. #include <Windows.h>
  9. #include <WindowsX.h>
  10. #include <tlhelp32.h>
  11.  
  12.  
  13. // The name of the DMMViewer executable
  14. const char* dmm_process_name = "dmmviewer.exe";
  15. // The number of the version of DMMViewer this program is built for
  16. const char* dmm_version = "1.1.17.2";
  17.  
  18. // Range of memory to search
  19. // Could potentially be narrowed down
  20. const LPCVOID memory_min = (LPCVOID)0x00000000;
  21. const LPCVOID memory_max = (LPCVOID)0xffffffff;
  22.  
  23. // Control identifiers for the important parts of the window
  24. const int dmm_button_parent_code = 0xE805;
  25. const int dmm_button_first_code = 0x8023;
  26. const int dmm_button_next_code = 0x8020;
  27. const int dmm_button_zoom_code = 0x800A;
  28. const int dmm_area_parent_code = 0xE900;
  29. const int dmm_area_main_code = 0xE900;
  30.  
  31. // structs for storing .bmp header information
  32. // char arrays to prevent padding, etc.
  33. struct BMPHeader {
  34.     char x[14];
  35. };
  36.  
  37. struct BMPDIB {
  38.     char x[54];
  39. };
  40.  
  41. int create_bmp_header(struct BMPHeader* header, struct BMPDIB* dib, int w, int h) {
  42.     // Create bmp headers in the given structs
  43.     // returns the number of padding bits on each horizontal line
  44.  
  45.     // Calculate the length in bytes of one line (1 byte per color channel)
  46.     int bytew = w * 3;
  47.     // Each line must be aligned to 4 bytes
  48.     if (bytew & 0x0003) {
  49.         bytew |= 0x0003;
  50.         ++bytew;
  51.     }
  52.  
  53.     memset(header->x, 0, sizeof(header->x));
  54.     memset(dib->x, 0, sizeof(dib->x));
  55.     // .bmp magic numbers
  56.     header->x[0] = 'B';
  57.     header->x[1] = 'M';
  58.  
  59.     // Store...
  60.     // The size of the file
  61.     *((int*)(&header->x[2])) = sizeof(header->x) + sizeof(dib->x) + bytew * h;
  62.     // The bitmap offset (i.e. the size of the combined headers)
  63.     *((int*)(&header->x[10])) = sizeof(header->x) + sizeof(dib->x);
  64.     // The size of the DIB
  65.     *((int*)(&dib->x[0])) = sizeof(dib->x);
  66.     // The width of the image, in pixels
  67.     *((int*)(&dib->x[4])) = w;
  68.     // The height of the image, in pixels
  69.     *((int*)(&dib->x[8])) = h;
  70.     // The number of color planes (always 1) and the number of bits per pixel
  71.     *((int*)(&dib->x[12])) = 1 | 24 << 16;
  72.     // The size of the image, in bytes
  73.     *((int*)(&dib->x[20])) = bytew * h;
  74.  
  75.     return bytew - (w * 3);
  76. }
  77.  
  78. // Compact some stuff so it can all be sent by a pointer to get_process_window
  79. struct process_window_helper {
  80.     // Set this before calling get_process_window as the process to match
  81.     DWORD pid;
  82.     // get_process_window will set this pointing to a visible window belonging to the process
  83.     HWND hwnd;
  84. };
  85.  
  86. BOOL CALLBACK get_process_window(HWND hwnd, LPARAM lParam) {
  87.     // Find all windows belonging to the process
  88.     DWORD pid;
  89.     GetWindowThreadProcessId(hwnd, &pid);
  90.     if (((struct process_window_helper*)lParam)->pid == pid) {
  91.         // Any given process may have many invisible top level windows
  92.         // DMMViewer has about 4, but should only have one visible (the main window)
  93.         if (IsWindowVisible(hwnd)) {
  94.             ((struct process_window_helper*)lParam)->hwnd = hwnd;
  95.             return FALSE;
  96.         }
  97.     }
  98.  
  99.     return TRUE;
  100. }
  101.  
  102. void windows_error(const LPTSTR function_name) {
  103.     // Retrieve the system error message for the last-error code
  104.    
  105.     LPVOID msg_buffer;
  106.     DWORD error_code = GetLastError();
  107.        
  108.     if (error_code == 0) {
  109.         // No error available
  110.         return;
  111.     }
  112.    
  113.     FormatMessage(
  114.         FORMAT_MESSAGE_ALLOCATE_BUFFER |
  115.         FORMAT_MESSAGE_FROM_SYSTEM |
  116.         FORMAT_MESSAGE_IGNORE_INSERTS,
  117.         NULL,
  118.         error_code,
  119.         MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  120.         (LPTSTR) &msg_buffer,
  121.         0, NULL);
  122.    
  123.     fprintf(stderr, "Error in function %s(): %lu: %s\n", function_name, error_code, (LPTSTR) &msg_buffer);
  124.    
  125.     LocalFree(msg_buffer);
  126. }
  127.  
  128. int main(int argc, char** argv) {
  129.     char* output_prefix = NULL;
  130.     int range_min = -1;
  131.     int range_max = -1;
  132.     int offset = -1;
  133.     int padwidth = -1;
  134.     BOOL debug = FALSE;
  135.  
  136.     HANDLE system_snapshot;
  137.     PROCESSENTRY32 process;
  138.  
  139.     int i;
  140.  
  141.     // Parse command line options
  142.     // Partially untested, partially unimplemented
  143.     for (i = 1; i < argc; ++i) {
  144.         if (strcmp(argv[i], "-p") == 0 ||
  145.             strcmp(argv[i], "--prefix") == 0) {
  146.                 // Output file prefix
  147.                 // e.g. ./dmmrip -p ms1212_
  148.                 // results in
  149.                 // ms1212_001.bmp, ms1212_002.bmp, etc.
  150.                 // Default is no prefix
  151.                 if (output_prefix) {
  152.                     fprintf(stderr, "Multiple output prefixes specified\n");
  153.                 } else if (i + 1 >= argc) {
  154.                     fprintf(stderr, "Output prefix expected\n");
  155.                 } else {
  156.                     size_t prefix_length = strlen(argv[i + 1]);
  157.                     output_prefix = malloc(sizeof(char) * (prefix_length + 1));
  158.                     strcpy(output_prefix, argv[i + 1]);
  159.                 }
  160.                 ++i;
  161.         } else if (strcmp(argv[i], "-r") == 0 ||
  162.             strcmp(argv[i], "--range") == 0) {
  163.                 // Page range (inclusive, one indexed)
  164.                 // e.g. ./dmmrip -r 1 10
  165.                 // will rip the first ten pages
  166.                 // Default is entire file
  167.                 if (range_min >= 0 && range_max >= 0) {
  168.                     fprintf(stderr, "Multiple ranges specified\n");
  169.                 } else if (i + 2 >= argc) {
  170.                     fprintf(stderr, "Two range values expected\n");
  171.                 } else {
  172.                     printf("Range is not yet supported\n");
  173.                     range_min = atoi(argv[i + 1]);
  174.                     range_max = atoi(argv[i + 2]);
  175.                     if (range_min < 0 || range_max < range_min) {
  176.                         fprintf(stderr, "Invalid range specified\n");
  177.                         range_min = range_max = -1;
  178.                     }
  179.                 }
  180.                 i += 2;
  181.         } else if (strcmp(argv[i], "-o") == 0 ||
  182.             strcmp(argv[i], "--offset") == 0) {
  183.                 // Naming offset
  184.                 // e.g. ./dmmrip -p file -r 11 20 -o 1
  185.                 // will start at page 11 but name the image file001.bmp
  186.                 // Default is the first page in the range
  187.                 if (offset >= 0) {
  188.                     fprintf(stderr, "Multiple offsets specified\n");
  189.                 } else if (i + 1 >= argc) {
  190.                     fprintf(stderr, "Offset expected\n");
  191.                 } else {
  192.                     offset = atoi(argv[i + 1]);
  193.                     if (offset < 0) {
  194.                         fprintf(stderr, "Invalid offset\n");
  195.                         offset = -1;
  196.                     }
  197.                 }
  198.                 ++i;
  199.         } else if (strcmp(argv[i], "-w") == 0 ||
  200.             strcmp(argv[i], "--width") == 0) {
  201.                 // File number minimum width
  202.                 // e.g. ./dmmrip -w 5
  203.                 // will name files 00001.bmp, 00002.bmp, etc.
  204.                 // Default is 3
  205.                 if (padwidth > 0) {
  206.                     fprintf(stderr, "Multiple widths specified\n");
  207.                 } else if (i + 1 >= argc) {
  208.                     fprintf(stderr, "Width expected\n");
  209.                 } else {
  210.                     padwidth = atoi(argv[i + 1]);
  211.                     if (padwidth < 0) {
  212.                         fprintf(stderr, "Invalid width\n");
  213.                         padwidth = -1;
  214.                     }
  215.                 }
  216.                 ++i;
  217.         } else if (strcmp(argv[i], "-d") == 0 ||
  218.             strcmp(argv[i], "--debug") == 0) {
  219.                 // Enables debugging text
  220.                 if (debug) {
  221.                     fprintf(stderr, "Multiple debug declarations\n");
  222.                 } else {
  223.                     debug = TRUE;
  224.                 }
  225.         }
  226.     }
  227.  
  228.     if (output_prefix == NULL) {
  229.         output_prefix = "";
  230.     }
  231.     if (range_min < 0) {
  232.         range_min = 1;
  233.     }
  234.     if (range_max < 0) {
  235.         range_max = -1;
  236.     }
  237.     if (offset < 0) {
  238.         offset = range_min;
  239.     }
  240.     if (padwidth < 0) {
  241.         padwidth = 3;
  242.     }
  243.  
  244.     // Main program
  245.  
  246.     // Get a snapshot of all the running processes
  247.     system_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  248.     process.dwSize = sizeof(PROCESSENTRY32);
  249.  
  250.     if (Process32First(system_snapshot, &process) == TRUE) {
  251.         // Iterate through all the processes
  252.         while (Process32Next(system_snapshot, &process) == TRUE) {
  253.             // Find the DMM Viewer process by name
  254.             if (_stricmp(process.szExeFile, dmm_process_name) == 0) {
  255.                 // Open the process with permission to read memory and process info
  256.                 HANDLE dmm_process = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, process.th32ProcessID);
  257.                 struct process_window_helper pwh;
  258.  
  259.                 SYSTEM_INFO system_info;
  260.                 MEMORY_BASIC_INFORMATION mbi;
  261.                 DWORD page_granularity;
  262.  
  263.                 HWND dmm_window_main;
  264.                 HWND dmm_button_parent;
  265.                 HWND dmm_button_first, dmm_button_next;
  266.                 HWND dmm_button_zoom;
  267.                 HWND dmm_area_parent;
  268.                 HWND dmm_area_main;
  269.  
  270.                 int page;
  271.                 BOOL done = FALSE;
  272.  
  273.                 RECT original_window_rect;
  274.                 WINDOWPLACEMENT original_window_placement;
  275.                 WINDOWPLACEMENT temp_window_placement;
  276.  
  277.                 if (dmm_process == NULL) {
  278.                     fprintf(stderr, "Could not open process %s\n", process.szExeFile);
  279.                     windows_error("OpenProcess");
  280.                     exit(EXIT_FAILURE);
  281.                 }
  282.  
  283.                 pwh.pid = process.th32ProcessID;
  284.                 pwh.hwnd = (HWND)0xdeaddead;
  285.                 EnumWindows(&get_process_window, (LPARAM)&pwh);
  286.  
  287.                 // Find all the subwindow by control ID
  288.                 dmm_window_main = pwh.hwnd;
  289.                 dmm_button_parent = GetDlgItem(dmm_window_main, dmm_button_parent_code);
  290.                 dmm_button_first = GetDlgItem(dmm_button_parent, dmm_button_first_code);
  291.                 dmm_button_next = GetDlgItem(dmm_button_parent, dmm_button_next_code);
  292.                 dmm_button_zoom = GetDlgItem(dmm_button_parent, dmm_button_zoom_code);
  293.                 dmm_area_parent = GetDlgItem(dmm_window_main, dmm_area_parent_code);
  294.                 dmm_area_main = GetDlgItem(dmm_area_parent, dmm_area_main_code);
  295.  
  296.                 if (debug) {
  297.                     printf("dmm_window_main: %p\n", dmm_window_main);
  298.                     printf("dmm_button_parent: %p\n", dmm_button_parent);
  299.                     printf("dmm_button_first: %p\n", dmm_button_first);
  300.                     printf("dmm_button_next: %p\n", dmm_button_next);
  301.                     printf("dmm_button_zoom: %p\n", dmm_button_zoom);
  302.                     printf("dmm_area_main: %p\n", dmm_area_main);
  303.                 }
  304.  
  305.                 // Store original window position and state
  306.                 // Window will be shrunken to ensure window size is smaller than the image
  307.                 // This is necessary so that the immage in memory will not be padded
  308.                 original_window_placement.length = sizeof(original_window_placement);
  309.                 GetWindowPlacement(dmm_window_main, &original_window_placement);
  310.                 temp_window_placement = original_window_placement;
  311.                 temp_window_placement.showCmd = SW_SHOW;
  312.                 SetWindowPlacement(dmm_window_main, &temp_window_placement);
  313.  
  314.                 GetWindowRect(dmm_window_main, &original_window_rect);
  315.                
  316.                 SetWindowPos(dmm_window_main, 0, 0, 0, 256, 256, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
  317.  
  318.                 GetSystemInfo(&system_info);
  319.  
  320.                 // Find the size of a page, so we only have to check memory on each page boundary
  321.                 page_granularity = system_info.dwAllocationGranularity;
  322.  
  323.                 // Send a virtual click to the first page button
  324.                 SendMessage(dmm_button_first, BM_CLICK, 0, 0);
  325.  
  326.                 // Continue until the next button becomes disabled, indicating the last image
  327.                 for (page = 0; !done; ++page) {
  328.                     LPVOID address = (LPVOID)memory_min;
  329.                     LPVOID image_address = address;
  330.                     DWORD image_size = 0;
  331.                     SCROLLINFO sih, siv;
  332.                     int w, h;
  333.                     int* image;
  334.  
  335.                     int number_length;
  336.                     char* output_name;
  337.                     FILE* output_file;
  338.  
  339.                     struct BMPHeader header;
  340.                     struct BMPDIB dib;
  341.  
  342.                     int buffer_width;
  343.  
  344.                     int i;
  345.  
  346.                     // If the next button is disabled, we reached the end; grab one last image and finish
  347.                     done = GetWindowLong(dmm_button_next, GWL_STYLE) & WS_DISABLED;
  348.  
  349.                     // In my testing, the image is always found in the largest memory block allocated
  350.                     // by the process. This isn't necessarily going to always be true, so this will likely
  351.                     // fail on small images. Fortunately, comic pages tend to be rather large.
  352.                     // We can further narrow the possibilities down by looking at other characteristics of the memory
  353.                     // The memory type is always Private and set to Read/Write protection
  354.                     while (VirtualQueryEx(dmm_process, address, &mbi, sizeof(mbi))) {
  355.                         if (mbi.State == MEM_COMMIT && mbi.Type == MEM_PRIVATE && mbi.Protect == PAGE_READWRITE && mbi.RegionSize > image_size) {
  356.                             image_address = mbi.BaseAddress;
  357.                             image_size = mbi.RegionSize;
  358.                         }
  359.                         address = (char*)mbi.BaseAddress + mbi.RegionSize;
  360.                     }
  361.  
  362.                     if (debug) {
  363.                         printf("Most likely image location at %p (%lu bytes)\n", image_address, image_size);
  364.                     }
  365.  
  366.                     // As a hack, the dimensions of the images are determined by
  367.                     // looking at the size of the scroll area
  368.                     // This requires that the image area be smaller than the image
  369.                     sih.cbSize = sizeof(sih);
  370.                     sih.fMask = SIF_ALL;
  371.                     siv.cbSize = sizeof(siv);
  372.                     siv.fMask = SIF_ALL;
  373.  
  374.                     GetScrollInfo(dmm_area_main, SB_HORZ, &sih);
  375.                     GetScrollInfo(dmm_area_main, SB_VERT, &siv);
  376.  
  377.                     w = sih.nMax - sih.nMin + 1;
  378.                     h = siv.nMax - siv.nMin + 1;
  379.  
  380.                     if (debug) {
  381.                         printf("%i x %i pixels\n", w, h);
  382.                     }
  383.  
  384.                     // Allocate memory to copy image into
  385.                     image = malloc(sizeof(int) * w * h);
  386.                     // Read the image memory (image is stored in 4 byte pixels, 0xrrggbbFF)
  387.                     if (ReadProcessMemory(dmm_process, image_address, (LPVOID)image, w * h * 4, NULL) == 0) {
  388.                         if (debug) {   
  389.                             printf("Image uses %i of %lu allocated bytes\n", w * h * 4, image_size);
  390.                         }
  391.                         printf("On page %i:\n", page);
  392.                         windows_error("ReadProcessMemory");
  393.                         continue;
  394.                     }
  395.                     // Once image memory is ours, 'click' to the next image
  396.                     SendMessage(dmm_button_next, BM_CLICK, 0, 0);
  397.                     number_length = (int)log10((double)page + offset);
  398.                     number_length = number_length > padwidth ? number_length : padwidth;
  399.                     output_name = malloc(sizeof(char) * (strlen(output_prefix) + number_length + strlen(".bmp") + 1));
  400.  
  401.                     sprintf(output_name, "%s%0*i.bmp", output_prefix, padwidth, page + offset);
  402.  
  403.                     output_file = fopen(output_name, "wb+");
  404.  
  405.                     if (!output_file) {
  406.                         fprintf(stderr, "Failed to create file '%s'\n", output_name);
  407.  
  408.                         continue;
  409.                     }
  410.                    
  411.                     buffer_width = create_bmp_header(&header, &dib, w, h);
  412.                     fwrite(header.x, sizeof(header.x), 1, output_file);
  413.                     fwrite(dib.x, sizeof(dib.x), 1, output_file);
  414.  
  415.                     // .bmps are mirrored vertically
  416.                     for (i = h - 1; i >= 0; --i) {
  417.                         int j;
  418.                         for (j = 0; j < w; ++j) {
  419.                             fwrite(((char*)(image + i * w + j)), 3, 1, output_file);
  420.                         }
  421.                         // Write any padding bytes
  422.                         for (j = 0; j < buffer_width; ++j) {
  423.                             fputc(0xff, output_file);
  424.                         }
  425.                     }
  426.  
  427.                     fclose(output_file);
  428.  
  429.                     free(image);
  430.                 }
  431.  
  432.                 // Restore window size and state
  433.                 SetWindowPos(dmm_window_main, 0, 0, 0, original_window_rect.right - original_window_rect.left, original_window_rect.top - original_window_rect.bottom, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
  434.                 SetWindowPlacement(dmm_window_main, &original_window_placement);
  435.  
  436.                 CloseHandle(dmm_process);
  437.  
  438.                 break;
  439.             }
  440.         }
  441.     } else {
  442.         windows_error("Process32First");
  443.         exit(EXIT_FAILURE);
  444.     }
  445.  
  446.     return EXIT_SUCCESS;
  447. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement