Giới thiệu
Với sự bùng nổ Internet như hiện nay, việc phát tán các phần mềm, băng đĩa lậu hay ảnh, video có bản quyền không có xa lạ gì nữa. Người sử dụng máy tính dần dần cũng mất đi sự tôn trọng đối với các sản phẩm có bản quyền.
Đứng về phía nhà phát triển, thật sự rất khó để có thể ngăn chặn tình trạng trên diễn ra. Bởi vậy, chứng minh 1 sản phẩm có phải là sản phẩm do mình tạo ra hay không là 1 giải pháp để hạn chế người dùng phát tán sản phẩm có bản quyền.
Bài viết hướng dẫn 1 trong những phương pháp để đánh dấu bản quyền và kiểm tra xem file ảnh có phải là file do mình đánh dấu hay không? Do bài viết chỉ mang tính minh họa nên chỉ thao tác trên file ảnh có định dạng TGA. Phương pháp này vẫn có thể áp dụng cho các loại định dạng ảnh và audio, video khác.
Các phương pháp đánh dấu bản quyền trên file ảnh
Nhìn chung có 2 cách đánh dấu bản quyền trên các file ảnh phổ biến:
- Đánh dấu thấy được.
- Đánh dấu không thấy được.
Đánh dấu bản quyền thấy được
Đánh dấu bản quyền thấy được là phương pháp đánh dấu của nhà xuất bản mà người sử dụng có thể dễ dàng thấy được (bằng mắt), hoặc nghe được (bằng tai).
Đối với hình ảnh, bản quyền thường được đánh dấu bằng 1 dòng chữ ghi tên nhà sản xuất hoặc logo của nhà sản xuất.
Đánh dấu bản quyền không thấy được
Cách đánh dấu như trên dường như quá "lộ", mọi người đều có thể thấy, sửa, xóa chúng. Nhà xuất bản có yêu cầu cao hơn sẽ đòi hỏi làm thế nào để che giấu thông tin của nhà xuất bản. Phương pháp này gọi là đánh dấu bản quyền không thấy được.
Phần còn lại của bài viết sẽ hướng dẫn phương pháp đánh dấu và kiểm tra bản quyền không thấy được trên các file ảnh có định dạng TGA.
Đề xuất ý tưởng
Với yêu cầu như trên, ý tưởng là làm thế nào để thêm thông tin của nhà xuất bản vào file ảnh mà không làm thay đổi chất lượng cũng như nội dung của file ảnh đó. Điều này sẽ làm cho người xem file ảnh không thể phân biệt được đâu là file ảnh đã được đánh dấu và đâu là file ảnh không được đánh dấu.
Đối với file ảnh TGA, hiện nay có 2 phiên bản được dùng: TGA v1.0 và TGA v2.0:
- Đối với TGA v1.0, file gồm 2 phần: header và nội dung ảnh. Thay đổi bất kỳ thông tin nào trong file ảnh sẽ làm biến đổi nội dung được hiển thị của ảnh.
- Đối với TGA v2.0, định dạng mới này cho phép nhà xuất bản có thể chèn bất kỳ thông tin gì vào khu vực có tên Developer Area. Có thể dùng khu vực này để đánh dấu bản quyền cho ảnh. Để tìm hiểu thông tin chi tiết về định dạng TGA, các bạn có thể xem tại mục Tham khảo.
Hiện thực
Đánh dấu bản quyền
Định dạng TGA hỗ trợ nhiều dạng ảnh khác nhau như grayscale, RGB, RGBA nén hoặc không nén. Vì bài viết chỉ mang tính minh họa, chỉ hiện thực việc đánh dấu bản quyền trên ảnh RGB và RGBA không nén.
Phương pháp hiện thực là ghi nội dung của file ảnh cũ vào file ảnh mới (file ảnh có chứa thông tin bản quyền) sau đó ghi thêm thông tin bản quyền và các thông tin kèm theo để phù hợp với định dạng TGA v2.0.
Đọc nội dung của file ảnh trước khi đánh dấu, bao gồm header và thông tin ảnh:
fread(&imageWidth, sizeof(short), 1, f); fread(&imageHeight, sizeof(short), 1, f); fread(&bitsPerPixel, sizeof(char), 1, f); int bytesPerPixel = bitsPerPixel / 8; int imageDataSize = imageWidth * imageHeight * bytesPerPixel; int imageSize = HEADER_SIZE + imageDataSize; char *imageData = new char[imageSize]; fseek(f, 0, SEEK_SET); fread(imageData, imageSize, 1, f);
Ghi nội dung của file ảnh vào file ảnh mới và thêm thông tin nhà xuất bản:
fwrite(imageData, imageSize, 1, signedFilePtr); fwrite(STDIO_DEVELOPER_AREA, sizeof(STDIO_DEVELOPER_AREA), 1, signedFilePtr); writeLEInt(signedFilePtr, 0); // extension area's offset writeLEInt(signedFilePtr, imageSize); // developer area's offset const char *SIGN = "TRUEVISION-XFILE."; fwrite(SIGN, 18, 1, signedFilePtr); // TGA v2.0 signature
Giả sử Developer Area hiện tại cần lưu 3 thông tin:
- STDIO Signature: là chuỗi STDIO dùng để nhận biết Developer Area này là do STDIO đánh dấu.
- Data Length: kích thước vùng dữ liệu chứa thông tin bản quyền.
- Data: thông tin bản quyền (trong bài viết này, thông tin bản quyền là chuỗi developer@stdio.vn).
Vì các số trong định dạng TGA đều được biểu diễn ở dạng little endian nên khi ghi số xuống file cần phải lưu ý vị trí của các byte trong số đó. Phần hiện thực như sau:
void writeLEInt(FILE* f, int x) { char buf[4]; buf[3] = ((x) & 0xFF000000) >> 24; buf[2] = ((x) & 0x00FF0000) >> 16; buf[1] = ((x) & 0x0000FF00) >> 8; buf[0] = ((x) & 0x000000FF); fwrite(buf, 4, 1, f); }
Kiểm tra bản quyền
Phần kiểm tra bản quyền của là kiểm tra xem các điều kiện sau có thỏa mãn hay không?
- Phiên bản ảnh sử dụng có phải là TGA v2.0 hay không?
- Developer Area có phải do STDIO thêm vào hay không?
- Thông tin bản quyền có phải là developer@stdio.vn hay không?
Mã nguồn của toàn bộ bài viết
Đánh dấu bản quyền
#include <stdio.h> #include <string.h> #define OFFSET_IMG_WIDTH 12 #define HEADER_SIZE 18 static const char STDIO_DEVELOPER_AREA[] = { "STDIO" // signature "\x13\x00\x00\x00" // data length "developer@stdio.vn" // data }; void writeLEInt(FILE* f, int x) { char buf[4]; buf[3] = ((x) & 0xFF000000) >> 24; buf[2] = ((x) & 0x00FF0000) >> 16; buf[1] = ((x) & 0x0000FF00) >> 8; buf[0] = ((x) & 0x000000FF); fwrite(buf, 4, 1, f); } int main(int argc, char **argv) { if(argc < 2) { printf("%s <image>\n", argv[0]); return -1; } /* Read image */ FILE *f = fopen(argv[1], "rb"); if(f == NULL) { printf("Can't open img_logo.tga to read\n"); return -1; } fseek(f, OFFSET_IMG_WIDTH, SEEK_SET); short imageWidth, imageHeight; char bitsPerPixel; fread(&imageWidth, sizeof(short), 1, f); fread(&imageHeight, sizeof(short), 1, f); fread(&bitsPerPixel, sizeof(char), 1, f); int bytesPerPixel = bitsPerPixel/8; int imageDataSize = imageWidth*imageHeight*bytesPerPixel; int imageSize = HEADER_SIZE + imageDataSize; char *imageData = new char[imageSize]; fseek(f, 0, SEEK_SET); fread(imageData, imageSize, 1, f); fclose(f); printf("Image Size: (%d, %d)\n", imageWidth, imageHeight); printf("Bits per pixel: %d\n", bitsPerPixel); printf("Image Size: %d\n", imageSize); /* Write image data + image footer to signed image */ char outputFilename[256]; sprintf(outputFilename, "signed_%s", argv[1]); FILE *signedFilePtr = fopen(outputFilename, "wb"); if(signedFilePtr == NULL) { printf("Can't open signed_img_logo.tga to write\n"); return -1; } fwrite(imageData, imageSize, 1, signedFilePtr); fwrite(STDIO_DEVELOPER_AREA, sizeof(STDIO_DEVELOPER_AREA), 1, signedFilePtr); writeLEInt(signedFilePtr, 0); // extension area's offset writeLEInt(signedFilePtr, imageSize); // developer area's offset const char *SIGN = "TRUEVISION-XFILE."; fwrite(SIGN, 18, 1, signedFilePtr); // TGA v2.0 signature fclose(signedFilePtr); printf("-> Created signed_img_logo.tga\n"); delete[] imageData; // release buffer to prevent leaking memory return 0; }
Kiểm tra bản quyền
#include <stdio.h> #include <string.h> #define TGAFOOTER_SIZE 26 unsigned int readLEInt(FILE* f) { char buf[4]; fread(buf, 4, 1, f); return ( ((buf[0] << 0) & 0xff) + \ ((buf[1] << 8) & 0xff00) + \ ((buf[2] << 16) & 0xff0000) + \ ((buf[3] << 24) & 0xff000000)); } int main(int argc, char **argv) { if(argc < 2) { printf("%s <image>\n", argv[0]); return -1; } /* Read signed image */ FILE *signedFilePtr = fopen(argv[1], "rb"); if(signedFilePtr == NULL) { return -1; } // find image size fseek(signedFilePtr, 0, SEEK_END); long int fileSize = ftell(signedFilePtr); // read image footer fseek(signedFilePtr, fileSize - TGAFOOTER_SIZE, SEEK_SET); unsigned int developerAreaOffset; unsigned int extensionOffset; char sig[18]; extensionOffset = readLEInt(signedFilePtr); developerAreaOffset = readLEInt(signedFilePtr); fread(sig, 18, 1, signedFilePtr); if(strcmp(sig, "TRUEVISION-XFILE.") != 0) { printf("ERROR: This is not TGA v2.0 image\n"); return -1; } printf("Extension area's offset: %d\n", extensionOffset); printf("Developer area's offset: %d\n", developerAreaOffset); printf("Signature: %s\n", sig); // read developer area // this area includes: // 1. STDIO signature: "STDIO" // 2. data length // 3. data fseek(signedFilePtr, developerAreaOffset, SEEK_SET); char stdioSig[6] = { 0 }; char *developerAreaData; unsigned int dataSize = 0; fread(stdioSig, 5, 1, signedFilePtr); // read STDIO signature if(strcmp(stdioSig, "STDIO") != 0) { printf("ERROR: This image isn't signed by STDIO\n"); return -1; } dataSize = readLEInt(signedFilePtr); // read data length developerAreaData = new char[dataSize]; fread(developerAreaData, dataSize, 1, signedFilePtr); // read data if(strcmp(developerAreaData, "developer@stdio.vn") != 0) { printf("ERROR: License is invalid\n"); return -1; } printf("STDIO Signature: %s\n", stdioSig); printf("Data Length: %d\n", dataSize); printf("Data: %s\n", developerAreaData); printf("-> This image is copyrighted by STDIO\n"); delete[] developerAreaData; return 0; }