对AddMessage源代码的部分解释

stardragon
2007-12-03, 19:18
在学习修改PE文件的源代码中,遇到这么一条语句,看了半天不懂
还请高手指教:
// 得到代码偏移,用代码段起始RVA减去此段的物理偏移
// 应为程序的入口计算公式是一个相对的偏移地址,计算公式为:
// 代码的写入地址+dwCodeOffset.
dwCodeOffset=header-> opt_head.BaseOfCode-header-> section_header[0].PointerToRawData;

// 保存旧的程序入口地址.
dwOldEntryAddress=header-> opt_head.AddressOfEntryPoint;

// 计算新的程序入口地址.
dwNewEntryAddress=dwEntryWrite+dwCodeOffset;
按照个人的理解这是计算PE新的入口地址的代码,但是有点不是太懂!PointerToRawData应该是指向硬盘中文件的指针,跟BaseOfCode(文件映射的执行代码RVA)好像不能发生什么关系吧。
请大侠不吝赐教,感激!

combojiang
2007-12-03, 19:57
这里要理解好PE文件存在的两种形态,一种是以硬盘文件存储的形式,一种是内存加载的形式。
对于code代码段,在内存中存在的形式和硬盘中存储的形式是一样的,都是以二进制方式存在。
BaseOfCode是code代码段在内存中加载的相对起始地址,PointerToRawData是code代码段在硬盘文件中的相对起始地址。PointerToRawData是以文件起头为基准。BaseOfCode是以ImageBase为基准。
因此,可以透过内存中的某个值的偏移计算出其在文件中的位置,也可以根据文件中的某个值的偏移,计算出其在内存中的位置。

stardragon
2007-12-03, 21:54
这里要理解好PE文件存在的两种形态,一种是以硬盘文件存储的形式,一种是内存加载的形式。
对于code代码段,在内存中存在的形式和硬盘中存储的形式是一样的,都是以二进制方式存在。
BaseOfCode是code代码段在内存中加载的相对起始地址,PointerToRawData是code代码段在硬盘文件...
感谢您的回复!但还是有点不懂,在PE Header中好像没有关于文件起始段的描述吧,那样的话PointerToRawData怎么寻找自己的基准呢?另ImageBase是从哪里起始的(应该是从PE Header的某个地方开始吧),能否给出一个从文件偏移到内存位置的位置计算公式或者从内存偏移到文件位置的计算公式:): :):

combojiang
2007-12-04, 00:56
呵呵,看得出,你对pe文件格式还是不太了解。
ImageBase对于EXE文件来说,由于每个文件总是使用独立的虚拟地址空间,EXE总是能够按照这个地址装入。通常是0x00400000,imagebase指向的内容是DOS文件头。对于内存中pe文件代码段的起始地址是ImageBase + BaseOfCode;对于磁盘文件中代码段的起始地址是相对于文件头偏移PointerToRawData的位置。

假设要在磁盘文件中的代码段缝隙中添加代码,则首先需要查看磁盘文件代码段缝隙大小,是否容的下插入的代码,如果能容下,则根据文件对齐的原则,首先将原来的磁盘文件中代码段按照文件对齐,找出起始插入代码的位置。例如:

// 此段的真实长度.
dwVirtSize=header->section_header[0].Misc.VirtualSize;

// 此段的物理偏移.
dwPhysAddress=header->section_header[0].PointerToRawData;

// 此段的物理长度.
dwPhysSize=header->section_header[0].SizeOfRawData;

// 得到代码段的可用空间,用以判断可不可以写入我们的代码
// 用此段的物理长度减去此段的真实长度就可以得到.
dwSpace=dwPhysSize-dwVirtSize;

if(dwSpace < viruslength) return;

// 代码写入的物理偏移.
dwEntryWrite=header->section_header[0].PointerToRawData+header->
section_header[0].Misc.VirtualSize;

//对齐边界.
mods=dwEntryWrite%16;

if(mods!=0)
{
dwEntryWrite+=(16-mods);
}

//计算加载到内存中的代码段与文件中的代码段的差。
dwCodeOffset=header->opt_head.BaseOfCode-dwPhysAddress;

这里的dwEntryWrite就是对齐后要插入代码的位置。现在有了物理文件中的位置,需要找出加载到内存中的位置。根据我在上面贴子中讲的“对于code代码段,在内存中存在的形式和硬盘中存储的形式是一样的,都是以二进制方式存在”,可以有公式:

内存中位置 - (ImageBase + BaseOfCode) = dwEntryWrite - dwPhysAddress;

由于pe中使用的都是PVA,因此假设内存中对应位置的RVA是dwNewEntryAddress,
则上面公式中: 内存中位置 = ImageBase + dwNewEntryAddress;

把这个代入上面公式就是:
ImageBase + dwNewEntryAddress - (ImageBase + BaseOfCode) = dwEntryWrite - dwPhysAddress

于是:dwNewEntryAddress = dwEntryWrite - dwPhysAddress + BaseOfCode;

dwNewEntryAddress = dwEntryWrite + (BaseOfCode - dwPhysAddress );

而前面dwCodeOffset=header->opt_head.BaseOfCode-dwPhysAddress;
所以:
dwNewEntryAddress = dwEntryWrite + dwCodeOffset;
到此为止,就推断出了你开头 计算新的程序入口地址的公式了。
 
