使用Fragment Shader进行图像解密的某游戏改皮肤手记

此游戏使用Unity 3D 5.3.4进行开发,加密图片通过Unity解包工具解出来之后,图片颜色不对,因游戏换过一次加密算法,固加密算法属于可更新资源。

先是从LUA层面入手未果,注意到游戏存在一个mat_v1f1及mat的特殊文件,通过Unity打开后,发现是个fragment shader,解开查看其kernel函数,注意到以下关键代码:

  lowp vec4 tmpvar_16;
  tmpvar_16 = texture2D (_MainTex, texcoord_11);
  color_14.w = tmpvar_16.w;
  color_14.xyz = sqrt(((1.0 - tmpvar_16.xyz) / (1.0 + tmpvar_16.xyz)));
  lowp float tmpvar_17;
  tmpvar_17 = mix (0.2, 0.8, float((color_14.z >= 0.25)));
  x_13 = tmpvar_17;
  lowp float tmpvar_18;
  tmpvar_18 = mix (0.2, 0.5, float((color_14.z >= 0.75)));
  y_12 = tmpvar_18;
  lowp float tmpvar_19;
  tmpvar_19 = float((color_14.x >= 0.5));
  highp float tmpvar_20;
  tmpvar_20 = mix (color_14.x, ((color_14.x - 
    (0.5 * x_13)
  ) * (1.0/(
    (1.0 - x_13)
  ))), tmpvar_19);
  color_14.x = tmpvar_20;
  lowp float tmpvar_21;
  tmpvar_21 = float((color_14.y >= 0.5));
  highp float tmpvar_22;
  tmpvar_22 = mix (color_14.y, ((color_14.y - 
    (0.5 * y_12)
  ) * (1.0/(
    (1.0 - y_12)
  ))), tmpvar_21);
  color_14.y = tmpvar_22;

使用OpenCV重写此逻辑:

float decode_impl(float a) {
	return sqrt((1 - a) / (1 + a));
}
cv::Mat decode(cv::Mat img) {
	cv::Mat fimg;
	img.convertTo(fimg, CV_32FC3, 1 / 256.0);
	for (auto i = 0; i<fimg.rows; i++) {
		for (auto j = 0; j<fimg.cols; j++) {
			auto& color = fimg.at<cv::Vec3f>(i, j);
			auto b = decode_impl(color[0]);
			auto g = decode_impl(color[1]);
			auto r = decode_impl(color[2]);
			auto x = mix(0.2f, 0.8f, b >= 0.25);
			auto y = mix(0.2f, 0.5f, b >= 0.75);
			r = mix(r, (r - 0.5f * x) * (1.0f / (1.0f - x)), r >= 0.5f);
			g = mix(g, (g - 0.5f * y) * (1.0f / (1.0f - y)), g >= 0.5f);
			color[0] = b;
			color[1] = g;
			color[2] = r;
		}
	}
	cv::Mat output;
	fimg.convertTo(output, CV_8UC3, 256.0);
	return output;
}

因需要更换皮肤,故需写出此解密的逆运算,如下:

float encode_impl(float a) {
	a *= a;
	return (1 - a) / (a + 1);
}
cv::Mat encode(cv::Mat img) {
	cv::Mat fimg;
	img.convertTo(fimg, CV_32FC3, 1 / 256.0);
	for (auto i = 0; i<fimg.rows; i++) {
		for (auto j = 0; j<fimg.cols; j++) {
			auto& color = fimg.at<cv::Vec3f>(i, j);
			auto b = color[0];
			auto g = color[1];
			auto r = color[2];
			auto x = mix(0.2f, 0.8f, b >= 0.25);
			auto y = mix(0.2f, 0.5f, b >= 0.75);
			r = mix(r, r * (1.0f - x) + (0.5f * x), r >= 0.5f);
			g = mix(g, g * (1.0f - y) + (0.5f * y), g >= 0.5f);
			color[0] = encode_impl(b);
			color[1] = encode_impl(g);
			color[2] = encode_impl(r);
		}
	}
	cv::Mat output;
	fimg.convertTo(output, CV_8UC3, 256.0);
	return output;
}

完成之后,后续工作就是替换原图片并重新封包即可。

附上旧版本的加密方式:
shader关键代码

