彻底解密C++宽字符:1、从char到wchar_t

本文深入解析C++中字符与宽字符的区别,包括char到wchar_t的转换机制,本地化策略集(locale)的作用,以及不同系统与编译器对宽字符的处理方式。
<本文PDF文档下载>

“这个问题比你想象中复杂”
(我也学下BS的风格,虽然这句话是我自己临时想说的。^^)

从字符到整数

char是一种整数类型,这句话的含义是,char所能表示的字符在C/C++中都是整数类型。好,接下来,很多文章就会举出一个典型例子,比如,'a'的数值就是0x61。这种说法对吗?如果你细心的读过K&R和BS对于C和C++描述的原著,你就会马上反驳道,0x61只是'a'的ASCII值,并没有任何规定C/C++的char值必须对应ASCII。C/C++甚至没有规定char占几位,只是规定了sizeof(char)等于1。
当然,目前大部分情况下,char是8位的,并且,在ASCII范围内的值,与ASCII对应。

本地化策略集(locale)

“将'a'翻译成0x61的整数值”,“将ASCII范围内的编码与char的整数值对应起来”,类似这样的规定,是特定系统和特定编译器制定的,C/C++中有个特定的名词来描述这种规定的集合:本地化策略集(locale。也有翻译成“现场”)。而翻译——也就是代码转换(codecvt)只是这个集合中的一个,C++中定义为策略(facet。也有翻译为“刻面”)

C/C++的编译策略

“本地化策略集”是个很好的概念,可惜在字符和字符串这个层面上,C/C++并不使用(C++的locale通常只是影响流(stream)),C/C++使用更直接简单的策略:硬编码。
简单的说,字符(串)在程序文件(可执行文件,非源文件)中的表示,与在程序执行中在内存中的表示一致。考虑两种情况:
A、char c = 0x61;
B、char c = 'a';
情况A下,编译器可以直接认识作为整数的c,但是在情况B下,编译器必须将'a'翻译成整数。编译器的策略也很简单,就是直接读取字符(串)在源文件中的编码数值。比如:
const char* s = "中文abc";
这段字符串在GB2312(Windows 936),也就是我们的windows默认中文系统源文件中的编码为:
0xD6   0xD0   0xCE 0xC4 0x61 0x62 0x63
在UTF-8,也就是Linux默认系统源文件中的编码为:
0xE4   0xB8   0xAD   0xE6   0x96   0x87   0x61   0x62   0x63
一般情况下,编译器会忠实于源文件的编码为s赋值,例外的情况比如VC会自作聪明的把大部分其他类型编码的字符串转换成GB2312(除了像UTF-8 without signature这样的幸存者)。
程序在执行的时候,s也就保持是这样的编码,不会再做其他的转换。

