您的位置:首页 > 移动开发 > 微信开发

Visual Studio 2017中使用正则修改部分内容 如何使用ILAsm与ILDasm修改.Net exe(dll)文件 C#学习-图解教程(1):格式化数字字符串 小程序开发之图片转Base64(C#、.Net) jquery遍历table为每一个单元格取值及赋值 。net加密解密相关方法 .net关于坐标之间一些简单操作

2018-10-18 00:05 3535 查看

Visual Studio 2017中使用正则修改部分内容

 

最近在项目中想实现一个小工具,需要根据类的属性

<summary>
的内容加上相应的
[Description]
特性,需要实现的效果如下

修改前:

/// <summary>
/// 条形码
/// </summary>
public List<GoodsBarcodeEditModel> Barcodes { get; set; }

修改后:

/// <summary>
/// 条形码
/// </summary>
[Description("条形码")]
public List<GoodsBarcodeEditModel> Barcodes { get; set; }

作为一个非处女座,但是有处女座特点的程序猿,牢记着DRY(Don't Repeat Yourself), 不想把时间浪费在不停的Copy-Paste上,于是想着VS的Find and Replace里的正则会不会有支持部分替换的功能,顺着这个想法,找到了微软文档《正则表达式中的替代》,里面的

$数值
替换捕获组下好满足我的需求。接下来的工作就简单了

(///\s<.*\n*)(\s*)(///)(\s*)(\w*)(\n*)(\s*///.*)
代码段正则分组分组序号
/// <summary>
(///\s<.*\n*)
$1
空格
(\s*)
$2
///
(///)
$3
空格
(\s*)
$4
条形码
(\w*)
$5
换行
(\n*)
$6
/// </summary>
(\s*///.*)
$7

因此使用如下组合,就是得到我们想要的结果

$1$2$3$4$5$6$7 [Description("$5")]

最终效果如下:

 

     

如何使用ILAsm与ILDasm修改.Net exe(dll)文件

 

一、背景

最近项目组新上项目,交付的时间比较急迫,原本好的分支管理习惯没有遵守好,于是出现下面状况:

  1. 多个小伙伴在不同的分支上开发。
  2. 原本QA环境也存在一个阻碍性的bug A
  3. 一位同事在QA环境发布了新的代码,引入了新bug B
  4. 回滚QA能修改bug B,但是对于bugA却无能为力
  5. 同时,混乱的代码管理已经导致无法确定原始发布包对应的代码版本。

最终陷入了两难的地步,既不能发布新包,回滚也无法解决问题。
好在之前了解到如何使用微软官方工具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所示:

最后会生成相应的.il与.res文件。

四、修改IL

打开.il文件,会看到如下代码(节选)

.method private hidebysig static bool  Login(string userName,
string password) cil managed
{
// Code size       43 (0x2b)
.maxstack  2
.locals init ([0] bool V_0,
[1] bool V_1)
IL_0000:  nop
IL_0001:  ldarg.0
IL_0002:  ldstr      "johnny"
IL_0007:  callvirt   instance bool [mscorlib]System.String::Equals(string)
IL_000c:  brfalse.s  IL_001b

IL_000e:  ldarg.1
IL_000f:  ldstr      "123456"
IL_0014:  callvirt   instance bool [mscorlib]System.String::Equals(string)
IL_0019:  br.s       IL_001c

IL_001b:  ldc.i4.0
IL_001c:  stloc.0
IL_001d:  ldloc.0
IL_001e:  brfalse.s  IL_0025

IL_0020:  nop
IL_0021:  ldc.i4.0
IL_0022:  stloc.1
IL_0023:  br.s       IL_0029

IL_0025:  ldc.i4.0
IL_0026:  stloc.1
IL_0027:  br.s       IL_0029

IL_0029:  ldloc.1
IL_002a:  ret
}// end of method Program::Login

这个就是

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

}// end of method Program::Login

五、使用ILAsm生成exe

修改保存完.il文件以后,接下来的工作就是利用 ILAsm 让.il文件生成重新生成.exe可执行文件了,在Console中执行如下命令

ilasm ILAsmAndILDasmDemo.il /output:ILAsmAndILDasmDemo_1.exe
// 如果修改的是dll文件,需要加上参数 /dll

成功以后会生成一个ILAsmAndILDasmDemo_1.exe文件,执行这个文件,我们可以看到,现在已经显示"登录成功"了。

使用反编译工具 dotPeekI查看新生成的ILAsmAndILDasmDemo_1.exe文件,我们能够看到,

Login(string userName, string password)
已经直接
return true
了。如下图,

六、总结

其它,上面所做的事情其实也是《CLR via C#》中提到强名能够 防止代码被不怀好意的人篡改 的一个反面教材了。通过这个例子,大家应该对代码被篡改的风险也有一定的认识了,所以如果大家需要将自己的.dll(.exe)文件露给别人,最好还是打上强名,防止别人恶意篡改你的代码,导致不必要的损失。

经过这次事件,也证明了多了解下底层还是很有必要的,说不定哪天就用上了。平时再忙,也不能只限于只业务和编程语言层面,还需要对底层有一定的了解,这样才能知其然与知其所以然。

虽然此次在不修改c#代码的情况下完美解决QA的环境问题,但是这种方法也只限于小范围的改动,只试用于救急。所以给我的教训是分支管理规范才是王道,要能做到随时可发布,随时可回滚才行,这样才能完全避免再次出现这样尴尬的情况了。

 

 

 

C#学习-图解教程(1):格式化数字字符串

Posted on 2018-05-18 18:39 Leo_olivine 阅读(9) 评论(0) 编辑 收藏

学习内容:C#;学习书籍:图解教程(中文第四版)。

目录:第二章 C#编程概述 -----> 2.7 从程序中输出文本 -----> 2.7.5格式化数字字符串

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(); } } }

学习感受:

  我现在的工作中还没有遇见这些的使用。

 

 

小程序开发之图片转Base64(C#、.Net)

Posted on 2018-05-18 16:13 Puede 阅读(50) 评论(0) 编辑 收藏

小程序页面代码因为某些人力不可控的原因代码丢失了,这里简单说明一下

调用小程序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);

}




jquery遍历table为每一个单元格取值及赋值

Posted on 2018-05-18 15:46 Puede 阅读(14) 评论(0) 编辑 收藏

表格代码

<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 = { };
 
加密算法--------/// <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);
}
 
解密算法--------
/// <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; } }
       

.net关于坐标之间一些简单操作

Posted on 2017-07-26 16:26 Puede 阅读(17) 评论(0) 编辑 收藏 火星坐标系 (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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