Jlox是一个用Java编写的lox语言解释器,它包含了词法扫描和语法分析等功能。以下是Jlox的源代码和词法扫描器部分的示例代码: ### Lox.java ```java import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.Paths; import java.util.List; public class Lox { static boolean hadError = false; public static void main(String[] args) throws IOException { if (args.length > 1) { System.out.println("Usage: jlox [script]"); System.exit(64); } else if (args.length == 1) { runFile(args[0]); } else { runPrompt(); } } private static void runFile(String path) throws IOException { byte[] bytes = Files.readAllBytes(Paths.get(path)); run(new String(bytes, Charset.defaultCharset())); if (hadError) System.exit(65); } private static void runPrompt() throws IOException { System.out.println("Welcome to the JLox REPL!"); for (;;) { System.out.print("> "); run(new java.util.Scanner(System.in).nextLine()); hadError = false; } } private static void run(String source) { Scanner scanner = new Scanner(source); List<Token> tokens = scanner.scanTokens(); // For now, just print the tokens. for (Token token : tokens) { System.out.println(token); } } static void error(int line, String message) { report(line, "", message); } private static void report(int line, String where, String message) { System.err.println( "[line " + line + "] Error" + where + ": " + message); hadError = true; } } ``` ### Scanner.java ```java import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import static TokenType.*; class Scanner { private final String source; private final List<Token> tokens = new ArrayList<>(); private int start = 0; private int current = 0; private int line = 1; private static final Map<String, TokenType> keywords; static { keywords = new HashMap<>(); keywords.put("and", AND); keywords.put("class", CLASS); keywords.put("else", ELSE); keywords.put("false", FALSE); keywords.put("for", FOR); keywords.put("fun", FUN); keywords.put("if", IF); keywords.put("nil", NIL); keywords.put("or", OR); keywords.put("print", PRINT); keywords.put("return", RETURN); keywords.put("super", SUPER); keywords.put("this", THIS); keywords.put("true", TRUE); keywords.put("var", VAR); keywords.put("while", WHILE); } Scanner(String source) { this.source = source; } List<Token> scanTokens() { while (!isAtEnd()) { // We are at the beginning of the next lexeme. start = current; scanToken(); } tokens.add(new Token(EOF, "", null, line)); return tokens; } private void scanToken() { char c = advance(); switch (c) { case '(': addToken(LEFT_PAREN); break; case ')': addToken(RIGHT_PAREN); break; case '{': addToken(LEFT_BRACE); break; case '}': addToken(RIGHT_BRACE); break; case ',': addToken(COMMA); break; case '.': addToken(DOT); break; case '-': addToken(MINUS); break; case '+': addToken(PLUS); break; case ';': addToken(SEMICOLON); break; case '*': addToken(STAR); break; case '!': addToken(match('=') ? BANG_EQUAL : BANG); break; case '=': addToken(match('=') ? EQUAL_EQUAL : EQUAL); break; case '<': addToken(match('=') ? LESS_EQUAL : LESS); break; case '>': addToken(match('=') ? GREATER_EQUAL : GREATER); break; case '/': if (match('/')) { // A comment goes until the end of the line. while (peek() != '\n' && !isAtEnd()) advance(); } else { addToken(SLASH); } break; case ' ': case '\r': case '\t': // Ignore whitespace. break; case '\n': line++; break; case '"': string(); break; default: if (isDigit(c)) { number(); } else if (isAlpha(c)) { identifier(); } else { Lox.error(line, "Unexpected character."); } break; } } private void identifier() { while (isAlphaNumeric(peek())) advance(); // See if the identifier is a reserved word. String text = source.substring(start, current); TokenType type = keywords.get(text); if (type == null) type = IDENTIFIER; addToken(type); } private void number() { while (isDigit(peek())) advance(); // Look for a fractional part. if (peek() == '.' && isDigit(peekNext())) { // Consume the "." advance(); while (isDigit(peek())) advance(); } addToken(NUMBER, Double.parseDouble(source.substring(start, current))); } private void string() { while (peek() != '"' && !isAtEnd()) { if (peek() == '\n') line++; advance(); } // Unterminated string. if (isAtEnd()) { Lox.error(line, "Unterminated string."); return; } // The closing ". advance(); // Trim the surrounding quotes. String value = source.substring(start + 1, current - 1); addToken(STRING, value); } private boolean match(char expected) { if (isAtEnd()) return false; if (source.charAt(current) != expected) return false; current++; return true; } private char peek() { if (isAtEnd()) return '\0'; return source.charAt(current); } private char peekNext() { if (current + 1 >= source.length()) return '\0'; return source.charAt(current + 1); } private boolean isAlpha(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; } private boolean isAlphaNumeric(char c) { return isAlpha(c) || isDigit(c); } private boolean isDigit(char c) { return c >= '0' && c <= '9'; } private boolean isAtEnd() { return current >= source.length(); } private char advance() { current++; return source.charAt(current - 1); } private void addToken(TokenType type) { addToken(type, null); } private void addToken(TokenType type, Object literal) { String text = source.substring(start, current); tokens.add(new Token(type, text, literal, line)); } } ``` 这些代码只是Jlox解释器的一部分,但它们展示了如何实现词法扫描器和一些简单的语法分析。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值