宽字符 wchar_t
正如char没有规定大小,wchar_t同样没有标准限定,标准只是要求一个wchar_t可以表示任何系统所能认识的字符,在win32中,wchar_t为16位;Linux中是32位。wchar_t同样没有规定编码,因为Unicode的概念我们后面才解释,所以这里只是提一下,在win32中,wchar_t的编码是UCS-2BE;而Linux中是UTF-32BE(等价于UCS-4BE),不过简单的说,在16位以内,一个字符的这3种编码值是一样的。因此:
const wchar_t* ws = L"中文abc";
的编码分别为:
0x4E2D   0x6587    0x0061   0x0062   0x0063                                                //win32,16位
0x00004E2D   0x00006587    0x00000061   0x00000062   0x00000063        //Linux,32位
大写的L是告诉编译器:这是宽字符串。所以,这时候是需要编译器根据locale来进行翻译的。
比如,在Windows环境中,编译器的翻译策略是GB2312到UCS-2BE;Linux环境中的策略是UTF-8到UTF-32BE。
这时候就要求源文件的编码与编译器的本地化策略集中代码翻译的策略一致,例如VC只能读取GB2312的源代码(这里还是例外,VC太自作聪明了 ,会将很多其他代码在编译时自动转换成GB2312),而gcc只能读取UTF-8的源代码(这里就有个尴尬,MinGW运行win32下,所以只有GB2312系统才认;而MinGW却用gcc编写,所以自己只认UTF-8,所以结果就是,MinGW的宽字符被废掉了)。
宽字符(串)由编译器翻译,还是被硬编码进程序文件中。
#define OFFSET_DECRYPTION_DWORD 0x5A33DC0 #define OFFSET_DECRYPTION_X 0x5A33DA8 #define OFFSET_DECRYPTION_X_PLUS_10 0x5A33DB8 std::wstring decryptStringValue(uintptr_t a1, bool notLock) {//notLock is for when the encrypted value doesn't move. For locks, every time you try again it switches between 2 locations if (!Globals::Mem.RPM<bool>(a1 + 0x30) || notLock) { __int64 v17; // rax int v18; // edi int v19; // er10 __int64 v20; // r9 int v21; // edx int v22; // ecx WORD* v23; // rcx __int64 v24; // rdx __int64 v25; // r8 int v26; // ebx void* v27; // [rsp+20h] [rbp-40h] BYREF int v28; // [rsp+28h] [rbp-38h] int v29; // [rsp+2Ch] [rbp-34h] char v30[16]; // [rsp+30h] [rbp-30h] BYREF *(DWORD*)(__int64)(v30 + 8) = Globals::Mem.RPM<DWORD>(a1 + 0x10 + 0x8); if (*(DWORD*)(__int64)(v30 + 8) > 0) { *(__int64*)v30 = (__int64)VirtualAlloc(NULL, *(DWORD*)(__int64)(v30 + 8) * 2, MEM_COMMIT, PAGE_READWRITE); Globals::Mem.RPM_Array(Globals::Mem.RPM<uintptr_t>(a1 + 0x10), *(byte**)v30, *(DWORD*)(__int64)(v30 + 8) * 2); v17 = (__int64)v30; v18 = Globals::Mem.RPM<DWORD>(Globals::BaseAddress + OFFSET_DECRYPTION_DWORD); v19 = 0; v20 = 0i64; while (1) { LABEL_25: v21 = *(DWORD*)(v17 + 8); v22 = v21 - 1; if (!v21) v22 = 0; if (v19 >= v22) break; v23 = (WORD*)Globals::Mem.RPM<uintptr_t>(Globals::BaseAddress + OFFSET_DECRYPTION_X_PLUS_10); v24 = -1i64; v25 = Globals::Mem.RPM<uintptr_t>(Globals::BaseAddress + OFFSET_DECRYPTION_X_PLUS_10) + 2i64 * v18; if (v18) v25 -= 2i64; if (Globals::Mem.RPM<uintptr_t>(Globals::BaseAddress + OFFSET_DECRYPTION_X_PLUS_10) != v25) { while (1) { ++v24; if (*(WORD*)(*(uintptr_t*)v17 + v20) == Globals::Mem.RPM<WORD>((uintptr_t)v23)) break; if (++v23 == (WORD*)v25) { ++v19; v20 += 2i64; goto LABEL_25; } } *(WORD*)(*(uintptr_t*)v17 + v20) = Globals::Mem.RPM<WORD>(Globals::Mem.RPM<uintptr_t>(Globals::BaseAddress + OFFSET_DECRYPTION_X) + 2 * v24); } ++v19; v20 += 2i64; } wchar_t* buff = *(wchar_t**)v17; return std::wstring(buff); } } else { __int64 v17; // rax int v18; // edi int v19; // er10 __int64 v20; // r9 int v21; // edx int v22; // ecx WORD* v23; // rcx __int64 v24; // rdx __int64 v25; // r8 int v26; // ebx void* v27; // [rsp+20h] [rbp-40h] BYREF int v28; // [rsp+28h] [rbp-38h] int v29; // [rsp+2Ch] [rbp-34h] char v30[16]; // [rsp+30h] [rbp-30h] BYREF *(DWORD*)(__int64)(v30 + 8) = Globals::Mem.RPM<DWORD>(a1 + 0x20 + 0x8); if (*(DWORD*)(__int64)(v30 + 8) > 0) { *(__int64*)v30 = (__int64)VirtualAlloc(NULL, *(DWORD*)(__int64)(v30 + 8) * 2, MEM_COMMIT, PAGE_READWRITE); Globals::Mem.RPM_Array(Globals::Mem.RPM<uintptr_t>(a1 + 0x20), *(byte**)v30, *(DWORD*)(__int64)(v30 + 8) * 2); v17 = (__int64)v30; v18 = Globals::Mem.RPM<DWORD>(Globals::BaseAddress + OFFSET_DECRYPTION_DWORD); v19 = 0; v20 = 0i64; while (1) { LABEL_26: v21 = *(DWORD*)(v17 + 8); v22 = v21 - 1; if (!v21) v22 = 0; if (v19 >= v22) break; v23 = (WORD*)Globals::Mem.RPM<uintptr_t>(Globals::BaseAddress + OFFSET_DECRYPTION_X_PLUS_10); v24 = -1i64; v25 = Globals::Mem.RPM<uintptr_t>(Globals::BaseAddress + OFFSET_DECRYPTION_X_PLUS_10) + 2i64 * v18; if (v18) v25 -= 2i64; if (Globals::Mem.RPM<uintptr_t>(Globals::BaseAddress + OFFSET_DECRYPTION_X_PLUS_10) != v25) { while (1) { ++v24; if (*(WORD*)(*(uintptr_t*)v17 + v20) == Globals::Mem.RPM<WORD>((uintptr_t)v23)) break; if (++v23 == (WORD*)v25) { ++v19; v20 += 2i64; goto LABEL_26; } } *(WORD*)(*(uintptr_t*)v17 + v20) = Globals::Mem.RPM<WORD>(Globals::Mem.RPM<uintptr_t>(Globals::BaseAddress + OFFSET_DECRYPTION_X) + 2 * v24); } ++v19; v20 += 2i64; } wchar_t* buff = *(wchar_t**)v17; return std::wstring(buff); } } return L"0"; } 这是一个字符串解密伪代码 其中一些代码或者变量 参数可能是错误的 Vk-$iliC解密出来是浮点数6.59 你能通过这些信息把UCkx$&-Vl解密出来吗
07-05
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值