如何实现动态函数

pt2519515 2005-10-27 10:00:14
要实现函数的动态指令:

一般地,一个程序中的代码是固定的,如果要调用函数,则只能调程序中已有的函数,或选择若干个
函数中的一个(如:用函数指针)

1、现在,假设我想我在程序运行后,想添加一个函数,能否分配一个代码空间,然后我可以向其中
填入机器码,再调用此函数?

2、或且,我在程序运行后,已有一个函数,而其中的一条指令我是否有办法将其改变,此后,该函
数被调用时,结果将不同.


如下:
...
typedef int (*F)(int a,int b);
int D1(int a,int b)
{
return a-b;
}
int D2(int a,int b)
{

return a+b;
}
void CTestDlg::OnBnClickedButton1()
{

F f1,f2;
f1=D1;
f2=D2;

int a=1,b=2,c=3;
char*p1=(char*)f1;
char*p2=(char*)f2;
int np=p1-p2;

memcpy(p1,p2,np);

a=f1(b,c);
...

结果memcpy(p1,p2,np);不能执行


各位可有办法?
...全文
417 33 打赏 收藏 转发到动态 举报
写回复
用AI写文章
33 条回复
切换为时间正序
请发表友善的回复…
发表回复
会思考的草 2005-11-16
  • 打赏
  • 举报
回复
VirtualProtect(
mbi.BaseAddress,
mbi.RegionSize,
PAGE_READWRITE, // desired access protection
mbi.Protect // address of variable to get old protection
);
woshizei 2005-11-16
  • 打赏
  • 举报
回复
可以把代码段的代码拷贝到堆中来执行.我试了一下,可以执行.但有2个问题没有解决.
(1)如何知道一个函数在代码段中的长度呢?我试了一下,用RET指令的机器值C3来找,但没有成功,不知是为什么.
(2)在堆中的函数代码中调用printf函数时被告诉权限不对.
大家有明白的请指点一下.
这是我试验的代码:
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

void Fun(void)
{
int i = 0;
for(i = 0; i < 1000000000; i++)
{
;
}
return;
}

typedef void (*PFUN)(void);

int main(void)
{
PFUN pf = NULL;
int iFunLength = 1000;
char *pDynaFun = NULL;

pDynaFun = (char*)malloc(iFunLength);
memcpy(pDynaFun, (void*)Fun, iFunLength);
pf = (PFUN)pDynaFun;
printf("Enter\n");
(*pf)();
printf("Exit\n");
free(pDynaFun);

return 0;
}
会思考的草 2005-11-16
  • 打赏
  • 举报
回复
除了上面的要点之外,还要注意用NOP指令预留出空间来。
wltg2001 2005-11-16
  • 打赏
  • 举报
回复
你的一些想法说白了也就是想你的程序在运行中动态的修改指令,这实际上是可行的。但是用VC作我真的不会,不过用汇编或是用C语言都应该不很难。首先你应该用VirtualProtectEx来修改你想要修改的内存的属性,因为一般的代码段只具有执行属性,你应该加上读写属性。修改好之后就可以用WriteProcessMemory来完成写代码段了。因为用C语言或是用汇编在地址定位上较容易,所以方便一点。
bobob 2005-11-16
  • 打赏
  • 举报
回复
学习
zkxz 2005-11-16
  • 打赏
  • 举报
回复
想codewarrior(会思考的草)学习!
pt2519515 2005-11-16
  • 打赏
  • 举报
回复
谢谢 codewarrior(会思考的草) ,就试一下
pt2519515 2005-11-15
  • 打赏
  • 举报
回复
用什么函数才能让目标区域能够写?
Winner25 2005-11-14
  • 打赏
  • 举报
回复
其实smc这种用在变形或者保护的技术,你用起来其实不难,一是你要让目标区域能够写,而是源区域代码要做好重定位操作
打雷啦的专栏 2005-11-14
  • 打赏
  • 举报
回复
楼主:
上面讲了这么多,就是为了解决你的问题,不过比较高深,我看不懂,但确实是为你解决问题的,你还是好好研究吧,你这功能没这么容易实现地.
pt2519515 2005-11-14
  • 打赏
  • 举报
回复
看了半天,不太明白。

我需要解决的问题是:

在VC中,如何让生成的程序在运行时,可以修改自身在内存中的代码?
或且生成一个可以运行、任意修改的代码段?

不必修改保存EXE文件。
菲斯可儿 2005-11-07
  • 打赏
  • 举报
回复
路过,崇拜 codewarrior(会思考的草) 。
jazy 2005-11-07
  • 打赏
  • 举报
回复
看起来这个挺象楼主的目标....
http://www.nsfocus.net/index.php?act=sec_self&do=view&doc_id=730
ringphone 2005-11-07
  • 打赏
  • 举报
回复
如果你的函数能用机器码写或转成机器码,那么还有一种方法就是“欺骗”,TXT文本中存储机器码(必须是数据而非文本),程序开一个BYTE数组读入,把这个数组的首址强制转换成函数指针执行。
wshcdr 2005-11-06
  • 打赏
  • 举报
回复
高深了
会思考的草 2005-11-06
  • 打赏
  • 举报
回复
上面老罗的代码是借助于汇编实现的,实现关键是编译器连接时的参数来指定段是否可读写。
我们如何在Delphi这种高级程序语言中实现呢?用编译器连接开关?告诉你,想都别想。
经过我这段时间的研究终于有了一点心得,下面来看看我是怎么在Delphi中实现这种技术的

要修改代码就需要先找到代码,如何找?做标志,空说无益,我们还是来看看下面的代码段,
有这么一段代码,如,如果是试用版就执行相应的操作,如:

procedure TForm1.ModifiedCode(Sender: TObject);
var
isTryVer: boolean;

begin
isTryVer := CheckVer;

if isTryVer then
begin
//执行试用版的功能
................ //省略代码若干
end
else
begin
//执行正式版的功能
.............. //省略代码若干
end;
end;

这段代码经过反汇编后,Cracker改一改跳转,可以很容易的爆破这个软件

要是代码具有自修改功能,那么菜鸟级Cracker就很难对这个软件实行爆破了,修改成下面的代码:
procedure TForm1.ModifiedCode(Sender: TObject);
var
isTryVer: boolean;

label tr1;

begin
.................... ////省略代码若干

isTryVer = true; //在程序中就写死了,这就是试用版

if isTryVer then goto tr1;

//执行正式版功能
.................. //省略代码若干
tr1:
//执行试用版功能
................. //省略代码若干
end;

是的,在程序中写死,这就是试用版,甚至还可以在
if isTryVer then goto tr1后写一个死循环、强退等爆力对抗代码,如果Cracker改了goto指令后
就会陷入这些对抗代码,至于这些怎么写,以防Cracker改对抗代码我以后再讲,今天就只讲SMC技术

哦,还没完呢,直接执行上面的代码就完蛋了,因为无论是正版用户或是试用用户执行的都是试用版功能
,是的,我们就是要在程序运行时修改上面那段代码,我们需要再另外一个过程来让,如果是正版用户让
isTryVer = false。。。
但在程序运行时如何找到这段代码在哪里呢,上面说过了,做个标志的话就简单了,于是我们再来改代码,如下:
procedure TForm1.ModifiedCode(Sender: TObject);
var
isTryVer: boolean;

label tr1, lb1;

begin
.................... ////省略代码若干

isTryVer = true; //在程序中就写死了,这就是试用版

if isTryVer then goto tr1;

;用nop来预留空间,以便后面的SMC能够成功执行;
;否则如果空间不够,将有可能产生不可预测的错误:
asm
nop
nop
nop
nop
nop
nop
nop
nop
end;

lb1:
//执行正式版功能
.................. //省略代码若干
tr1:
//执行试用版功能
................. //省略代码若干
end;
我们也用连续8个nop来在程序中加标志,nop 什么都不做,在内存中形如

807DFB00 cmp byte ptr [ebp-$05],$00
750D jnz TForm1.ModifiedCode + $2A

90 nop
90 nop
90 nop
90 nop
90 nop
90 nop
90 nop
90 nop
lb1:
...
tr1:
...

$00是代码Delphi中的false(不同的反汇编工具,得到结果可能不同,可能是其它的代码如01,然后后面就是jz了)
于是我们只在内存中找到$9090909090909090,然后再回退3个字节,读取1个字节,再减1,写回原处
于是我们就得到了一个可用的程序,功能和本文第一个delphi程序实现的功能一样了,代码形如:
procedure TForm1.ModifyCode;
var
lpBuffer: DWORD;
mbi: _MEMORY_BASIC_INFORMATION;
a: DWORD;
BaseAddr: Pointer;
hProcess: THandle;

begin
hProcess := OpenProcess(PROCESS_ALL_ACCESS, false, GetCurrentProcessId);
VirtualQuery(@ModifyedProc, mbi, sizeof(MEMORY_BASIC_INFORMATION));

VirtualProtect(
mbi.BaseAddress,
mbi.RegionSize,
PAGE_READWRITE, // desired access protection
mbi.Protect // address of variable to get old protection
);

a := 0;
lpBuffer := 0;
BaseAddr := Pointer($0047170d); //这个地址是我在Exe文件中根据标志找到的

ReadProcessMemory(
hProcess,
BaseAddr, // address to start reading
@lpBuffer, // address of buffer to place read data
1, // number of bytes to read
a // address of number of bytes read
);


Dec(lpBuffer);
WriteProcessMemory(
hProcess,
BaseAddr,
@lpBuffer,
1,
a
);

VirtualProtect(
mbi.BaseAddress, // address of region of committed pages
mbi.RegionSize, // size of the region
mbi.Protect, // desired access protection
a // address of variable to get old protection
);
end;

于是程序在有需要的某个时候就自己修改自己内存中的代码以给用户完整的正式版功能了。。。
SMC技术在加密/解密技术中应用相当广泛,只要我们使用得当便可以很好的防止破解

会思考的草 2005-11-06
  • 打赏
  • 举报
回复
"Self Modifying Code",代码自修改,简称SMC,看名字就知道是狠角色,程序在运行过程中
自己修改自己的代码这项技术在病毒界、加密/解密界是应用相当广泛的技术。病毒与反病毒、
加密与解密都用这项技术相互对抗。

下面为了更清楚的说明一下我引一下老罗的相关文章 -- 浅析SMC技术

===========================================
今天让我们来看Win32ASM里面的高级一点的技术——SMC(当当当当……)!!!

SMC是什么意思?它的英文名叫“Self Modifying Code”,顾名思义,就是“代码自修改”(?)(不好意思,小弟的英语六级还没过,只能翻译成这样啦……)

“代码自修改”?哇,好高深啊!其实不然……

我们知道,Win32应用程序是运行在保护模式下的,每个Win32应用程序都有相互独立的4GB地址空间,并且已经摒弃了16位时代的把代码分为Data、Code等段的内存模式的做法;现在它们只有一种内存模式,即FLAT模式,意思是“平坦”的内存模式——再也没有烦人的64KB的段大小限制啦。如此一来,所有的Win32应用程序都能各自运行在一个连续、平坦、巨大的4GB空间中,作为程序员,也不用再跟段寄存器打交道,您可以用任意的段寄存器寻址任意的地址空间,是不是很方便呢?

不过且慢,Win32时代的编程虽然比之Win16时代已经方便了不止一个数量级,但是毕竟还有一些规则是需要遵守的。最明显的之一就是不能在程序运行的过程中随便更改代码段!

(咦?刚刚不是说了Win32里面没有段的概念了吗?怎么这里又来了一个“代码段”了?别急,请听我细细道来……)

虽然Win32下已经没有了“段”,但是您还是可以给自己的程序分成不同的“段”,一个“分段”的开始即是上一个“分段”的结束。Win32只有两种性质的分段:Data和Code。

实际上,在Win32里面的分段并不是像DOS下一样,为不同的段分别指出不同的段寄存器,因为Windows下只有一个4GB的段,Windows程序中的分段表现在当程序装载时,赋予不同的段不同的属性,比如说当你的程序加载时,对于Ring3程序来说,.code段是不可写的,而.data段是可写的,如果你尝试像在DOS下一样写自己的代码部分,你将会得到一个“很cool”的蓝屏错误。

怎么样?头晕了吗?如果没有的话,让我们继续!^_^

上面已经提到代码段是不能在程序运行途中更改的了,那么怎么又来了一个“SMC”技术呢?它是如何实现的?

其实关键就在于链接时的参数,只要指定了代码段的属性是可写的,那么就OK啦!(默认的参数是不可写的)。也就是说,我们在编译、链接带有SMC的Win32ASM时应该这样做:

ml /c /coff %1.asm
link /subsystem:windows /section:.text,RWE %1.obj


怎么样?明白了吗? /section:.text,RWE 这句指定了代码段(.text)的属性是RWE,含义是:R(ReadAble),W(WriteAble),E(ExecuteAble),也就是“可读可写可执行”。这样我们的程序就可以在运行途中自己改写自己的代码段啦,怎么样?是不是很爽呢?

下面给出了一个完整的带有SMC技术的Win32ASM例子,很容易理解的,记得要用上面的方法来编译和链接哦!


;***********************************************
;程序名称:演示SMC原理
;作者:罗聪
;日期:2002-10-2
;出处:http://laoluoc.yeah.net(老罗的缤纷天地)/
;注意事项:如欲转载,请保持本程序的完整,并注明:
;转载自“老罗的缤纷天地”(http://laoluoc.yeah.net)/
;***********************************************

.386
.model flat, stdcall
option casemap:none

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib

ShowMessage proto
ReplaceMent proto

.data
szMsg1 db "这是未执行SMC之前的代码!", 0
szMsg2 db "SMC已经执行!", 0
szCaption db "SMC demo by LC, 2002", 0
Replace_Len dd 0

.code
main:
;第一次执行子程序ShowMessage,此时还没执行SMC操作
invoke ShowMessage

lea eax, ReplaceMentEnd ;标号ReplaceMent的结束
lea edx, ReplaceMentStart ;标号ReplaceMent的开始
sub eax, edx ;标号ReplaceMent的长度
mov Replace_Len, eax ;把长度储存起来

;关键代码!!!!!!!!!
lea esi, ReplaceMentStart ;标号ReplaceMent的开始
lea edi, ShowMessageStart ;原程序ShowMessage的标号的开始
mov ecx, Replace_Len ;标号ReplaceMent的长度
rep movsb ;这里是最关键的语句!!!执行SMC操作!

;第二次执行子程序ShowMessage,此时已经执行了SMC操作。
;换句话说,ShowMessage的内容已经不是第一次运行时的内容了:
invoke ShowMessage

invoke ExitProcess, 0

ShowMessage proc
;这里用“::”的话,就能够使标号成为全局性的
ShowMessageStart::
invoke MessageBox, NULL, addr szMsg1, addr szCaption, MB_OK
ShowMessageEnd::

;用nop来预留空间,以便后面的SMC能够成功执行;
;否则如果空间不够,将有可能产生不可预测的错误:
nop
nop
nop
nop
nop
nop
nop
nop

ret
ShowMessage endp

ReplaceMent proc
;将要用来SMC的代码:
ReplaceMentStart::
;invoke MessageBox, NULL, addr szMsg2, addr szCaption, MB_OK or MB_ICONINFORMATION
push MB_OK or MB_ICONINFORMATION
lea eax, szCaption
push eax
lea eax, szMsg2
push eax
push NULL
lea eax, MessageBox
call eax
ReplaceMentEnd::

ret
ReplaceMent endp

end main



怎么样?明白了吧?如果还有不够明白的地方,欢迎给我来信讨论!
lcother@163.net

老罗
2002-10-2
==========================================
会思考的草 2005-11-06
  • 打赏
  • 举报
回复
可以,这个叫做SMC(Self Modifying Code)。
pt2519515 2005-11-06
  • 打赏
  • 举报
回复
菜鸟愚昧,问题还无法解决。

EXE文件中大多数节都是only-read,如何改成 read and write?(不用存盘)
还有,让程序加一个节?
pt2519515 2005-10-30
  • 打赏
  • 举报
回复
VC程序可否可以在运行时改变自身代码.或创建新代码?
加载更多回复(13)

16,551

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Creator Browser
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