tmpvar_12 = (floor((tmpvar_8.xy * 0.1)) * 10.0);
lowp vec4 tmpvar_13;
highp vec2 P_14;
P_14 = (((
(tmpvar_12 + min((_MainTex_TexelSize.zw - tmpvar_12), vec2(9.0, 9.0)))
-
(tmpvar_8.xy - tmpvar_12)
) + vec2(0.5, 0.5)) * _MainTex_TexelSize.xy);
tmpvar_13 = texture2D(_MainTex, P_14);
color_11.xyw = tmpvar_13.xyw;
color_11.z = (1.0 - tmpvar_13.z);
lowp float tmpvar_15;
tmpvar_15 = mix(2.0, 1.125, float((color_11.z >= 0.5)));
x_10 = tmpvar_15;
color_11.xy = (tmpvar_13.xy * x_10);

坐标变换代码

void transform(int ix, int iy, int width, int height, int& ox, int& oy) {
	ox = ix / 10 * 10;
	oy = iy / 10 * 10;
	ox += std::min(width - ox - 1, 9) - (ix - ox);
	oy += std::min(height - oy - 1, 9) - (iy - oy);
}

解密代码:

cv::Mat odecode(cv::Mat img) {
	cv::Mat fimg;
	cv::Mat ofimg;
	auto ox = 0, oy = 3;
	img.convertTo(fimg, CV_32FC4, 1 / 256.0);
	ofimg.create(fimg.rows, fimg.cols, CV_32FC3);
	for (auto i = oy; i < fimg.rows; i++) {
		for (auto j = ox; j < fimg.cols; j++) {
			int x, y;
			transform(j - ox, i - oy, fimg.cols - ox, fimg.rows - oy, x, y);
			auto color = fimg.at<cv::Vec4f>(y + oy, x + ox);
			auto& oc = ofimg.at<cv::Vec3f>(i, j);
			auto b = 1 - color[0];
			auto mul = mix(2.f, 1.125f, b > 0.5f);
			if (color[3] > 0.5) {
				oc[0] = clip(b); / b
				oc[1] = clip(color[1] * mul); / g
				oc[2] = clip(color[2] * mul); / r
			}
		}
	}
	cv::Mat output;
	ofimg.convertTo(output, CV_8UC3, 256.0);
	return output;
}

加密代码:

cv::Mat oencode(cv::Mat img) {
	cv::Mat fimg;
	cv::Mat ofimg;
	auto ox = 0, oy = 3;
	img.convertTo(fimg, CV_32FC3, 1 / 256.0);
	ofimg.create(fimg.rows, fimg.cols, CV_32FC3);
	for (auto i = oy; i < fimg.rows; i++) {
		for (auto j = ox; j < fimg.cols; j++) {
			int x, y;
			transform(j - ox, i - oy, fimg.cols - ox, fimg.rows - oy, x, y);
			auto color = fimg.at<cv::Vec3f>(y + oy, x + ox);
			auto& oc = ofimg.at<cv::Vec3f>(i, j);
			auto b = 1 - color[0];
			auto mul = mix(2.f, 1.125f, color[0] > 0.5f);
			oc[0] = clip(b); / b
			oc[1] = clip(color[1] / mul); / g
			oc[2] = clip(color[2] / mul); / r
		}
	}
	cv::Mat output;
	ofimg.convertTo(output, CV_8UC3, 256.0);
	return output;
}

从vmlinuz解压vmlinux的一个方法

# od -t x1 -A d vmlinuz-2.6.17 | grep “1f 8b 08 00”
0036864 84 9d 29 00 00 80 0b 00 f6 a8 18 00 1f 8b 08 00
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~1f 8b 08开始的地址为36864+12=36876

# dd if=vmlinuz-2.6.17 bs=1 skip=36876 | zcat > vmlinux
1616118+0 records in
1616118+0 records out

ref:/blog.chinaunix.net/uid-546544-id-2096148.html

(410) 348-1061

