一、背景
最近项目组新上项目,交付的时间比较急迫,原本好的分支管理习惯没有遵守好,于是出现下面状况:
- 多个小伙伴在不同的分支上开发。
- 原本QA环境也存在一个阻碍性的bug A
- 一位同事在QA环境发布了新的代码,引入了新bug B
- 回滚QA能修改bug B,但是对于bugA却无能为力
- 同时,混乱的代码管理已经导致无法确定原始发布包对应的代码版本。
最终陷入了两难的地步,既不能发布新包,回滚也无法解决问题。
好在之前了解到如何使用微软官方工具ILAsm与ILDasm对dll文件进行修改,于是开始动手实现。下面将会用示例代码讲解如何修改已经的.exe文件。
二、ILAsm与ILDasm
我们知道,.net是一个跨平台的的开发平台,其跨平台则是由其编译的中间语言(Intermediate Language, 简称IL或MSIL)实现,无论我们使用的是C#、VB.Net、还是F#或者C++, 最终都会被编译成IL,由JIT(Just In Time)编译成目标机器语言,在CLR(Commen Language Runtime, 公用语言运行时)上运行。
因此,理论上,我们可以跳过过平时使用的C#代码,直接修改IL,然后生成相应的dll或者exe文件。
那么如何查看与修改IL呢,这就是ILAsm与ILDasm的工作了。ILAsm (MSIL Assembler),用来从IL语言生成PE(Portable Executable),也就是.net中我们使用的.exe、.dll文件。ILDasm (MSIL Disassembler),则与ILAsm相反,从PE文件,生成.IL文件。那么我们可以猜到,要修改dll,我们需要先用ILDasm反编译.dll生成.il文件,再用ILAsm编译修改后的.il文件生成.dll,最终替换.dll文件。
三、使用ILDasm生成IL
先看下示例代码:
class Program
{
static void Main(string[] args)
{
var loginResult = Login("foo", "111111");
if (loginResult)
{
Console.WriteLine("登录成功");
}
else
{
Console.WriteLine("登录失败,请重试");
}
Console.ReadLine();
}
private static bool Login(string userName, string password)
{
if (userName.Equals("johnny") && password.Equals("123456"))
{
return false;
}
return false;
}
}
显然,上述代码针对Login(string userName, string password)
的调用会返回false,导致最后Console中会输出"登录失败,请重试", 我们的目的是通过直接修改.exe文件,让它返回true, Console里面输出"登录成功"。
ILDasm与ILAsm已经包含在Visual Studio发行包中中,无需另外下载安装。按如下步骤执行即可:
1. 开Developer Command Prompt for VS 2017,在里面输入命令ILDasm
。
2. 在打开的IL Dasm窗口中找到需要修改的.exe文件。
3. 选择菜单 File > Dump,弹出的新窗口中点击确认,保存生成的.il文件。
整个过程如下面gif所示:
![](https://images2018.cnblogs.com/blog/1188602/201804/1188602-20180402214243800-805358223.gif)
最后会生成相应的.il与.res文件。
![](https://images2018.cnblogs.com/blog/1188602/201804/1188602-20180402214317230-1372271562.png)
四、修改IL
打开.il文件,会看到如下代码(节选)
.method private hidebysig static bool Login(string userName,
string password) cil managed
{
这个就是Login(string userName, string password)
所对应的IL代码了。如果你了解IL语言,可以直接对其修改。
如果不想直接修改IL,我们可以重写一个小的示例方法,直接return true
,如下:
private static bool Login()
{
return true;
}
然后使用ILDasm生成相应的IL代码,替换我们想修改的方法。最终的IL如下:
.method private hidebysig static bool Login(string userName,
string password) cil managed
{
.maxstack 8
IL_0000: ldc.i4.1
IL_0001: ret
}
五、使用ILAsm生成exe
修改保存完.il文件以后,接下来的工作就是利用 ILAsm 让.il文件生成重新生成.exe可执行文件了,在Console中执行如下命令
ilasm ILAsmAndILDasmDemo.il /output:ILAsmAndILDasmDemo_1.exe
![编译修改后的IL代码](https://images2018.cnblogs.com/blog/1188602/201804/1188602-20180402214727355-1513154320.png)
成功以后会生成一个ILAsmAndILDasmDemo_1.exe文件,执行这个文件,我们可以看到,现在已经显示"登录成功"了。
![新生成的.exe文件](https://images2018.cnblogs.com/blog/1188602/201804/1188602-20180402214503938-1885787479.png)
![Console输出“登录成功”](https://images2018.cnblogs.com/blog/1188602/201804/1188602-20180402214533596-329144403.png)
使用反编译工具 dotPeekI查看新生成的ILAsmAndILDasmDemo_1.exe文件,我们能够看到,Login(string userName, string password)
已经直接return true
了。如下图,
![Login直接返回true](https://images2018.cnblogs.com/blog/1188602/201804/1188602-20180402214359698-394490086.png)
六、总结
其它,上面所做的事情其实也是《CLR via C#》中提到强名能够 防止代码被不怀好意的人篡改 的一个反面教材了。通过这个例子,大家应该对代码被篡改的风险也有一定的认识了,所以如果大家需要将自己的.dll(.exe)文件露给别人,最好还是打上强名,防止别人恶意篡改你的代码,导致不必要的损失。
经过这次事件,也证明了多了解下底层还是很有必要的,说不定哪天就用上了。平时再忙,也不能只限于只业务和编程语言层面,还需要对底层有一定的了解,这样才能知其然与知其所以然。
虽然此次在不修改c#代码的情况下完美解决QA的环境问题,但是这种方法也只限于小范围的改动,只试用于救急。所以给我的教训是分支管理规范才是王道,要能做到随时可发布,随时可回滚才行,这样才能完全避免再次出现这样尴尬的情况了。
Posted on
2018-05-18 18:39 Leo_olivine 阅读(9) 评论(
学习内容:C#;学习书籍:图解教程(中文第四版)。
目录:第二章 C#编程概述 -----> 2.7 从程序中输出文本 -----> 2.7.5格式化数字字符串
![复制代码](https://common.cnblogs.com/images/copycode.gif)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _2018_05_04_AM_1520_格式化数字字符串
{
class Program
{
static void Main(string[] args)
{
/*
* 格式化数字字符串
* 格式说明符的语法由3个字段组成:索引号、对齐说明符、格式字段(format field)
* 格式:{ index , alignment :format }
* index 为索引,是必需要填写的,表示是哪一个需要进行格式化;
* alignment 为对齐说明符,由自己决定是否需要填写;是一个整数,其中正整数表示的是字段右对齐,负整数表示的是左对齐,填写的数字表示的是长度(宽度);
* format 为格式字段,来指定由index选择的数字的表现形式;需注意冒号后面紧跟格式说明符,中间不能由空格。
* 格式说明符 是一个字母字符;精度说明符 是可以选择是否需要,由1~2位数字组成。
* 具体请看下面的例子
*/
double first = 1234.56789;
double second = -12.3456789;
/*
* 标准数字格式说明符
*/
Console.WriteLine("格式说明符:");
Console.WriteLine("货币C、c(不区分大小写) |{0, 15 :C}|{1, -15 :c3}|", first, second);
/*
* 把值格式化为货币,货币符号取决与程序所在的PC的区域自动设置
* 精度说明符 设置的是小数位的位数;如果不设置 精度说明符 那么会自动取2位小数。
*/
Console.WriteLine("十进制D、d(不区分大小写) |{0, 15 :D}|{1, -15 :d4}|", 107, -29);
/*
* 十进制数字字符串,需要注意,只能和整数类型配合使用;如果是其他类型会报错,提示格式说明符无效
* 精度说明符 设置的是整个字符串的位数;如果实际数字位数小于给出的精度说明位数,则在左边补0。
*/
Console.WriteLine("定点F、f(不区分大小写) |{0, 15 :F}|{1, -15 :f3}|", first, second);
/*
* 带有小数的十进制数字字符串
* 精度说明符 设置的是小数位的位数;如果不设置 精度说明符 那么会自动取2位小数。
*/
Console.WriteLine("常规G、g(不区分大小写) |{0, 15 :G}|{1, -15 :g4}|", first, second);
/*
* 在没有说明符的情况下,会根据值转换为定点或科学记数法表示的紧凑形式
* 精度说明符 设置的是整个字符串的位数;如果实际数字位数小于给出的精度说明位数,并不会进行任何补0操作;不设置,则按原样显示
*/
Console.WriteLine("十六进制X、x(区分大小写) |{0, 15 :x5}|{1, -15 :X}|", 107, -29);
/*
* 十六进制数字的A~F会根据格式说明符的大小写来进行匹配,X 匹配A~F ;x 匹配a~f
* 精度说明符 设置的是整个字符串的位数;如果实际数字位数小于给出的精度说明位数,则在左边补0。补充:字符串的位数是按转成了十六进制的字符串的位数。
*/
Console.WriteLine("数字N、n(不区分大小写) |{0, 15 :N}|{0, -15 :n5}|", first, second);
/*
* 和定点相似,但在每三个数字的一组中有逗号或空格分隔符,从小数点向左数,取决于程序所在的PC的区域设置
* 精度说明符 设置的是小数位的位数;如果不设置 精度说明符 那么会自动取2位小数。
*/
Console.WriteLine("百分比P、p(不区分大小写) |{0, 15 :P}|{1, -15 :p6}|", first, second);
/*
* 表示百分比,数字会乘以100
* 精度说明符 设置的是小数位的位数;如果不设置 精度说明符 那么会自动取2位小数。
*/
Console.WriteLine("往返过程R、r(不区分大小写)|{0, 15 :R}|{1, -15 :r3}|", first, second);
/*
* 保证输出字符串后,如果使用Parse方法将字符串转化成数字,则该值与原始值一样。
* 精度说明符 设置了也并没有对数据有影响。
*/
Console.WriteLine("科学记数法E、e(区分大小写)|{0, 15 :E}|{1, -15 :e10}|", first, second);
/*
* 具有尾数和指数的科学记数法。指数前加的字母E 是根据说明符的大小写进行匹配的
* 精度说明符 设置的是小数位的位数。
*/
Console.ReadKey();
}
}
}
![复制代码](https://common.cnblogs.com/images/copycode.gif)
学习感受:
我现在的工作中还没有遇见这些的使用。
Posted on
2018-05-18 16:13 Puede 阅读(50) 评论(
小程序页面代码因为某些人力不可控的原因代码丢失了,这里简单说明一下
调用小程序APIwx.chooseImage(OBJECT)选择相册或拍摄照片,会返回 tempFilePaths,之后通过wx.uploadFile(OBJECT)把照片传至后台,在服务器后台进行Base64编码,
小程序目前不支持在前台页面和本地进行Base64编码的
下面贴一下后台编码方法(注:本方法只进行Base64编码,图片不保存至服务器)
public void ProcessRequest(HttpContext context)
{
string dataType = context.Request["dataType"];
string responseData = "";
switch (dataType)
{
case "BaseImg":
{
try
{
System.IO.Stream s = context.Request.Files["uploadiamges"].InputStream;
System.Drawing.Bitmap image = new System.Drawing.Bitmap(s);
MemoryStream ms = new MemoryStream();
image.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
byte[] arr = new byte[ms.Length];
ms.Position = 0;
ms.Read(arr, 0, (int)ms.Length);
ms.Close();
String strbaser64 = Convert.ToBase64String(arr);
responseData = strbaser64; //strbaser64 即是编码后的 base64字符串
} catch(Exception ex) {
responseData = ex.Message;
}
break;
}
}
context.Response.Write(responseData);
}
Posted on
2018-05-18 15:46 Puede 阅读(14) 评论(
表格代码
<tr>
<td>
<input type="text" style="border: none; text-align: center;" class="chengwei" /></td>
<td>
<input type="text" style="border: none; text-align: center;" class="xingming" /></td>
<td>
<input type="text" style="text-align: center; border: none" class="age" /></td>
<td>
<input type="text" style="text-align: center; border: none" class="zhengzhimm" /></td>
<td>
<input type="text" style="text-align: center; border: none" class="gongzuodanwei" /></td>
<td>
<input type="text" style="text-align: center; border: none" class="zhiwu" /></td>
</tr>
js取值-------------------------
//称谓
function getchengweival() {
var reVal = '';
$('.chengwei').each(function () {
if ($(this).val() != "" & $(this).val() != undefined) {
reVal += $(this).val() + ",";
}
});
reVal = reVal.substr(0, reVal.length - 1);
return reVal.split(',');
}
赋值
$(function () {
var vals = ‘1,2,3;2,3.4’;
if (vals.length > 0) {
for (var i = 0; i < vals.length; i++) {
var cs = vals[i].split(',');
$('.chengwei').each(function (x) {
if (x == i) {
$(this).val(cs[0])
}
});
}
}
});
AES加密及解密
声明密钥级偏移向量--------
/// <summary>
/// 加密密钥
/// </summary>
private static readonly string Default_AES_Key = "";
/// <summary>
/// 偏移向量
/// </summary>
private static byte[] Keys = { };
![复制代码](https://common.cnblogs.com/images/copycode.gif)
加密算法--------/// <summary>
/// 对称加密算法AES RijndaelManaged加密(RijndaelManaged(AES)算法是块式加密算法)
/// </summary>
/// <param name="encryptString">待加密字符串</param>
/// <returns>加密结果字符串</returns>
public static string AES_Encrypt(string encryptString)
{
return AES_Encrypt(encryptString, Default_AES_Key);
}
/// <summary>
/// 对称加密算法AES RijndaelManaged加密(RijndaelManaged(AES)算法是块式加密算法)
/// </summary>
/// <param name="encryptString">待加密字符串</param>
/// <param name="encryptKey">加密密钥,须半角字符</param>
/// <returns>加密结果字符串</returns>
public static string AES_Encrypt(string encryptString, string encryptKey)
{
encryptKey = GetSubString(encryptKey, 32, "");
encryptKey = encryptKey.PadRight(32, ' ');
RijndaelManaged rijndaelProvider = new RijndaelManaged();
rijndaelProvider.Key = Encoding.UTF8.GetBytes(encryptKey.Substring(0, 32));
rijndaelProvider.IV = Keys;
ICryptoTransform rijndaelEncrypt = rijndaelProvider.CreateEncryptor();
byte[] inputData = Encoding.UTF8.GetBytes(encryptString);
byte[] encryptedData = rijndaelEncrypt.TransformFinalBlock(inputData, 0, inputData.Length);
return Convert.ToBase64String(encryptedData);
}
![复制代码](https://common.cnblogs.com/images/copycode.gif)
![复制代码](https://common.cnblogs.com/images/copycode.gif)
解密算法--------
/// <summary>
/// 对称加密算法AES RijndaelManaged解密字符串
/// </summary>
/// <param name="decryptString">待解密的字符串</param>
/// <returns>解密成功返回解密后的字符串,失败返源串</returns>
public static string AES_Decrypt(string decryptString)
{
return AES_Decrypt(decryptString, Default_AES_Key);
}
/// <summary>
/// 对称加密算法AES RijndaelManaged解密字符串
/// </summary>
/// <param name="decryptString">待解密的字符串</param>
/// <param name="decryptKey">解密密钥,和加密密钥相同</param>
/// <returns>解密成功返回解密后的字符串,失败返回空</returns>
public static string AES_Decrypt(string decryptString, string decryptKey)
{
try
{
decryptKey = GetSubString(decryptKey, 32, "");
decryptKey = decryptKey.PadRight(32, ' ');
RijndaelManaged rijndaelProvider = new RijndaelManaged();
rijndaelProvider.Key = Encoding.UTF8.GetBytes(decryptKey);
rijndaelProvider.IV = Keys;
ICryptoTransform rijndaelDecrypt = rijndaelProvider.CreateDecryptor();
byte[] inputData = Convert.FromBase64String(decryptString);
byte[] decryptedData = rijndaelDecrypt.TransformFinalBlock(inputData, 0, inputData.Length);
return Encoding.UTF8.GetString(decryptedData);
}
catch
{
return string.Empty;
}
}
![复制代码](https://common.cnblogs.com/images/copycode.gif)
Posted on
2017-07-26 16:26 Puede 阅读(17) 评论(
火星坐标系 (GCJ-02)转换为百度坐标系 (BD-09)
function GCJ02toBD09(lng, lat) {
var x_pi = 3.14159265358979324 * 3000.0 / 180.0;
var z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_pi);
var theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_pi);
var bd_lng = z * Math.cos(theta) + 0.0065;
var bd_lat = z * Math.sin(theta) + 0.006;
return [bd_lng,bd_lat]
}
计算两个坐标点之间的距离
function getDistance(lat1, lng1, lat2, lng2) {
var dis = 0;
var radLat1 = toRadians(lat1);
var radLat2 = toRadians(lat2);
var deltaLat = radLat1 - radLat2;
var deltaLng = toRadians(lng1) - toRadians(lng2);
var dis = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(deltaLat / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(deltaLng / 2), 2)));
return dis * 6378137;
function toRadians(d) { return d * Math.PI / 180; }
}
已知一点经纬度A,和与另一点B的距离和方位角,求B点的经纬度
/// <summary>
///
/// </summary>
/// <param name="distance">距离</param>
/// <param name="lng">A坐标经度</param>
/// <param name="lati">A坐标纬度</param>
/// <param name="angle">角度 ,以正北方向顺时针开始</param>
/// <returns></returns>
private string ConvertDistanceToLogLat(int distance, string lng, string lati, double angle)
{
double juli = (float)distance / 1000;
string logLat = null;
double lng1 = Convert.ToDouble(lng.Trim());
double lat1 = Convert.ToDouble(lati.Trim());
//将距离转换成经度的计算公式
double lon = lng1 + (juli * Math.Sin(angle * Math.PI / 180)) / (111 * Math.Cos(lat1 * Math.PI / 180));
//将距离转换成纬度的计算公式
double lat = lat1 + (juli * Math.Cos(angle * Math.PI / 180)) / 111;
logLat = lon + "," + lat;
return logLat;
}
将double值转换成度分秒字符串
private string ConvertLogLatToString(double lon)
{
string resut = null;
string temp = lon.ToString();
string[] du = temp.Split('.');
resut += du[0] + "°";
double fen = (lon - Convert.ToDouble(du[0])) * 60;
string[] fen_Arrary = fen.ToString().Split('.');
resut += fen_Arrary[0] + "′";
if (fen_Arrary.Length > 1)
{
double second = Math.Round((fen - Convert.ToDouble(fen_Arrary[0])) * 60, 0);
resut += second.ToString() + "″";
}
return resut;
}