【汇编】命名混淆:再也不怕名称冲突了以及不能“见名思义”的导出表
本帖最后由 Mat 于 2023-3-29 03:29 编辑【原创】来源:https://www.0xaa55.com/thread-27352-1-1.html
转载请注明出处。
写汇编程序的麻烦之一就是怕定义的标号名、变量名以及过程名产生名称冲突,所以通常会做一些编码规范的约定,防止以上问题的产生。不过总觉得心理负担加重了,不能愉快的写代码。或者辛辛苦苦的编写了一个dll,导出了一些函数,但别人一看导出表导出的函数名称就大概能猜测出某个函数是干什么用的,会不会觉得很不爽?如果说中了各位的痛处,那就请继续往下看。
利用MASM的宏函数进行名称混淆,混淆的规则很简单,就是进行简单的字符映射,准备2张字符序列表:
C_VARNAME_DEFAULT_ALPHABETEQU <abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPTRSTUVWXYZ0123456789_$?@>
C_VARNAME_SHUFFLE_ALPHABET1 EQU <Yx4Om7LuN2XTI38S_wAtnalRhi1CcspFEoqzDGrBZ06gUHdeMKVvkTjJ5Pbf9Wy@$?>
比如要将 Sub 进行混淆,则从第一张表中查找对应的索引,然后映射到第二张表中,就变成了:Unx,之后再附加指定的前缀,就妥了。
可以看到,生成的TestDll.dll中导出表中的某些全局变量及函数名称已经混淆了:
以下是VarName.mac的实现:
;;=============================================================================
;;作者:Mat
;;网址:https://www.0xaa55.com/?8271
;;请保留原作者信息,否则视为侵权。
;;-----------------------------------------------------------------------------
IFNDEF __VARNAME_MAC_
__VARNAME_MAC_ EQU <1>
IFNDEF VAR_NAME_PREFIX
VAR_NAME_PREFIX TEXTEQU <VARNAME>
ELSE
IFIDN VAR_NAME_PREFIX, <>
VAR_NAME_PREFIX TEXTEQU <VARNAME>
ENDIF
ENDIF
; private
C_VARNAME_DEFAULT_ALPHABETEQU <abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPTRSTUVWXYZ0123456789_$?@>
C_VARNAME_SHUFFLE_ALPHABET1 EQU <Yx4Om7LuN2XTI38S_wAtnalRhi1CcspFEoqzDGrBZ06gUHdeMKVvkTjJ5Pbf9Wy@$?>
C_VARNAME_SHUFFLE_ALPHABET2 EQU <5irBYsdzMUakRcKg1IDE9C_vftmNeTbX6ZpyS3J7Hho2LGV08FAnqjux4lwTWPO?@$>
; private
C_VARNAME_ALPHABET_INDEX_MIN EQU <1>
C_VARNAME_ALPHABET_INDEX_MAX EQU @SizeStr(%C_VARNAME_DEFAULT_ALPHABET)
; private
M_VARNAME_CharMapping MACRO chr:REQ, shuffleAlphabet
LOCAL index
index TEXTEQU @InStr(, %C_VARNAME_DEFAULT_ALPHABET, chr)
IF index
EXITM @SubStr(shuffleAlphabet, index, 1)
ENDIF
EXITM VAR_NAME_PREFIX
ENDM
; public
MVN MACRO varname:REQ
LOCAL symname, chr
symname TEXTEQU @CatStr(<_>, %VAR_NAME_PREFIX, <_>)
% FOR num, <1,2>
% FORC chr, varname
symname TEXTEQU @CatStr(%symname, M_VARNAME_CharMapping(chr, %C_VARNAME_SHUFFLE_ALPHABET&num))
ENDM
ENDM
symname TEXTEQU @CatStr(%symname, <_>, %(@SizeStr(%symname) - @SizeStr(%VAR_NAME_PREFIX) - 2), <_>)
EXITM <symname>
ENDM
;% ECHO Generate VarName: MVN(<gHello>)
ENDIF
只需要引入 VarName.mac,调用宏函数 MVN 即可。
但是有一个问题,就是如果用于 dll 的话,需要生成相应在的 .def 文件,这个我用 VBScript 实现了。Def.vbs 代码如下:
'=============================================================================
'作者:Mat
'网址:https://www.0xaa55.com/?8271
'请保留原作者信息,否则视为侵权。
'-----------------------------------------------------------------------------
Option Explicit
Const DEFAULT_ALPHABET= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPTRSTUVWXYZ0123456789_$?@"
Const SHUFFLE_ALPHABET1 = "Yx4Om7LuN2XTI38S_wAtnalRhi1CcspFEoqzDGrBZ06gUHdeMKVvkTjJ5Pbf9Wy@$?"
Const SHUFFLE_ALPHABET2 = "5irBYsdzMUakRcKg1IDE9C_vftmNeTbX6ZpyS3J7Hho2LGV08FAnqjux4lwTWPO?@$"
Const VAR_NAME_PREFIX = "VARNAME"
Const ALPHABET_INDEX_MIN = 1
DimALPHABET_INDEX_MAX: ALPHABET_INDEX_MAX = Len(DEFAULT_ALPHABET)
Function CharMapping(char, shuffleAlphabet)
Dim pos: pos = InStr(1, DEFAULT_ALPHABET, char, vbBinaryCompare)
If pos = 0 Then
CharMapping = VAR_NAME_PREFIX
Else
CharMapping = Mid(shuffleAlphabet, pos, 1)
End If
End Function
Function StrMapping(vname, shuffleAlphabet)
Dim symname: symname = ""
Dim i
For i = 1 To Len(vname) Step 1
symname = symname & CharMapping(Mid(vname, i, 1), shuffleAlphabet)
Next
StrMapping = symname
End Function
Function VarName(vname, prefix)
Dim strPrefix
If IsEmpty(prefix) Or IsNull(prefix) Or prefix="" Then
strPrefix = VAR_NAME_PREFIX
Else
strPrefix = prefix
End If
Dim symname: symname = "_" & strPrefix & "_"
symname = symname & StrMapping(vname, SHUFFLE_ALPHABET1)
symname = symname & StrMapping(vname, SHUFFLE_ALPHABET2)
symname = symname & "_" & (Len(symname) - Len(strPrefix) - 2) & "_"
VarName = symname
End Function
Dim fso: Set fso = CreateObject("Scripting.FileSystemObject")
If WScript.Arguments.Count < 1 Then
WScript.Echo "缺少参数:", Chr(13), Chr(10), Chr(9), "Usage: cscript Def.vbs <Dll Name>.def "
WScript.Quit
End If
Dim strPrefix: strPrefix = ""
If WScript.Arguments.Count > 1 Then
strPrefix = Trim(WScript.Arguments(1))
If Len(strPrefix) < 1 Then
strPrefix = VAR_NAME_PREFIX
End If
End If
Dim defFileName: defFileName = Trim(WScript.Arguments(0))
If fso.FileExists(defFileName) Then
fso.DeleteFile defFileName, True
End If
Const ForReading = 1, ForWriting = 2
Dim defTplFile: Set defTplFile = fso.OpenTextFile("def.tpl", ForReading)
Dim defFile: Set defFile = fso.OpenTextFile(defFileName, ForWriting, True)
defFile.WriteLine "LIBRARY " & fso.GetBaseName(defFileName) & ".dll"
defFile.WriteLine "EXPORTS"
Do While Not defTplFile.AtEndOfStream
Dim line: line = Trim(defTplFile.ReadLine)
If line <> "" Then
If Left(line, 1) = "!" Then
line = Mid(line, 2)
Else
Dim i
Dim strName: strName = ""
Dim strChar
For i = 1 To Len(line)
strChar = Mid(line, i, 1)
If strChar = " " Or strChar = "\t" Or strChar = "\r" Or strChar = "\n" Then Exit For
strName = strName & strChar
Next
line = VarName(strName, strPrefix) & Mid(line, i)
End If
defFile.WriteLine Chr(9) & line
End If
Loop
defTplFile.Close
defFile.Close
Set defTplFile = Nothing
Set defFile = Nothing
Set fso = Nothing
事实上 Def.vbs 也实现了 VarName.mac 的混淆算法,在编译 dll 前,执行 Def.vbs 生成相应的模块定义文件 .def。Def.tpl 是用于生成 .def 的模板,用于定义导出的全局变量及函数,模板中以 ! 开头的名称表示不进行名称混淆,接下来进行编译:
cscript /nologo Def.vbs $(DLL).def $(VAR_NAME_PREFIX)
并且可以指定名称前缀,当然在编译的时候也同样需要指定,帖上 Makefile:
NAME=Test
OBJS=$(NAME).obj
RES=$(NAME).res
DLL=TestDll
VAR_NAME_PREFIX=0x3f3f3f3f
ML_FLAG=/nologo /c /coff /DVAR_NAME_PREFIX=$(VAR_NAME_PREFIX)
LINK_FLAG=/nologo /subsystem:windows
$(NAME).exe: $(OBJS) $(RES) $(DLL).obj $(DLL).dll
link32 $(LINK_FLAG) /out:$(NAME).exe $(OBJS) $(RES)
start $(NAME).exe
VarNameDll.obj:
ml $(ML_FLAG) $(DLL).asm
.asm.dll:
cscript /nologo Def.vbs $(DLL).def $(VAR_NAME_PREFIX)
link32 $(LINK_FLAG) /dll /def:$(DLL).def $(DLL).obj
.asm.obj:
ml $(ML_FLAG) $<
.rc.res:
rc $<
clean:
del /q *.obj > nul 2>&1
del /q *.pdb > nul 2>&1
del /q *.ilk > nul 2>&1
del /q *.lst > nul 2>&1
del /q *.map > nul 2>&1
del /q *.res > nul 2>&1
del /q *.exp > nul 2>&1
del /q *.lib > nul 2>&1
del /q *.dll > nul 2>&1
del /q *.exe > nul 2>&1
用 nmake 进行编译:
nmake clean && nmake
接下来是写了下dll,并且在一个主模块中调用这个dll,代码很简单,就直接帖上来了。
TestDll.asm:
.386
.model flat, stdcall
option casemap:none
;=============================================================================
;作者:Mat
;网址:https://www.0xaa55.com/?8271
;请保留原作者信息,否则视为侵权。
;-----------------------------------------------------------------------------
include windows.inc
include user32.inc
includelib user32.lib
include TestDll.inc
public gTitle
public MVN(<gHello>)
public MVN(<gGoodBye>)
.data
gTitle BYTE 'VarName Test', 0
MVN(<gHello>) BYTE 'Hello, World!', 0
MVN(<gGoodBye>) BYTE 'Good Bye My Love!', 0
.code
DllMain PROC hinstDLL:HINSTANCE, fdwReason:DWORD, lpvReserved:LPVOID
mov eax, TRUE
ret
DllMain ENDP
MVN(<SayHello>) PROC STDCALL _lpTitle:PBYTE
INVOKE MessageBox, NULL, ADDR MVN(<gHello>), _lpTitle, MB_OK
ret
MVN(<SayHello>) ENDP
_SayGoodBye PROC STDCALL _lpMessage:PBYTE
INVOKE MessageBox, NULL, _lpMessage, ADDR gTitle, MB_OK
ret
_SayGoodBye ENDP
end DllMain
Test.asm:
.386
.model flat, stdcall
option casemap:none
;=============================================================================
;作者:Mat
;网址:https://www.0xaa55.com/?8271
;请保留原作者信息,否则视为侵权。
;-----------------------------------------------------------------------------
include windows.inc
include kernel32.inc
include user32.inc
include msvcrt.inc
includelib kernel32.lib
includelib user32.lib
includelib msvcrt.lib
include Test.inc
includelib TestDll.lib
.data
hDllInstance HINSTANCE 0
.code
start:
INVOKE GetModuleHandle, M_TEXT(<TestDll>)
mov hDllInstance, eax
; SayHello
INVOKE GetProcAddress, hDllInstance, M_TEXT(<gTitle>)
INVOKE MVN(<SayHello>), eax
; _SayGoodBye
INVOKE GetProcAddress, hDllInstance, M_TEXT(%MVN(<gGoodBye>))
INVOKE _SayGoodBye, eax
INVOKE ExitProcess, NULL
end start
不开发.dll的就没那么多担忧了,直接开发静态库 现在的 C/C++ 编译器都在链接时间进行代码生成,写静态库就行了。静态库里面的 obj 包含链接时间代码生成所需的字节码,在进行代码生成的时候可以进行一次总的优化,包括但不限于跨编译单元的函数内联、自动剔除未用函数和变量、把特定变量固定为常量后生成某个函数的常量特化版等,编译出来的程序是不包含任何符号也不需要考虑任何混淆的,因为早就被优化得面目全非了。 照这么说dll退出历史舞台好了{:4_113:}更别说用masm写dll了。
而且这种方式更大的好处就算不写dll,也没有给变量取名的困扰,快哉!{:4_109:} Mat 发表于 2023-3-29 12:12
照这么说dll退出历史舞台好了更别说用masm写dll了。
而且这种方式更大的好处就算不写dll,也没有 ...
dll是可以不用的,dll主要是为了节省内存和硬盘,因为早期系统资源有限
苹果最早就不支持开发动态库这种东西,后来才支持,因为仅使用静态库其二进制实在太大了。
Win上个人开发的小软件也大不到哪去,静态库爽歪歪。而且就像a5说的,一起链接的,可以达到最大化二进制优化,增加破解难度。 不过总觉得心理负担加重了,不能愉快的写代码。或者辛辛苦苦的编写了一个dll,导出了一些函数,但别人一看导出表导出的函数名称就大概能猜测出某个函数是干什么用的,会不会觉得很不爽?你的目的是为了干扰破解者吗?如果是的话,直接上VMP、TMD之类的工具。或者上网络验证,重要的程序数据只有在验证之后才下发。
如果是为了防止DLL被盗用,可以给程序加自定义的数字签名,只有数字签名验证通过,才进行初始化。 本帖最后由 Mat 于 2023-3-31 03:42 编辑
美俪女神 发表于 2023-3-30 15:32
你的目的是为了干扰破解者吗?如果是的话,直接上VMP、TMD之类的工具。或者上网络验证,重要的程序数据只有 ...
这个越说越远了。
1、这里只说了用在masm开发上;
2、声明变量和过程方便,不用考虑名称冲突;
3、对dll的导出表导出的函数名能起到一定的名称混淆作用。
4、并没有说要干扰破解者,但至少能在一定程度上增加点代价吧。
总之:只是常规开发中的一些小想法,请高手自动略过。:lol Mat 发表于 2023-3-31 03:35
这个越说越远了。
1、这里只说了用在masm开发上;
dll导出表根本没有必要混淆,直接按编号匿名导出就行了。 YY菌 发表于 2023-4-13 08:48
dll导出表根本没有必要混淆,直接按编号匿名导出就行了。
是的,在十年前我的确是这样做的。
一种“我才不管三个月后的自己如何解开”的爽快感wwww
页:
[1]