在进行业务测试中,对服务器进行了以天为单位的时间调整,从而导致Dataaccess无法正常退出。
查看CPU状态时,占用和内存正常。使用GDB调试发现,主线程在pthread_join处等待,RMI业务线程处于pthread_cond_timedwait位置,查看栈上变量的值,发现预期1秒超时的等待并未正确返回。
REFER:
/stackoverflow.com/questions/7121212/how-to-make-pthread-cond-timedwait-robust-against-system-clock-manipulations
/linux.die.net/man/3/pthread_condattr_setclock
/man7.org/linux/man-pages/man2/clock_gettime.2.html # CLOCK_MONOTONIC

Linux文件读写调优

2.6内核下
1、/proc/sys/vm/dirty_ratio
这个参数控制文件系统的文件系统写缓冲区的大小,单位是百分比,表示系统内存的百分比,表示当写缓冲使用到系统内存多少的时候,开始向磁盘写出数据。增大之会使用更多系统内存用于磁盘写缓冲,也可以极大提高系统的写性能。但是,当你需要持续、恒定的写入场合时,应该降低其数值,:
echo ‘1’ > /proc/sys/vm/dirty_ratio

echo ’40’ > /proc/sys/vm/dirty_ratio

2、/proc/sys/vm/dirty_background_ratio
这个参数控制文件系统的pdflush进程,在何时刷新磁盘。单位是百分比,表示系统内存的百分比,意思是当写缓冲使用到系统内存多少的时候,pdflush开始向磁盘写出数据。增大之会使用更多系统内存用于磁盘写缓冲,也可以极大提高系统的写性能。但是,当你需要持续、恒定的写入场合时,应该降低其数值,:
echo ‘1’ > /proc/sys/vm/dirty_background_ratio

echo ’10’ > /proc/sys/vm/dirty_background_ratio
3、/proc/sys/vm/dirty_writeback_centisecs
这个参数控制内核的脏数据刷新进程pdflush的运行间隔。单位是 1/100 秒。缺省数值是500,也就是 5 秒。如果你的系统是持续地写入动作,那么实际上还是降低这个数值比较好,这样可以把尖峰的写操作削平成多次写操作。设置方法如下:
echo “100” > /proc/sys/vm/dirty_writeback_centisecs

echo “500” > /proc/sys/vm/dirty_writeback_centisecs
如果你的系统是短期地尖峰式的写操作,并且写入数据不大(几十M/次)且内存有比较多富裕,那么应该增大此数值:
echo “1000” > /proc/sys/vm/dirty_writeback_centisecs
4、/proc/sys/vm/dirty_expire_centisecs
这个参数声明Linux内核写缓冲区里面的数据多“旧”了之后,pdflush进程就开始考虑写到磁盘中去。单位是1/100秒。缺省是 30000,也就是 30 秒的数据就算旧了,将会刷新磁盘。对于特别重载的写操作来说,这个值适当缩小也是好的,但也不能缩小太多,因为缩小太多也会导致IO提高太快。
echo “100” > /proc/sys/vm/dirty_expire_centisecs
当然,如果你的系统内存比较大,并且写入模式是间歇式的,并且每次写入的数据不大(比如几十M),那么这个值还是大些的好。

在n个元素的数组中获取Top k的实现方案

关键代码如下:

int i = 0;
for (; i < n && i < k + 1; i++){
	resultSet.push_back(arr[i]);
}
std::sort(resultSet.rbegin(), resultSet.rend());
for(; i < n; i++){
	resultSet[k] = arr[i];
	std::sort(resultSet.rbegin(), resultSet.rend());
}

OpenSSL生成RSA的公私钥

1、生成私钥pem, 执行命令openssl genrsa -out rsa_private_key.pem 1024
2、生成公钥,执行命令openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
3、将RSA私钥转换成PKCS8格式,命令执行openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt PHP服务端语言读取私钥不需要PKCS8转换。

516-804-9632

sudo service mysql stop
sudo mv /var/lib/mysql /data/mysql
sudo vim /etc/mysql/my.cnf
change:
datadir to /data/mysql
sudo vim /etc/apparmor.d/usr.sbin.mysqld
change all:
/var/lib/mysql -> /data/mysql

sudo service mysql start

REF. /www.digitalocean.com/community/tutorials/how-to-move-a-mysql-data-directory-to-a-new-location-on-ubuntu-16-04
/wiki.ubuntu.com/AppArmor

(417) 834-1808

这是一篇受密码保护的文章,您需要提供访问密码: